Summing the values of an array of hashes in Ruby - ruby

I am having trouble figuring out the elegant way to add an array of hashes
[{:a=>1,:b=>2,:c=>3},{:a=>1,:b=>2,:c=>3},{:a=>1,:b=>2,:c=>3}]
should return
[{:a=>3,:b=>6,:c=>9}]
I know it would probably involve mapping/reducing, but I can't figure out the right syntax, doesn't help that ruby-doc dot org doesn't match my version
I am using 1.8.7

array.inject{|x,y| x.merge(y){|_,a,b| a + b}}
(verified on Ruby 1.8.7)

Related

Ruby Hash by default get sorted in ruby 1.8.7

I am using ruby 1.8.7 on a production server and my hash get automatically get sorted while assigning it to variable. and in local machine its working fine.
This is output taken from my production console
>> a = {"b" => "a", "a" => "c"}
=> {"a"=>"c", "b"=>"a"}
>> a
=> {"a"=>"c", "b"=>"a"}
>>
Have any ideas? How do I avoid sorting of hash?
Thanks
You shouldn't / can't. If your script relies on some specific ordering, then you most likely want to use a list of key/value pairs instead. When using a hash in 1.8, the order is not defined in the specification. When using 1.9 it is defined to enumerate in the insertion order.
Hash in 1.8.7 isn't ordered, meaning you cannot rely on the order of the key-value pairs. That you're seeing them sorted alphanumerically is a coincidence.
If you need to know the order of the key/value pairs, you can use ActiveSupport::OrderedHash (or upgrade to Ruby 1.9+ and use the builtin Hash).

reverse method - 1.9.1 v 1.8.7

I wrote some code:
output = File.open(text_file).collect.reverse.join("<BR>")
It seems to work okay on 1.8.7 but throws the error
NoMethodError - undefined method 'reverse' for #<Enumerator: #<File:C:\EduTester\cron\rufus.log>:collect>:
on 1.9.1 (ruby 1.9.3p194 (2012-04-20) [i386-mingw32])
Does somebody know why this happens and how to fix this? (Why is of most interest to me.)
First how to fix it - you should be doing this:
output = File.open(text_file).to_a.reverse.join("<BR>")
This will work on either version of Ruby. Basically you need to turn the file into an array of lines (with .to_a) before reversing them and adding line breaks.
In terms of the why (this gets a little technical): File mixes in the Enumerable module, which gives it methods like collect. Now in Ruby 1.87, if you called Enumberable.collect without a block it would return an Array. But in 1.9, it returns an Enumerator - which doesn't respond to the reverse method.
Here are the 2 versions of the method in question:
http://ruby-doc.org/core-1.8.7/Enumerable.html#method-i-collect
http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-collect
So basically before 1.9 .collect was a (hacky) equivalent to .to_a. But always use .to_a to turn something into an array.
In Ruby 1.8.7 if a block is given or not given with collect method, it returns an array.
But in 1.9 , it will only return array if block is given with collect method. Otherwise it will return enumerator object.
From documentation -
Collect method-
Returns a new array with the results of running block once for every element in enum.
If no block is given, an enumerator is returned instead.
in 1.8.7, the collect method returns an array when applied on a File object whereas in 1.9.3, it returns an enumerator. reverse method can be applied on an array only.
The reason this works in 1.8.7 is that when you call Enumerable#collect without a block in 1.8.7, it uses a default block which just returns its arg, so file.collect is equivalent to file.collect {|x| x} which returns an array of the lines in the file, on which in you can call Array#reverse`.
In 1.9.x, calling Enumerable#collect without a block returns an Enumerator. Enumerator does not natively support #reverse, nor does its mixin Enumerable. So, you get NoMethodError.
If you want to write this expression in a way compatible with either version, use #to_a instead of #collect.
output = File.open(text_file).to_a.reverse.join("<BR>")

Ruby Hash .keys and .values, safe to assume same order?

Rudimentary irb testing suggests that Ruby Hash returns .keys and .values in matching order. Is it safe to assume that this is the case?
Yes. According to the Ruby Docs for Hash, "Hashes enumerate their values in the order that the corresponding keys were inserted." So you should always get the same order for a hash if it is created in the same way.
Depends on which Ruby version you are running.
Up to 1.8, enumeration was not insertion-ordered. Starting with 1.9, it will enumerate keys and values according to insertion order so, yes, it is safe to assume as long as you are running 1.9.

How can I splattify an anonymous object so I can use &method on it?

I'm wanting to use the &method(:method_name) idiom when there's more than one object required by method_name. Can I do this under Ruby 1.9?
For example, if I've got
def move_file(old_filename, new_filename)
STDERR.puts "Moving #{old_filename.inspect} to #{new_filename.inspect}"
# Implementation for careful moving goes here
end
old_filenames = ["foo.txt", "bar.txt", "hoge.ja.txt"]
new_filenames = ["foo_20110915.txt", "bar_20110915.txt", "hoge_20110915.ja.txt"]
the code
old_filenames.zip(new_filenames).each(&method(:move_file))
works under Ruby 1.8, but not under Ruby 1.9. Under Ruby 1.9, it's trying to do move_file(["foo.txt", "foo_20110915.txt"]) instead of move_file("foo.txt", "foo_20110915.txt").
How do I splattify it so it has the correct arity?
Workarounds I'm aware of:
Replace def move_file(old_filename, new_filename) with def move_file(*arguments)
Replace each(&method(:move_file)) with
each{|old_filename, new_filename| move_file(old_filename, new_filename)}
Instead
each{|old_filename, new_filename| move_file(old_filename, new_filename)}
you should be able to do
each{|pair| move_file(*pair)}
But I don't know how you'd pull off blockless variant (I needed it couple of times as well). I guess &-shorthand was made to make the syntax simpler, and is not meant to be clogged much (whether it will be passed an array as an array, or splatted, for example). :)
How do I splattify it so it has the correct arity?
I don't think there is a way to do this while being compatible to both Ruby versions. What you could do is wrap it into a lambda
move_from_to = Proc.new {|*both| move_files(*both) }
The thing is - block and proc arity is something that got addressed in Ruby 1.9 so there might be a difference in behavior there. Also see prc.lambda? here http://www.ruby-doc.org/core/classes/Proc.html for info on what it does to the arity.
This question is also related to what you want to do (the solution there is to resplat and unsplat manually): Inconsistency of arity between Hash.each and lambdas

Ruby: What is the order of keys/values returned by Hash.keys and Hash.values methods?

Is it the same order in which the items were added to the Hash ?
The top of the Ruby 1.9.2 documentation for the Hash class declares:
Hashes enumerate their values in the order that the corresponding keys were inserted.
Cursory tests suggest that this does indeed apply to both Hash#keys and Hash#values, although the corresponding documentation for those methods doesn't seem to specify it.
In Ruby 1.8, there's no guaranteed order for the elements in a hash.

Resources