sum of all elements in hash of arrays - ruby

I am given a hash with values as an array of integers. I want to add all values of same index in my result. The length of all arrays in hash values is same.
For ex,
hash = {
"A" => [1,2,3],
"B" => [1,2,3],
"C" => [1,2,3]
}
Then the resultant array i want,adding all same index elements together will be [3,6,9].
Is there any efficient way to do this in ruby ?? Or looping over the keys and array elements is the best possible solution ??
Thanks.

Here's one way:
hash.values.transpose.map { |r| r.reduce(:+) }
#=> [3,6,9]
Another would be:
values = hash.values
values.shift.zip(*values).map { |r| r.reduce(:+) }
#=> [3,6,9]
If you'd prefer using using linear algebra, you could do this:
require 'matrix'
(Matrix.row_vector([1]*hash.size)*Matrix[*hash.values]).row(0).to_a
#=> [3, 6, 9]

Related

Sort hash by length of values (descending)

I'm having problems sorting a hash by the length of the array values in descending order. I have the following hash:
hash = {
"1" => [0,3],
"2" => [0,2],
"3" => [1,2,3,4],
"4" => [1,8,7,6,5],
"5" => [7,8],
"10" => [5]
}
I want to sort it to be in this order: 4,3,1,2,5,10.
hash.sort_by {|k,v| v.length}.reverse
What am I not doing right? Any ideas?
It seems you are looking for Enumerable#sort_by like this (as a note, this could be hash.sort_by {|_,v| -v.length}.to_h depending on the Ruby version. I used Hash[] because of it's compatibility).
Hash[hash.sort_by {|_,v| -v.length}]
#=>
# {
# "4"=>[1, 8, 7, 6, 5],
# "3"=>[1, 2, 3, 4],
# "1"=>[0, 3],
# "2"=>[0, 2],
# "5"=>[7, 8],
# "10"=>[5]
# }
Sorting a Hash using Enumerable#sort_by will return an associative array of [[key,value],[key,value],...] when called with a block (otherwise it returns an Enumerator). Since Hash understands associative Array structure, you can easily turn this back into a Hash by calling associative_array.to_h (Ruby >= 2.1) or Hash[associative_array] (for all Ruby Versions).
You cannot sort a hash - that might be causing your confusion. There is no "internal" ordering of elements of a hash as it appears in an array.
You can, however, iterate over a hash in a certain order, e.g.
hash.sort_by {|k,v| v.length}.reverse.each do |k, v|
puts "k = #{k}, v = #{v}"
end

How does `Hash#sort{|a, b| block}` work?

In ruby-doc I see this example:
h = { "a" => 20, "b" => 30, "c" => 10 }
h.sort {|a,b| a[1]<=>b[1]} #=> [["c", 10], ["a", 20], ["b", 30]]
Can anyone explain what a[1]<=>b[1] means? What are we comparing here? Is a is a key and b its value? Why we are comparing index 1?
a and b are both arrays of [key, value] which come from Hash#sort.
Converts hsh to a nested array of [ key, value ] arrays and sorts it, using Array#sort.
So a[1]<=>b[1] sorts the resulting pairs by the value. If it were a[0]<=>b[0] it would be sorting by the key.
Ruby doesn't have a key-value-pair or tuple datatype, so all Hash iteration methods (each, map, select, sort, …) represent hash entries as an Array with two elements, [key, value]. (In fact, most methods aren't even implemented in Hash, they are inherited from Enumerable and don't even know anything about keys and values.)
Meditate on this:
h = { "a" => 20, "b" => 30, "c" => 10 }
h.sort { |a,b| # => {"a"=>20, "b"=>30, "c"=>10}
a[1]<=>b[1]
} # => [["c", 10], ["a", 20], ["b", 30]]
For each loop of the key/value pairs in h, Ruby passes each key/value pair into the block as an array of two elements.

Sort hash by order of keys in secondary array

