0
votes

I have two hashes:

a = {"0"=>"name", "1"=>"email"} 
b = {"0"=>"source", "1"=>"info", "2"=>"extra", "3"=>"name"} 

I want a hash created by doing the following:

1) When the two hashes contain identical values, keep value of original hash and discard value of second hash.

2) When the values of second hash are not in first hash, just add to the end of new hash, making sure that the key is ordered.

with this result:

{"0"=>"name", "1"=>"email", "2"=>"source", "3"=>"info", "4"=>"extra"}

I did it this ugly way:

l1 = a.keys.length
l2 = b.keys.length
max = l1 > l2 ? l1 : l2
counter = l1
result = {}
max.times do |i|
  unless a.values.include? b[i.to_s]
    result[counter.to_s] = b[i.to_s]
    counter += 1
  end
end
a.merge!(result)

Is there a built-in ruby method or utility that could achieve this same task in a cleaner fashion?

3
That's a pretty oddball data structure. Why not use arrays instead? The string indexes seem totally arbitrary and will be a total pain to sort. 0, 1, 10, 2 for example if they're strings. - tadman
there's no such thing as ordered hashes. - Mircea
@Mircea, I believe Ruby 1.9 added ordered hashes, which preserve the insertion order. - Daniel Stevens
oh wow @DanielStevens I did not know that :) - Mircea

3 Answers

2
votes
(a.values + b.values).uniq.map.with_index{|v, i| [i.to_s, v]}.to_h
# => {"0"=>"name", "1"=>"email", "2"=>"source", "3"=>"info", "4"=>"extra"}
1
votes

First create an array containing the values in the hash. This can be accomplished with the concat method. Now that we have an array, we can call the uniq method to retrieve all unique values. This also preserves the order.

a = { "0" => "name", "1" => "email" }
b = { "0" => "source", "1" => "info", "2" => "extra", "3" => "name" }
values = a.values.concat(b.values).uniq

A shortcut to generating a hash in Ruby is with this trick.

Hash[[*0..values.length-1].zip(values)]

Output:

{0=>"name", 1=>"email", 2=>"source", 3=>"info", 4=>"extra"}
1
votes
a = {"0"=>"name", "1"=>"email"} 
b = {"0"=>"source", "1"=>"info", "2"=>"extra", "3"=>"name"} 

key = (a.size-1).to_s
  #=> "1"
b.each_value.with_object(a) { |v,h| (h[key.next!] = v) unless h.value?(v) }
  #=> {"0"=>"name", "1"=>"email", "2"=>"source", "3"=>"info", "4"=>"extra"}