Extract specific keys from variedly nested hash - ruby

I'm trying to concatenate the different "Name" values from the provided Hash into one string. So the output should be one array or string containing all "Name" values.
The difficulty for me sits in that the level of nesting varies between two and four parents.
I've tried to solve it in two ways:
Recursively go through the hash and on each level append the value to an array. Finally spit out the array.
Flatten the hash and then cherry pick from that array
Unfortunately none of the answers given in related questions seemed to work either. I'm sure it's quite simple but I can't seem to figure it out. Many thanks
my_hash = { "BreadCrumbs" => {
"Id" => 375,
"Name" => "Willingen",
"Parent" => {
"Id" => 52272,
"Name" => "Wintersport-Arena Sauerland",
"Parent" => {
"Id" => 8,
"Name" => "Germany"
}
}
}
}

▶ hash = { "BreadCrumbs" => {
▷ "Id" => 375,
▷ "Name" => "Willingen",
▷ "Parent" => {
▷ "Id" => 52272,
▷ "Name" => "Wintersport-Arena Sauerland",
▷ "Parent" => {
▷ "Id" => 8,
▷ "Name" => "Germany"
▷ }
▷ }
▷ }}
▶ def concat hash
▷ [hash['Name'], hash['Parent'] ? concat(hash['Parent']) : nil]
▷ end
▶ (concat hash['BreadCrumbs']).flatten.compact
#⇒ ["Willingen", "Wintersport-Arena Sauerland", "Germany"]
I do not flatten on every iteration so that the result still contains hierarchy:
▶ concat hash['BreadCrumbs']
#⇒ ["Willingen", ["Wintersport-Arena Sauerland", ["Germany", nil]]]
The requested string as the result:
▶ (concat hash['BreadCrumbs']).flatten.compact.join ', '
#⇒ "Willingen, Wintersport-Arena Sauerland, Germany"

For any number of levels, without using recursion:
def pull_names(hash)
h = hash
names = []
loop do
names << h["Name"]
return names unless h.key?("Parent")
h = h["Parent"]
end
end
Suppose:
hash = { "BreadCrumbs" => {
"Name" => "Willingen",
"Parent" => {
"Id" => 52272,
"Name" => "Wintersport-Arena Sauerland",
"Parent" => {
"Id" => 8,
"Name" => "Germany",
"Parent" => {
"Id" => 1,
"Name" => "Bora Bora"
}
}
}
}
}
then:
arr = pull_names hash["BreadCrumbs"]
#=> ["Willingen", "Wintersport-Arena Sauerland", "Germany", "Bora Bora"]
arr.join(' ')
#=> "Willingen Wintersport-Arena Sauerland Germany Bora Bora"

Related

Best/cleanest way to grab all the keys that end in url in Ruby