I have a hash:
hash = {"a" => 1, "b" =>2, "c" => 3, "d" => 4}
And I have an array:
array = ["b", "a", "d"]
I would like to create a new array that is made up of the original hash values that correspond with original hash keys that are also found in the original array while maintaining the sequence of the original array. The desired array being:
desired_array = [2, 1, 3]
The idea here is to take the word "bad", assign numbers to the alphabet, and then make an array of the numbers that correspond with "b" "a" and "d" in that order.
Since your question is a little unclear I'm assuming you want desired_array to be an array (you say you want a new array and finish the sentence off with new hash). Also in your example I'm assuming you want desired_array to be [2, 1, 4] for ['b', 'a', 'd'] and not [2, 1, 3] for ['b', 'a', 'c'].
You should just you the Enumerable#map method to create a array that will map the first array to the your desired array like so:
desired_array = array.map { |k| hash[k] }
You should familiarize yourself with the Enumerable#map method, it's quite the handy method. From the rubydocs for the method: Returns a new array with the results of running block once for every element in enum. So in this case we are iterating through array and invoking hash[k] to select the value from the hash and creating a new array with values selected by the hash. Since iteration is in order, you will maintain the original sequence.
I would use Enumerable#map followed by Enumerable#sort_by, for example:
hash = {"d" => 4, "b" =>2, "c" => 3, "a" => 1}
order = ["b", "a", "d"]
# For each key in order, create a [key, value] pair from the hash.
# (Doing it this way instead of filtering the hash.to_a is O(n) vs O(n^2) without
# an additional hash-probe mapping. It also feels more natural.)
selected_pairs = order.map {|v| [v, hash[v]]}
# For each pair create a surrogate ordering based on the `order`-index
# (The surrogate value is only computed once, not each sort-compare step.
# This is, however, an O(n^2) operation on-top of the sort.)
sorted = selected_pairs.sort_by {|p| order.find_index(p[0]) }
p sorted
# sorted =>
# [["b", 2], ["a", 1], ["d", 4]]
I've not turned the result back into a Hash, because I am of the belief that hashes should not be treated as having any sort of order, except for debugging aids. (Do keep in mind that Ruby 2 hashes are ordered-by-insertion.)
All you need is values_at:
hash.values_at *array
Enumerable methods map, each works perfect
desired_array = array.map { |k| hash[k] }
or
desired_array = array.each { |k| hash[k] }

Having trouble with the sort method

I am building a histogram based on of the amount of words in a text file. I have an array of hashes whose keys are the words and the values are the amount of times the word appears per line. I need to use the sort method on this array of hashes to sort the values in order of the most occurring word to the least. This is what my sort line looks like:
twoOfArray.sort { |k, v| v <=> k }
twoOfArray.each { |key, value| puts "#{key} occurs #{value} times" "\n"}
Full code is here. If I use the sort! method, I get an undefined method error. Does anyone know why?
I would convert your data structure (an array of hashes) into just one large hash. If you want to sort the words, there's no reason to have them in separate hashes.
Then, if your hash is something like {'the' => 5, 'and' => 23, 'beer' => 2} you can sort via:
> h = {'the' => 5, 'and' => 23, 'beer' => 2}
> a = h.sort {|a, b| b[1] <=> a[1] } # sort converts a hash into an array of arrays.
> a
#=> [['and', 23], ['the', 5], ['beer', 2]]

ruby noob: are hashes speedy and optimal for storage or should I make a tuple?

This is a pretty simple problem im working on in Ruby, but im a total noob so I want to learn the most correct solution. I have a list of data with a name and a value. I need to remember all those (obvious answer: hash). But, i also need to remember the order of this data. SO it looks like:
x=1
y=2
z=3
I was thinking about making an array of 1 element hashes:
[0 => {'x' => 1},
1 => {'y' => 2},
2 => {'z' => 3}]
Are hashes the best choice in this situation? Is there some reason they would be slow, or not optimal?
Use Ruby 1.9. Hashes are ordered here.
You could try OrderedHash from ActiveSupport or Dictionary using Ruby Facets.
Performance permitting, an associate array would work. An associative array is an array of arrays, with each sub-array containing two elements:
a = [
[:x, 1],
[:y, 2],
[:z, 3],
]
You can use assoc to find a sub-array by its first element:
p a.assoc(:x) # [:x, 1]
p a.assoc(:x).last # 1
Or rassoc to find a sub-array by its last element:
p a.rassoc(2) # [:y, 2]
p a.rassoc(2).first # :y
Whether or not this approach will work for you depend upon the size of the list and how often you have to search it. On my machine, finding the last element in a 1000 element list takes about 100 microseconds.
Another approach is to use a plain, unordered (in Ruby <= 1.8.7) hash:
h = {
:x => 1,
:y => 2,
:z => 3,
}
And order it at the time you do an operation where order matters:
sorted_h = h.sort_by do |key, value|
key.to_s
end.each do |key, value|
p [key, value]
end
# [:x, 1]
# [:y, 2]
# [:z, 3]
This approach is good for alphabetical or numerical ordering (for example). It's not so good for order of insertion.

Resources