How to Marshal a Hash with Arrays

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 values
  • map{ |elem| elem.is_a?(Hash) ? elem.custom_flatten : elem } calls back fully_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" of h1, so that h1 is not modified. If h1 can be modified, merge_em(h1, h2) is sufficient, with h1 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 by ap 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



Leave a reply



Submit