irb> pp config
[{"file"=>"/var/tmp"},
{"size"=>"1024"},
{"modified"=>"03/28/2012"}]
=> nil
In the code,
config.each do |d|
# then how to break d into (k, v)???
end
config.each do |items|
items.each do |key, value|
# e.g. key="file", value="/var/tmp", etc.
end
end
Just do
config.each do |hash|
(k,v),_ = *hash
end
Inspired by #Arup's answer, here's a solution that doesn't require a extra, unused variable in the parallel assignment:
config.each do |hash|
key, value = hash.to_a[0]
end
to_a converts the hash into the same kind of array that you would get by using splat *hash, but you can actually index the first element of the array (i.e. the first key/value pair) with [0] this way, while trying to do so with splat (*hash) generates a syntax error (at least in Ruby version 2.1.1):
>> k,v = (*hash)[0]
SyntaxError: (irb):4: syntax error, unexpected ')', expecting '='
k,v = (*x)[0]
^
from c:/RailsInstaller/Ruby1.9.3/bin/irb:12:in `<main>'
>>
Of course, depending on what you're going to do with the key and value variables, it might make your code shorter and more readable to use one of these standard block constructs:
config.each do |hash|
hash.each { |key,value| puts "#{key}: #{value}" }
end
# or
config.each do |hash|
hash.each do |key,value|
puts "#{key}: #{value}"
end
end
Related
I'm trying to write a method that removes all keys in a nested hash that point to nil recursively.
For example:
{:a=>nil, :b=>"b", :c=>nil, :d=>{:dd=>"dd", :ee=>nil, :ff=>"ff"}, :e=>{:gg=>nil, :hh=>nil}}
becomes:
{:b=>"b", :d=>{:dd=>"dd", :ff=>"ff"}}
I'm not having much luck though.
My most recent attempt looks like:
def deep_compact(hash)
hash.reject do |key, value|
deep_compact(value) if value.class == Hash
next true if value.nil? || value.empty?
end
end
Here I want to iterate over each key value pair in the hash. If the value is a hash, I want to do the same for that hash. I want to reject the pair if the value is nil or empty. Otherwise, I want to keep it.
The result isn't what I want:
#=> {:b=>"b", :d=>{:dd=>"dd", :ee=>nil, :ff=>"ff"}, :e=>{:gg=>nil, :hh=>nil}}
I have also tried:
def deep_compact(hash)
hash.compact.transform_values do |value|
deep_compact(value) if value.class == Hash
value
end
end
Again, I get the same result:
#=> {:b=>"b", :d=>{:dd=>"dd", :ee=>nil, :ff=>"ff"}, :e=>{:gg=>nil, :hh=>nil}}
This leaves me to believe that either I've missed something or my understanding of recursion is wrong.
Are any of my attempts close? What do I need to do to ensure I get the result I want: {:b=>"b", :d=>{:dd=>"dd", :ff=>"ff"}}?
The trick would be to recursively compact nested hashes and then to eliminate empty values.
compact = ->(hash) {
hash.is_a?(Hash) ?
hash.map { |k, v| [k, compact.(v)] }.
to_h.
delete_if { |_, v| v.nil? || v.respond_to?(:empty?) && v.empty? } :
hash
}
compact.(input)
#⇒ {:b=>"b", :d=>{:dd=>"dd", :ff=>"ff"}}
I discovered that by placing my recursive function call at the end of the block got me most of the way there. (Is this 'tail-end' recursion?)
I also call reject on the hash returned by transform_values to removed any empty pairs.
This achieves what I wanted:
def deep_compact(hash)
hash.compact.transform_values do |value|
next value unless value.class == Hash
deep_compact(value)
end.reject { |_k, v| v.empty? }
end
> h
=> {:a=>nil, :b=>"b", :c=>nil, :d=>{:dd=>"dd", :ee=>nil, :ff=>"ff"}, :e=>{:gg=>nil, :hh=>nil}}
> deep_compact h
=> {:b=>"b", :d=>{:dd=>"dd", :ff=>"ff"}}
Other option using Hash#reject!, it changes the original Hash:
def deep_compact(h)
h.each { |_, v| deep_compact(v) if v.is_a? Hash }.reject! { |_, v| v.nil? || v.empty? }
end
deep_compact(h)
#=> {:b=>"b", :d=>{:dd=>"dd", :ff=>"ff"}}
Is there a more elegant way to write this code?
def create_a_hash_from_a_collection
my_hash = {}
collection_of_hashes.each do |key, value|
my_hash[key] = {} if my_hash[key].nil?
my_hash[key] = value
end
my_hash
end
The line that seems clumsy to me is this:
my_hash[key] = {} if my_hash[key].nil?
Is there a shorthand way of expressing it?
If you want to have a brand new hash for each key in your initial hash you need to initialise it with a block:
hash = Hash.new { |hash, key| hash[key] = {} }
hash[:foo].object_id == hash[:bar].object_id #=> false
Otherwise, if you do this, It will be always the same default hash
hash = Hash.new({})
hash[:foo].object_id == hash[:bar].object_id #=> true
You can use ||= operator which does exactly what you want
my_hash[key] ||= {}
The rest of my answer here is because I'm not sure what a "collection of hashes" is, so my best guess is that it would be an array of hashes. If I'm wrong, let me know and disregard the rest of this answer.
It seems that the rest of your method may not do what it sounds like you're trying to do. Consider:
#collection_of_hashes = [{foo: 'bar'}, {baz: 'qux'}]
def create_a_hash_from_a_collection
my_hash = {}
#collection_of_hashes.each do |key, value|
# this is not actually doing anything here and returns same with or
# without the following line
# my_hash[key] ||= {}
my_hash[key] = value
end
my_hash
end
#=> {{:foo=>"bar"}=>nil, {:baz=>"qux"}=>nil}
But what you probably want is
def create_a_hash_from_a_collection
my_hash = {}
#collection_of_hashes.each do |hash|
hash.keys.each do |k|
my_hash[k] = hash[k]
end
end
my_hash
end
#=> {:foo=>"bar", :baz=>"qux"}
But also keep in mind, if any of your "collection of hashes" which we would tend to assume would be an array of hashes, contain the same key, which one wins? This code, it would be the last item in the array's key value. What is the actual goal of your method?
I guess what you want is to initialise your my_hash with a default value, so then you don't need to check if it's nil or not.
That can be done using the Hash.new constructor, compare:
my_hash = {}
puts my_hash['no_existing_key'] #=> nil
my_hash = Hash.new({})
puts my_hash['no_existing_key'] #=> {}
You then can reduce your code to:
def create_a_hash_from_a_collection
my_hash = Hash.new({})
collection_of_hashes.each do |key, value|
my_hash[key] = value
end
my_hash
end
Since you are assigning value anyway, maybe you could use my_hash[key] = value || {}?
So, if value to assign is nil, the value of that key becomes {}.
I find myself having to do this all the time, and when I'm in a hurry I wrap it in a conditional:
if !myhash.blank?
hash.each do |k,v|
do_something
end
end
or:
myhash.map{|k,v| do_something} unless myhash.blank?
There must be something cleaner.
I tend to use to_h to hide the "is it nil" test:
hash.to_h.each do |k,v|
# something
end
If hash is already a Hash then hash.to_h is hash, if hash.nil? then hash.to_h is { }.
Similarly, I use to_a for arrays:
array.to_a.each { |e| ... }
to_s for strings, to_i for integers (when I want to treat nil as zero), ...
I would probably do either this:
(my_hash || {}).each do |k, v|
# something
end
or this:
my_hash ||= {}
my_hash.each do |k, v|
# something
end
If you want to specifically iterate if not nil you could do something like
hash && hash.each do |x|
# do something
end
But I find the unless hash.nil? to be more expressive.
If you are using rails, you can say:
my_hash.try(:each) do |key, value|
# code
end
try will return nil if called on nil or if receiver does not respond to the method.
Hash(myhash).each {|k,v| # whatevs }
Always returns a Hash. Modified from: https://stackoverflow.com/a/18894072/1076207
irb test:
# $ irb
# irb(main):001:0> Hash(myhash=nil).each {|k,v|}
# => {}
# irb(main):002:0> Hash(myhash={"a" => "b"}).each {|k,v|}
# => {"a"=>"b"}
Let's say I have a Hash like this:
my_hash = {"a"=>{"a1"=>"b1"}, "b"=>"b", "c"=>{"c1"=>{"c2"=>"c3"}}}
And I want to convert every element inside the hash that is also a hash to be placed inside of an Array.
For example, I want the finished Hash to look like this:
{"a"=>[{"a1"=>"b1"}], "b"=>"b", "c"=>[{"c1"=>[{"c2"=>"c3"}]}]}
Here is what I've tried so far, but I need it to work recursively and I'm not quite sure how to make that work:
my_hash.each do |k,v|
if v.class == Hash
my_hash[k] = [] << v
end
end
=> {"a"=>[{"a1"=>"b1"}], "b"=>"b", "c"=>[{"c1"=>{"c2"=>"c3"}}]}
You need to wrap your code into a method and call it recursively.
my_hash = {"a"=>{"a1"=>"b1"}, "b"=>"b", "c"=>{"c1"=>{"c2"=>"c3"}}}
def process(hash)
hash.each do |k,v|
if v.class == Hash
hash[k] = [] << process(v)
end
end
end
p process(my_hash)
#=> {"a"=>[{"a1"=>"b1"}], "b"=>"b", "c"=>[{"c1"=>[{"c2"=>"c3"}]}]}
Recurring proc is another way around:
h = {"a"=>{"a1"=>"b1"}, "b"=>"b", "c"=>{"c1"=>{"c2"=>"c3"}}}
h.map(&(p = proc{|k,v| {k => v.is_a?(Hash) ? [p[*v]] : v}}))
.reduce({}, &:merge)
# => {"a"=>[{"a1"=>"b1"}], "b"=>"b", "c"=>[{"c1"=>[{"c2"=>"c3"}]}]}
It can be done with single reduce, but that way things get even more obfuscated.
I have a terribly nested Json response.
[[{:test=>[{:id=>1, :b=>{id: '2'}}]}]]
There's more arrays than that but you get the idea.
Is there a way to recursively search through and find all the items that have a key I need?
I tried using this function extract_list() but it doesn't handle arrays well.
def nested_find(obj, needed_keys)
return {} unless obj.is_a?(Array) || obj.is_a?(Hash)
obj.inject({}) do |hash, val|
if val.is_a?(Hash) && (tmp = needed_keys & val.keys).length > 0
tmp.each { |key| hash[key] = val[key] }
elsif val.is_a?(Array)
hash.merge!(obj.map { |v| nested_find(v, needed_keys) }.reduce(:merge))
end
hash
end
end
Example
needed_keys = [:id, :another_key]
nested_find([ ['test', [{id:1}], [[another_key: 5]]]], needed_keys)
# {:id=>1, :another_key=>5}
The following is not what I'd suggest, but just to give a brief alternative to the other solutions provided:
2.1.1 :001 > obj = [[{:test=>[{:id=>1, :b=>{id: '2'}}]}]]
=> [[{:test=>[{:id=>1, :b=>{:id=>"2"}}]}]]
2.1.1 :002 > key = :id
=> :id
2.1.1 :003 > obj.inspect.scan(/#{key.inspect}=>([^,}]*)[,}]/).flatten.map {|s| eval s}
=> [1, "2"]
Note: use of eval here is just for an example. It would fail/produce incorrect results on anything whose inspect value was not eval-able back to the same instance, and it can execute malicious code:
You'll need to write your own recursive handler. Assuming that you've already converted your JSON to a Ruby data structure (via JSON.load or whatnot):
def deep_find_value_with_key(data, desired_key)
case data
when Array
data.each do |value|
if found = deep_find_value_with_key value, desired_key
return found
end
end
when Hash
if data.key?(desired_key)
data[desired_key]
else
data.each do |key, val|
if found = deep_find_value_with_key(val, desired_key)
return found
end
end
end
end
return nil
end
The general idea is that given a data structure, you check it for the key (if it's a hash) and return the matching value if found. Otherwise, you iterate it (if it's an Array or Hash) and perform the same check on each of it's children.
This will find the value for the first occurrence of the given key, or nil if the key doesn't exist in the tree. If you need to find all instances then it's slightly different - you basically need to pass an array that will accumulate the values:
def deep_find_value_with_key(data, desired_key, hits = [])
case data
when Array
data.each do |value|
deep_find_value_with_key value, desired_key, hits
end
when Hash
if data.key?(desired_key)
hits << data[desired_key]
else
data.each do |key, val|
deep_find_value_with_key(val, desired_key)
end
end
end
return hits
end