Merge two hashes and sum nested hashes within each - ruby

I have two hashes:
g_record = {"4-2014"=>{:total_conversions=>0, :total_cost=>0.0}, "5-2014"=>{:total_conversions=>0, :total_cost=>189.8}, "6-2014"=>{:total_conversions=>0, :total_cost=>474.18}}
b_record = {"4-2014"=>{:total_conversions=>7, :total_cost=>639.7}, "5-2014"=>{:total_conversions=>5, :total_cost=>298.03}}
My desired output is:
combined_record = {"4-2014"=>{:total_conversions=>7, :total_cost=>639.7}, "5-2014"=>{:total_conversions=>5, :total_cost=>487.83}, "6-2014"=>{:total_conversions=>0, :total_cost=>474.18}}
I've had to combine hashes and sum values before and have been trying variations of
g_record.merge(b_record) {|a, b, c| b + c}
But this time I don't understand how I should get inside the intial hashes to run something like that. I feel like I should be able to do something like
g_record.merge(b_record).each {|e| e.merge {|a, b, c| b + c}
But I know that is incorrect.

I'd do :
merged_hash = g_record.merge(b_record) do |_, o, n|
o.merge(n) { |_, o1, n1| o1 + n1 }
end
merged_hash
# => {"4-2014"=>{:total_conversions=>7, :total_cost=>639.7},
# "5-2014"=>{:total_conversions=>5, :total_cost=>487.83},
# "6-2014"=>{:total_conversions=>0, :total_cost=>474.18}}

Related

How to get an array with true/false values after comparing 2 arrays in ruby?

I am trying to generate array as a result of comparison 2 arrays as:
a = %w{a b a e}
b = %w{c d a e}
After comparing it should give result as:
c = [false, false, true, true]
Is there any ruby way which is better than doing a for loop because I have to do this comparison with a lot of arrays.
This way?
a.zip(b).map { |a, b| a == b }
Uglier, but just to show an alternative:
a.map.with_index { |aa, i| aa == b[i] }

Ruby block taking array or multiple parameters

Today I was surprised to find ruby automatically find the values of an array given as a block parameter.
For example:
foo = "foo"
bar = "bar"
p foo.chars.zip(bar.chars).map { |pair| pair }.first #=> ["f", "b"]
p foo.chars.zip(bar.chars).map { |a, b| "#{a},#{b}" }.first #=> "f,b"
p foo.chars.zip(bar.chars).map { |a, b,c| "#{a},#{b},#{c}" }.first #=> "f,b,"
I would have expected the last two examples to give some sort of error.
Is this an example of a more general concept in ruby?
I don't think my wording at the start of my question is correct, what do I call what is happening here?
Ruby block are quirky like that.
The rule is like this, if a block takes more than one argument and it is yielded a single object that responds to to_ary then that object is expanded. This makes yielding an array versus yielding a tuple seem to behave the same way for blocks that take two or more arguments.
yield [a,b] versus yield a,b do differ though when the block takes one argument only or when the block takes a variable number of arguments.
Let me demonstrate both of that
def yield_tuple
yield 1, 2, 3
end
yield_tuple { |*a| p a }
yield_tuple { |a| p [a] }
yield_tuple { |a, b| p [a, b] }
yield_tuple { |a, b, c| p [a, b, c] }
yield_tuple { |a, b, c, d| p [a, b, c, d] }
prints
[1, 2, 3]
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, nil]
Whereas
def yield_array
yield [1,2,3]
end
yield_array { |*a| p a }
yield_array { |a| p [a] }
yield_array { |a, b| p [a, b] }
yield_array { |a, b, c| p [a, b, c] }
yield_array { |a, b, c, d| p [a, b, c, d] }
prints
[[1, 2, 3]]
[[1, 2, 3]]
[1, 2] # array expansion makes it look like a tuple
[1, 2, 3] # array expansion makes it look like a tuple
[1, 2, 3, nil] # array expansion makes it look like a tuple
And finally to show that everything in Ruby uses duck-typing
class A
def to_ary
[1,2,3]
end
end
def yield_arrayish
yield A.new
end
yield_arrayish { |*a| p a }
yield_arrayish { |a| p [a] }
yield_arrayish { |a, b| p [a, b] }
yield_arrayish { |a, b, c| p [a, b, c] }
yield_arrayish { |a, b, c, d| p [a, b, c, d] }
prints
[#<A:0x007fc3c2969190>]
[#<A:0x007fc3c2969050>]
[1, 2] # array expansion makes it look like a tuple
[1, 2, 3] # array expansion makes it look like a tuple
[1, 2, 3, nil] # array expansion makes it look like a tuple
PS, the same array expansion behavior applies for proc closures which behave like blocks, whereas lambda closures behave like methods.
Ruby's block mechanics have a quirk to them, that is if you're iterating over something that contains arrays you can expand them out into different variables:
[ %w[ a b ], %w[ c d ] ].each do |a, b|
puts 'a=%s b=%s' % [ a, b ]
end
This pattern is very useful when using Hash#each and you want to break out the key and value parts of the pair: each { |k,v| ... } is very common in Ruby code.
If your block takes more than one argument and the element being iterated is an array then it switches how the arguments are interpreted. You can always force-expand:
[ %w[ a b ], %w[ c d ] ].each do |(a, b)|
puts 'a=%s b=%s' % [ a, b ]
end
That's useful for cases where things are more complex:
[ %w[ a b ], %w[ c d ] ].each_with_index do |(a, b), i|
puts 'a=%s b=%s # %d' % [ a, b, i ]
end
Since in this case it's iterating over an array and another element that's tacked on, so each item is actually a tuple of the form %w[ a b ], 0 internally, which will be converted to an array if your block only accepts one argument.
This is much the same principle you can use when defining variables:
a, b = %w[ a b ]
a
# => 'a'
b
# => 'b'
That actually assigns independent values to a and b. Contrast with:
a, b = [ %w[ a b ] ]
a
# => [ 'a', 'b' ]
b
# => nil
I would have expected the last two examples to give some sort of error.
It does in fact work that way if you pass a proc from a method. Yielding to such a proc is much stricter – it checks its arity and doesn't attempt to convert an array argument to an argument list:
def m(a, b)
"#{a}-#{b}"
end
['a', 'b', 'c'].zip([0, 1, 2]).map(&method(:m))
#=> wrong number of arguments (given 1, expected 2) (ArgumentError)
This is because zip creates an array (of arrays) and map just yields each element, i.e.
yield ['a', 0]
yield ['b', 1]
yield ['c', 2]
each_with_index on the other hand works:
['a', 'b', 'c'].each_with_index.map(&method(:m))
#=> ["a-0", "b-1", "c-2"]
because it yields two separate values, the element and its index, i.e.
yield 'a', 0
yield 'b', 1
yield 'c', 2

Ruby: How to access each element of an array inside a hash where hash key is the array I want to access

I have:
Array1 = [x, y, z]
Array2 = [m, n]
a = b
hash1 = {Array1 => val1,
Array2 => val2,
a => c
}
How to iterate inside each element of Array1, Array2 inside the hash1?
hash1.each do |t|
t[0] #gives me Array1 as a string. I need [x,y,z]
end
It don't give you a string. It give you the correct array.
{
[1,2] => 'a'
}.each{|t| puts t[0].class}
# prints array
{
[1,2] => 'a'
}.each{|t| puts t[0][0]}
# prints 1
Note that you are doing each on a hash. You can deconstruct the key-value pair giving two variables to the block, like this:
{a:1, b:2}.each { |k,v| p k; p v }
#prints :a
#prints 1
#prints :b
#prints 2
Something like this
hash1.keys.each do |k|
if k.is_a?(Array)
k.each do |v|
.. Do something here ..
end
end
end
Just replace the Do something here with the code you want, and v will be the value in the array

How do I create three objects with one line of code?

How would I create three empty hashes with a single line of code?
I know that a = b = c = Hash.new won't work, since that'll create three references to the same Hash object.
a,b,c = Hash.new will assign the Hash to a, but b and c remain nil.
I know I could do a, b, c = Hash.new, Hash.new, Hash.new, but that doesn't look very DRY.
As I posted as a comment, I think a, b, c = {}, {}, {} is the best way, because it's short, and easy to read. If you really want to do it in a more complicated way, something like this will work:
>> a, b, c = Array.new(3) { Hash.new } #=> [{}, {}, {}]
>> a #=> {}
>> b #=> {}
>> c #=> {}
I am not really sure if I would use that, but it is possible:
a, b, c = 3.times.map { Hash.new }
# or
a, b, c = (1..3).map { Hash.new }
Although you already marked an answer, I'd throw in another way which I find as the simplest one:
a,b,c = [{}]*3
I am not really sure if I would use that, but it is possible:
a, b, c = 3.times.map { Hash.new }
#or
a, b, c = (1..3).map { Hash.new }
And yet another answer.. since you can simply use {} instead of Hash.new
The assignation could be like this:
a, b, c = 3.times.map{{}}

Ruby 1.8: Hash#sort not return hash but array (better way to do this?)

In some scenario of Ruby 1.8. If I have a hash
# k is name, v is order
foo = { "Jim" => 1, "bar" => 1, "joe" => 2}
sorted_by_values = foo.sort {|a, b| a[1] <==> b[1]}
#sorted_by_values is an array of array, it's no longer a hash!
sorted_by_values.keys.join ','
my workaround is to make method to_hash for Array class.
class Array
def to_hash(&block)
Hash[*self.collect { |k, v|
[k, v]
}.flatten]
end
end
I can then do the following:
sorted_by_values.to_hash.keys.join ','
Is there a better way to do this?
Hashes are unordered by definition. There can be no such thing as a sorted Hash. Your best bet is probably to extract the keys from the sorted array using collect and then do a join on the result
sortedByValues = foo.sort {|a, b| a[1] <==> b[1]}
sortedByValues.collect { |a| a[0] }.join ','

Resources