How can I marshal a hash with arrays?
s = Hash.new
s.default = Array.new
s[0] << "Tigger"
s[7] << "Ruth"
s[7] << "Puuh"
This code changes the default 3 times (which is probably what showed up in the dump), but it does not store anything in the hash. Try "puts s[8]", it will return [["Tigger"], ["Ruth"], ["Puuh"]].
A Hash.default_proc will do what you want
s = Hash.new{|hash,key| hash[key]=[] }
But you can't marshall a proc. This will work:
s = Hash.new
s.default = Array.new
s[0] += ["Tigger"]
s[7] += ["Ruth"]
s[7] += ["Puuh"]
This works because []+=["Tigger"] creates a new array.
An alternative, creating less arrays:
s = Hash.new
(s[0] ||= []) << "Tigger"
(s[7] ||= []) << "Ruth"
(s[7] ||= []) << "Puuh"
Only creates a new array when the key is absent (nil).
How to search nested hash of arrays and arrays of hash and return multiple matching objects from the parent node?
Here is a recursive solution to your "real" question. Since it mutates the original object, I used a "trick" to make a deep copy first. The keep_entries_with
produces an object with the original shape as your input, but since your output shape is different the second step just transforms the filtered result into the shape of your desired output.
deep_copy = Marshal.load(Marshal.dump(hash_or_array))
def keep_entries_with(target, obj)
return unless obj.is_a?(Hash) || obj.is_a?(Array)
case obj
when Hash
keep_entries_with(target, obj.values)
obj.keep_if do |k, v|
v == target ||
v.is_a?(Hash) && v.values.any? { _1 == target || _1.is_a?(Hash) || _1.is_a?(Array) } ||
v.is_a?(Array) && v.any? { _1 == target || _1.is_a?(Hash) || _1.is_a?(Array) }
end
when Array
obj.each do |v|
keep_entries_with(target, v)
end
end
end
filtered = keep_entries_with("buyer35", deep_copy)
final_result = filtered.first.map { |k, v| { k => v } }
pp final_result
which produces:
[{"book3"=>{"3"=>[{"6"=>[{"7"=>"buyer35"}]}]}},
{"book4"=>["buyer41", "buyer42", "buyer43", "buyer35"]}]
Flatten deep nested hash to array for sha1 hashing
EDIT : As you detailed, two hashes with keys in different order should give the same string. I would reopen the Hash class to add my new custom flatten method :
class Hash
def custom_flatten()
self.sort.map{|pair| ["key: #{pair[0]}", pair[1]]}.flatten.map{ |elem| elem.is_a?(Hash) ? elem.custom_flatten : elem }.flatten
end
end
Explanation :
sort
converts the hash to a sorted array of pairs (for the comparison of hashes with different keys order).map{|pair| ["key: #{pair[0]}", pair[1]]}
is a trick to differentiate keys from values in the final flatten array, to avoid the problem of{a: {b: {c: :d}}}.custom_flatten == {a: :b, c: :d}.custom_flatten
flatten
converts an array of arrays into a single array of valuesmap{ |elem| elem.is_a?(Hash) ? elem.custom_flatten : elem }
calls backfully_flatten
on any sub-hash left.
Then you just need to use :
require 'digest/sha1'
Digest::SHA1.hexdigest hash.custom_flatten.to_s
hash deep_merge with arrays
If h1
and h2
are the two hashes you wish to merge, and h3
is the desired result, I think the following should work:
@merge_key1, @merge_key2 = "key-one", "key-two"
def merge_em(g1, g2)
case g1
when Array
g1.size.times {|i| merge_em(g1[i], g2[i])}
when Hash
g1.keys.each do |k|
v = g1[k]
if (Hash === v || (Array === v && !v.empty?))
merge_em(v, g2[k])
else
g1[k] = {@merge_key1 => v, @merge_key2 => g2[k]}
end
end
end
end
h = Marshal.load(Marshal.dump(h1))
merge_em(h, h2)
p (h == h3) # => true
A few notes:
- This solution depends on the hash structures; it may not work if the structures are changed.
Marshal.load(Marshal.dump(h1))
is used to make a "deep copy" ofh1
, so thath1
is not modified. Ifh1
can be modified,merge_em(h1, h2)
is sufficient, withh1
being the merged hash.- The gem awesome_print gives you a nicely-formatted display of complex structures like these. All you'd need to do here is
require 'ap'
followed byap h
. Try it!
How do I copy a hash in Ruby?
The clone
method is Ruby's standard, built-in way to do a shallow-copy:
h0 = {"John" => "Adams", "Thomas" => "Jefferson"}
# => {"John"=>"Adams", "Thomas"=>"Jefferson"}
h1 = h0.clone
# => {"John"=>"Adams", "Thomas"=>"Jefferson"}
h1["John"] = "Smith"
# => "Smith"
h1
# => {"John"=>"Smith", "Thomas"=>"Jefferson"}
h0
# => {"John"=>"Adams", "Thomas"=>"Jefferson"}
Note that the behavior may be overridden:
This method may have class-specific behavior. If so, that behavior will be documented under the
#initialize_copy
method of the class.
Ruby hash of arrays remove first element from array
One might shift
a required element inplace.
arr.group_by &:shift
Whether you do not want to modify an original array:
arr.map(&:dup).group_by &:shift
Why is append a value to an array in hash will also modify the other variable that have it assigned?
Because value of b
and value of c
is the same object (check out Object#object_id
):
b.object_id == c.object_id
#=> true
Related Topics
Error Loading the 'Sqlite3' Active Record Adapter. When I Deploy in Heroku
How to Return the Number of Devise Users Currently Logged In
Understanding Ruby Method Parameters Syntax
Tilt (Kramdown) Preventing Erb Processing When Rendering Markdown
How to Strip Commas from Float Input
Rally Ruby Toolkit: How to Get Url of Portfolio Item's State
Why Are Database Entries Being Automatically Created When I Visit the "New" Page
Rails in Rendering Unnecessary Information
How to Install Ruby Gems on MAC
How to Ignore Irrelevant Methods When Profiling Ruby Applications
Create a Titleize Method, That Excludes "Little Words"
Assets Precompiling Error with Jquery UI Plugin
How to Convert a Large Gem to Standalone Rails App
Scope That Has Three Levels Deep Joins