1

i want to convert in ruby

[[1, 1], [2, 3], [3, 5], [4, 1], [1, 2], [2, 3], [3, 5], [4, 1]]

into

[{1=>1}, {2=>3}, {3=>5}, {4=>1}, {1=>2}, {2=>3}, {3=>5}, {4=>1}]

and after this to obtain sum of all different keys:

{1=>3,2=>6,3=>10,4=>2}
2
  • do you need the second step or just want to achieve the last step Commented Sep 6, 2012 at 12:56
  • no , the second step was just to make task more clear Commented Sep 7, 2012 at 8:42

4 Answers 4

2

For the second question

sum = Hash.new(0)
original_array.each{|x, y| sum[x] += y}
sum # => {1 => 3, 2 => 6, 3 => 10, 4 => 2}
Sign up to request clarification or add additional context in comments.

3 Comments

Unless I know I'm the only user of the hash, I don't like doing things like this, it returns a hash which behaves in ways most people aren't expecting. e.g. it's standard practice to check existence with Hash#[], and this will then return a truthy value.
@JoshuaCheek That is not the standard way to check existence. That will fail when you have nil values. The standard way is to use Hash#key?, and my code works well with it.
The ratio in which I have seen Hash#[] to Hash#key? is probably on the order of 25:1, and that only because I'm grouping Hash#has_key? in with it. In those cases, it was used because false was a legitimate hash value (or the hash contents weren't known). Personally, as soon as I use a default value or block, I stop thinking of it as a hash and start thinking of it as an interface (which I occasionally replace later with a custom object).
2

Functional approach:

xs = [[1, 1], [2, 3], [3, 5], [4, 1], [1, 2], [2, 3], [3, 5], [4, 1]]    
Hash[xs.group_by(&:first).map do |k, pairs| 
  [k, pairs.map { |x, y| y }.inject(:+)]
end]
#=> {1=>3, 2=>6, 3=>10, 4=>2}

Using Facets is much simpler thanks to the abstractions map_by (a variation of group_by) and mash (map + Hash):

require 'facets'
xs.map_by { |k, v| [k, v] }.mash { |k, vs| [k, vs.inject(:+)] }
#=> {1=>3, 2=>6, 3=>10, 4=>2}

Comments

1

You don't need the intermediate form.

arrays = [[1, 1], [2, 3], [3, 5], [4, 1], [1, 2], [2, 3], [3, 5], [4, 1]]

aggregate = arrays.each_with_object Hash.new do |(key, value), hash|
  hash[key] = hash.fetch(key, 0) + value
end

aggregate # => {1=>3, 2=>6, 3=>10, 4=>2}

3 Comments

Hmm. My first questions as a Java developer, does this involve any form of virgin sacrifice, or does it work out of the box?
@willcodejavaforfood not really sure what you mean. If you'd like I can explain anything that isn't apparent.
@Joshua Cheek - It does look like magic to me at first glance, but having stared at it for a while it's beginning to make sense :)
0
arr= [[1, 1], [2, 3], [3, 5], [4, 1], [1, 2], [2, 3], [3, 5], [4, 1]]

final = Hash.new(0)

second_step = arr.inject([]) do |arr,inner|
   arr << Hash[*inner]
   final[inner.first] += inner.last
   arr
end

second_step
#=> [{1=>1}, {2=>3}, {3=>5}, {4=>1}, {1=>2}, {2=>3}, {3=>5}, {4=>1}] 
final
#=> {1=>3, 2=>6, 3=>10, 4=>2}

if you directly only need the last step

arr.inject(Hash.new(0)){|hash,inner| hash[inner.first] += inner.last;hash}
=> {1=>3, 2=>6, 3=>10, 4=>2} 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.