Select a value from a nested hash with unknown keys - ruby

I have a hash that looks something like this:
hash = { "data" => {
"Aatrox" => {
"id" => "Aatrox",
"key" => "266",
"name" => "Aatrox"
},
"Ahri" => {
"id" => "Ahri",
"key" => "123",
"name" => "Ahri"
},
"Another name" => {
"id" => "Another name",
"key" => "12",
"name" => "Another name"
},
}
}
I'm trying to get the value from "id" that matches a given key:
def get_champion_name_from_id(key)
filtered = #champion_data["data"].select do | key, champ_data |
Integer(champ_data["key"]) == key
end
end
I'm using select to get the items that match the block, however, the return value is another hash that looks like this:
{
"Aatrox": {
"id" => "Aatrox",
"key" => "266",
"name" => "Aatrox"
}
}
How can I avoid this and get just the last nested hash?
If the key passed was 266, I want to get this hash:
{
"id" => "Aatrox",
"key" => "266",
"name" => "Aatrox"
}
This hash is the result of a parsed JSON file, so there's no way I can do filtered["Aatrox"] to get a given value.

Hash#values returns values only (without keys). And by using Enumerable#find, you will get the first matched item instead of an array that contains a single item.
#champion_data['data'].values.find { |champ_data|
champ_data['key'] == '266'
}
# => {"id"=>"Aatrox", "key"=>"266", "name"=>"Aatrox"}
def get_champion_name_from_id(key)
key = key.to_s
#champion_data['data'].values.find { |champ_data|
champ_data['key'] == key
}
end

You can do it with the select method also:
#champion_data["data"].select do |key, val|
#champion_data["data"][key] if #champion_data["data"][key]["key"] == "266"
end

Related

Convert flat list of products and categories to tree structure

I currently have items in the following structure:
[{
"category" => ["Alcoholic Beverages", "Wine", "Red Wine"],
"name" => "Robertson Merlot",
"barcode" => '123456789-000'
"wine_farm" => "Robertson Wineries",
"price" => 60.00
}]
I have made up this data, but the data I am using is in the same structure and I cannot change the data coming in.
I have > 100 000 of these.
Each product is nested between 1 and n (unlimited) categories.
Because of the tabular nature of this data, the categories are repeated. I want to use tree data to prevent this repetition and cut down the file size by 25 to 30%.
I am aiming at a tree structure something like this:
{
"type" => "category",
"properties" => {
"name" => "Alcoholic Beverages"
},
"children" => [{
"type" => "category",
"properties" => {
"name" => "Wine"
},
"children" => [{
"type" => "category",
"properties" => {
"name" => "Red Wine"
},
"children" => [{
"type" => "product",
"properties" => {
"name" => "Robertson Merlot",
"barcode" => '123456789-000',
"wine_farm" => "Robertson Wineries",
"price" => 60.00
}
}]
}]
}]
}
I can't seem to think of an efficient algorithm to get this right. I would appreciate any help in the right direction.
Should I be generating ID's and ad the parent ID for each node? I am concerned that using ID's will add more length to the text, which I am trying to shorten.
Although I have simplified it a bit from your requested structure, you can use the logic to get an idea of how it could be done:
require 'pp'
x = [{
"category" => ["Alcoholic Beverages", "Wine", "Red Wine"],
"name" => "Robertson Merlot",
"barcode" => '123456789-000',
"wine_farm" => "Robertson Wineries",
"price" => 60.00
}]
result = {}
x.each do |entry|
# Save current level in a variable
current_level = result
# We want some special logic for the last item, so let's store that.
item = entry['category'].pop
# For each category, check if it exists, else add a category hash.
entry['category'].each do |category|
unless current_level.has_key?(category)
current_level[category] = {'type' => 'category', 'children' => {}, 'name' => category}
end
current_level = current_level[category]['children'] # Set the new current level of the hash.
end
# Finally add the item:
entry.delete('category')
entry['type'] = 'product'
current_level[item] = entry
end
pp result
And it gives us:
{"Alcoholic Beverages"=>
{"type"=>"category",
"children"=>
{"Wine"=>
{"type"=>"category",
"children"=>
{:"Red Wine"=>
{"name"=>"Robertson Merlot",
"barcode"=>"123456789-000",
"wine_farm"=>"Robertson Wineries",
"price"=>60.0,
"type"=>"product"}},
"name"=>"Wine"}},
"name"=>"Alcoholic Beverages"}}
There are probably easier ways of doing this but this is all I can think of for now, it should match your structure.
require 'json'
# Initial set up, it seems the root keys are always the same looking at your structure
products = {
'type' => 'category',
'properties' => {
'name' => 'Alcoholic Beverages'
},
'children' => []
}
data = [{
"category" => ['Alcoholic Beverages', 'Wine', 'Red Wine'],
"name" => 'Robertson Merlot',
"barcode" => '123456789-000',
"wine_farm" => 'Robertson Wineries',
"price" => 60.00
}]
data.each do |item|
# Make sure we set the current to the top-level again
curr = products['children']
# Remove first entry as it's always 'Alcoholic Beverages'
item['category'].shift
item['category'].each do |category|
# Get the index for the category if it exists
index = curr.index {|x| x['type'] == 'category' && x['properties']['name'] == category}
# If it exists then change current hash level to the child of that category
if index
curr = curr[index]['children']
# Else add it in
else
curr << {
'type' => 'category',
'properties' => {
'name' => category
},
'children' => []
}
# We can use last as we know it'll be the last index.
curr = curr.last['children']
end
end
# Delete category from the item itself
item.delete('category')
# Add the item as product type to the last level of the hash
curr << {
'type' => 'product',
'properties' => item
}
end
puts JSON.pretty_generate(products)

