0

I have two hashes:

first = { 1 => [15, 15, 15, 8], 4 => [11, 12, 7, 7], 5 => [14, 17, 13, 13],
          6 => [19, 19, 15, 15], 7 => [5, 12, 12, 12], 8 => [10, 14, 14, 14], 
          9 => [8, 7, 8, 8] } 

second = { 1 => [0, 1, 2], 4 => [2, 3], 5 => [2, 3], 6 => [0, 1, 2, 3], 
           7 => [1, 2, 3], 8 => [1, 2, 3], 9 => [2, 3] }

As you see they both use same keys, but values are different. second's values contains ranges of indexes of the first's values and I need to sum only those first's values that are in those ranges.

The expected output:

result = { 1 => [45], 4 => [14], 5 => [26], etc }
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
Julius Dzidzevičius
  • 10,775
  • 11
  • 36
  • 81
  • 1
    Explain this result = { 1 => [45], 4 => [14], 5 => [26], etc } properly – gates Apr 04 '17 at 16:37
  • 1
    What is the code you are having trouble with? [so] is not a Write-My-Code-For-Me-Service. Those *do* exist, they are called "programmers" and you can hire them for a fee. – Jörg W Mittag Apr 04 '17 at 17:42

4 Answers4

8

A straightforward way is to say exactly what you mean:

first.map { |k, v| [ k, [ v.values_at(*second[k]).sum ] ] }.to_h

If your version of Ruby doesn't have Array#sum then use inject(:+):

first.map { |k, v| [ k, [ v.values_at(*second[k]).inject(:+) ] ] }.to_h

You could also skip the map/to_h business by using each_with_object instead:

first.each_with_object({}) { |(k, v), h| h[k] = [ v.values_at(*second[k]).inject(:+) ] }

A little time with the Array#values_at documentation might be fruitful as would the Enumerable documentation.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
3

More natural way is to use Hash#merge, I think:

first.merge(second) { |_, f, s| [f.values_at(*s).sum] }
#=> {1=>[45], 4=>[14], 5=>[26], 6=>[68], 7=>[36], 8=>[42], 9=>[16]}

Of course, you can use inject(:+) instead of sum if your Ruby version < 2.4

Ilya
  • 13,337
  • 5
  • 37
  • 53
2

Since you say the elements of second are ranges, you might write them as such:

second = { 1=>0..2, 4=>2..3, 5=>2..3, 6=>0..3, 7=>1..3, 8=>1..3, 9=>2..3 }

Then

second.merge(second) { |k,o,_| first[k].values_at(*o).sum }
  #=> {1=>45, 4=>14, 5=>26, 6=>68, 7=>36, 8=>42, 9=>16}

This uses the form of Hash#merge that employs a block to determine the values of keys that are present in both hashes being merged, which here is all keys. See the doc for details, particularly the values of the three block variables k, o, and n. (Here I've replaced n with _ to signify that it is not used in the block calculation.)

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
1

Try this:

def sum(list, indices)
    sum = 0
    indices.each do |index|
        sum += list[index]
    end
   return sum
end

And then

second.map { |k, v| [k, sum(first[k], v)] }.to_h