Ruby 散列中特定值的总和

Sum of particular values in a Ruby hash

我有两个哈希:

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] }

如您所见,它们都使用相同的键,但值不同。 second 的值包含 first 的值的索引范围,我只需要对那些在这些范围内的 first 的值求和。

预期输出:

result = { 1 => [45], 4 => [14], 5 => [26], etc }

一个直接的方法就是准确地说出你的意思:

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

如果您的 Ruby 版本没有 Array#sum 则使用 inject(:+):

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

您也可以使用 each_with_object 来跳过 map/to_h 业务:

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

Array#values_at documentation might be fruitful as would the Enumerable documentation.

的一点时间

试试这个:

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

然后

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

更自然的方式是使用Hash#merge,我认为:

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

当然,如果您的 Ruby 版本 < 2.4

,您可以使用 inject(:+) 而不是 sum

既然你说 second 的元素是范围,你可以这样写:

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

然后

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}

这使用 Hash#merge 的形式,它使用一个块来确定要合并的两个哈希中存在的键的值,这里是所有键。有关详细信息,请参阅文档,特别是三个块变量 kon 的值。 (这里我用 _ 替换了 n 以表示它不用于块计算。)