modify a hash in ruby to make the keys' value to contain nested hashes

I have a hash passed by a user in puppet declaration and for the create_resources to use it, it needs to be added a new nested level with the key remaining to be the key but it's value being set to a nested hash in it with the values from the original value and some string.
like given this
hash = {
"Field1" => "Value11",
"Field2" => ["value1","value2"],
}
Then would like to have the new hash after that to be given as this output
hash = {
"Field1" => { "ensure" => "present",
"value" => "Value11",
},
"Field2" => { "ensure" => "present",
"value" => ["value1","value2"],
},
}
Tried to do
added = {'ensure' => 'present'}
hash.zip([added])
to no avail.
hash.each{|k, v| hash[k] = {"ensure" => "present", "value" => v}}

Using Ruby, how do I print just the value of a MongoDB query result

I want to print just the value of the field in question (name). What it does now is print the following.
nameHOSTA
nameHOSTB
I want it to print
HOSTA
HOSTB
Query ...
puts collection.find({"environment_name" => role, "type" => "TF"}, {:fields => {"_id" => 0, "name" =>
1}}).to_a
I think something like this should get you want you want:
collection.find({"environment_name" => role, "type" => "TF"}, {:fields => {"_id" => 0, "name" =>1}}).each { |item|
puts item['name']
}

HOW-TO Create an Array of Hashes in Ruby

New to ruby and I'm trying to create an array of hashes (or do I have it backwards?)
def collection
hash = { "firstname" => "Mark", "lastname" => "Martin", "age" => "24", "gender" => "M" }
array = []
array.push(hash)
#collection = array[0][:firstname]
end
#collection does not show the firstname for the object in position 0... What am I doing wrong?
Thanks in advance!
You're using a Symbol as the index into the Hash object that uses String objects as keys, so simply do this:
#collection = array[0]["firstname"]
I would encourage you to use Symbols as Hash keys rather than Strings because Symbols are cached, and therefore more efficient, so this would be a better solution:
def collection
hash = { :firstname => "Mark", :lastname => "Martin", :age => 24, :gender => "M" }
array = []
array.push(hash)
#collection = array[0][:firstname]
end
You have defined the keys of your hash as String. But then you are trying to reference it as Symbol. That won't work that way.
Try
#collection = array[0]["firstname"]
You can do this:
#collection = [{ "firstname" => "Mark", "lastname" => "Martin", "age" => "24", "gender" => "M" }]

How can I get a key => value out of this foursquare hash?

Here is what it looks like:
{
"groups" => [
{ "venues" => [
{ "city" => "Madrid",
"address" => "Camino de Perales, s/n",
"name" => "Caja Mágica",
"stats" => {"herenow"=>"0"},
"geolong" => -3.6894333,
"primarycategory" => {
"iconurl" => "http://foursquare.com/img/categories/arts_entertainment/stadium.png",
"fullpathname" => "Arts & Entertainment:Stadium",
"nodename" => "Stadium",
"id" => 78989 },
"geolat" => 40.375045,
"id" => 2492239,
"distance" => 0,
"state" => "Spain" }],
"type" => "Matching Places"}]
}
Big and ugly... I just want to grab the id out. How would I go about doing this?
h = { "groups" => ......... }
The two ids are:
h["groups"][0]["venues"][0]["primarycategory"]["id"]
h["groups"][0]["venues"][0]["id"]
If the hash stores one id:(assuming the value is stored in a variable called hash)
hash["groups"][0]["venues"][0]["primarycategory"]["id"] rescue nil
If the hash stores multiple ids then:
ids = Array(hash["groups"]).map do |g|
Array(g["venues"]).map do |v|
v["primarycategory"]["id"] rescue nil
end.compact
end.flatten
The ids holds the array of id's.

Resources