I have a Hash that looks like this:
{
"id" => 108,
"position" => 0,
"attachment_content_type" => "image/jpeg",
"attachment_updated_at" => "2014-11-14T21:50:46.395Z",
"attachment_width" => 1140,
"attachment_height" => 1140,
"alt" => "",
"viewable_type" => "Spree::Variant",
"viewable_id" => 43,
"mini_url" => "xyz,
"small_url" => "http:blahblahblah",
"product_url" => "http:blahblahblah",
"large_url" => "http:blahblahblah",
"xlarge_url" => "http:blahblahblah"
}
How can I use the splat * to grab all the keys that end in url? Is there a way to do that?
This Hash is in a serializer in Rails and I'm trying to nest the urls in a JSON structure that looks like this:
{
urls: {
mini_url: "blaaaah",
(etc.)
}
}
Thoughts?
Not sure what you mean by using the splat operator here but you can easily pull them with code like:
input = {
"id" => 108,
"position" => 0,
"attachment_content_type" => "image/jpeg",
"attachment_file_name" => "Strainer_-_Blue_Apron_Marketplace_213.jpg",
"type" => "Spree::Image",
"attachment_updated_at" => "2014-11-14T21:50:46.395Z",
"attachment_width" => 1140,
"attachment_height" => 1140,
"alt" => "",
"viewable_type" => "Spree::Variant",
"viewable_id" => 43,
"mini_url" => "https://s3.amazonaws.com/marketplace-staging.blueapron.com/app/public/spree/products/108/mini/Strainer_-_Blue_Apron_Marketplace_213.jpg?1416001846",
"small_url" => "https://s3.amazonaws.com/marketplace-staging.blueapron.com/app/public/spree/products/108/small/Strainer_-_Blue_Apron_Marketplace_213.jpg?1416001846",
"product_url" => "https://s3.amazonaws.com/marketplace-staging.blueapron.com/app/public/spree/products/108/product/Strainer_-_Blue_Apron_Marketplace_213.jpg?1416001846",
"large_url" => "https://s3.amazonaws.com/marketplace-staging.blueapron.com/app/public/spree/products/108/large/Strainer_-_Blue_Apron_Marketplace_213.jpg?1416001846",
"xlarge_url" => "https://s3.amazonaws.com/marketplace-staging.blueapron.com/app/public/spree/products/108/xlarge/Strainer_-_Blue_Apron_Marketplace_213.jpg?1416001846"
}
urls = input.each_with_object({}) do |(key,value),acc|
if key.end_with?("_url")
acc[key] = value
end
end
puts urls.inspect
How can I use the splat * to grab all the keys that end in url? Is there a way to do that?
Splats cannot do that.
You can use Hash#select:
input = {
"id" => 108,
"position" => 0,
"attachment_content_type" => "image/jpeg",
"attachment_file_name" => "Strainer_-_Blue_Apron_Marketplace_213.jpg",
"type" => "Spree::Image",
"attachment_updated_at" => "2014-11-14T21:50:46.395Z",
"attachment_width" => 1140,
"attachment_height" => 1140,
"alt" => "",
"viewable_type" => "Spree::Variant",
"viewable_id" => 43,
"mini_url" => "https://s3.amazonaws.com/marketplace-staging.blueapron.com/app/public/spree/products/108/mini/Strainer_-_Blue_Apron_Marketplace_213.jpg?1416001846",
"small_url" => "https://s3.amazonaws.com/marketplace-staging.blueapron.com/app/public/spree/products/108/small/Strainer_-_Blue_Apron_Marketplace_213.jpg?1416001846",
"product_url" => "https://s3.amazonaws.com/marketplace-staging.blueapron.com/app/public/spree/products/108/product/Strainer_-_Blue_Apron_Marketplace_213.jpg?1416001846",
"large_url" => "https://s3.amazonaws.com/marketplace-staging.blueapron.com/app/public/spree/products/108/large/Strainer_-_Blue_Apron_Marketplace_213.jpg?1416001846",
"xlarge_url" => "https://s3.amazonaws.com/marketplace-staging.blueapron.com/app/public/spree/products/108/xlarge/Strainer_-_Blue_Apron_Marketplace_213.jpg?1416001846"
}
require "pp"
pp input.select { |key, value| key.end_with?("_url") }
Output:
{"mini_url"=>
"https://s3.amazonaws.com/marketplace-staging.blueapron.com/app/public/spree/products/108/mini/Strainer_-_Blue_Apron_Marketplace_213.jpg?1416001846",
"small_url"=>
"https://s3.amazonaws.com/marketplace-staging.blueapron.com/app/public/spree/products/108/small/Strainer_-_Blue_Apron_Marketplace_213.jpg?1416001846",
"product_url"=>
"https://s3.amazonaws.com/marketplace-staging.blueapron.com/app/public/spree/products/108/product/Strainer_-_Blue_Apron_Marketplace_213.jpg?1416001846",
"large_url"=>
"https://s3.amazonaws.com/marketplace-staging.blueapron.com/app/public/spree/products/108/large/Strainer_-_Blue_Apron_Marketplace_213.jpg?1416001846",
"xlarge_url"=>
"https://s3.amazonaws.com/marketplace-staging.blueapron.com/app/public/spree/products/108/xlarge/Strainer_-_Blue_Apron_Marketplace_213.jpg?1416001846"}
Do you want the hash, or do you want the values. If I only wanted the values I'd start by taking advantage of keys and select:
hash = {
"id" => 108,
"viewable_id" => 43,
"mini_url" => "mini_url_value",
"small_url" => "small_url_value",
}
hash.keys.select{ |k| k[/.*_url$/] } # => ["mini_url", "small_url"]
That uses *, but there's no reason to use it. From experience and testing I know it'd slow the check and waste CPU. Simplifying the pattern would be faster:
hash.keys.select{ |k| k[/_url$/] } # => ["mini_url", "small_url"]
Even faster is:
hash.keys.select{ |k| k.end_with?('_url') } # => ["mini_url", "small_url"]
From there I'd use values_at to get the associated values:
url_keys = hash.keys.select{ |k| k.end_with?('_url') } # => ["mini_url", "small_url"]
hash.values_at(*url_keys) # => ["mini_url_value", "small_url_value"]
It'd be easy to build a new hash:
url_keys = hash.keys.select{ |k| k.end_with?('_url') } # => ["mini_url", "small_url"]
url_keys.zip(hash.values_at(*url_keys)).to_h # => {"mini_url"=>"mini_url_value", "small_url"=>"small_url_value"}
It's also possible to use select with the hash and convert the resulting array-of-arrays back to a new hash:
hash.select{ |k| k.end_with?('_url') } # => {"mini_url"=>"mini_url_value", "small_url"=>"small_url_value"}
I use the first way out of habit; It was the Perl-ish way and I wrote a lot of Perl, and it's compatible with old Rubies. Hash.select didn't return hashes for a long time and to_h wasn't available as part of Array for even longer.

Select a value from a nested hash with unknown keys

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

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}}

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