哈希中具有多个值的重复值之和

时间:2018-12-23 02:02:02

标签: ruby

给出如下所示的哈希值:

h = {
  "0" => ["1", "true", "21"],
  "1" => ["2", "true", "21"],
  "2" => ["3", "false", "21"],
  "3" => ["4", "true", "22"],
  "4" => ["5", "true", "22"],
  "5" => ["6", "true", "22"],
  "6" => ["7", "false", "21"]
}

我想找到在索引01上具有相同元素的数组中位置2的元素总和,并返回如下所示的哈希值:

{
  0 => ["3", "true", "21"],
  1 => ["10", "false", "21"],
  2 => ["15", "true", "22"]
}

由于存在两个索引为12且值分别为"true""21"的数组,我想将索引0的整数值求和例如,这两个数组。

如何将此问题顶部的示例哈希转换为其下方的结果哈希?

7 个答案:

答案 0 :(得分:2)

我不是ruby开发人员,所以我不建议任何最佳实践,而是在阅读本文后想到的简单算法是创建一个新的哈希值,并检查其中是否包含数组值,如果不是,则附加这样的新值。

h = {
  "0" => ["1", "true", "21"],
  "1" => ["2", "true", "21"],
  "2" => ["3", "false", "21"],
  "3" => ["4", "true", "22"],
  "4" => ["5", "true", "22"],
  "5" => ["6", "true", "22"],
  "6" => ["7", "false", "21"]
}

new_h = {}
h.each do |key, val|
  x1 = val.at(1)
  x2 = val.at(2)
  found = false
  new_h.each do |key1, val2|
    y1 = val2.at(1)
    y2 = val2.at(2)
    if x1 === y1 && x2 === y2
      found = true
      arr = [val2.at(0).to_i + val.at(0).to_i, x1, x2]
      new_h[key1] = arr
    end
  end
  if !found
    new_h[new_h.length] = val
  end
  if new_h.empty?
    new_h[key] = val
  end
end

puts "#{new_h}"

答案 1 :(得分:2)

代码

def group_em(h)    
  h.group_by { |_,v| v.drop(1) }.
    transform_values do |a|
      a.transpose.
        last.
        map(&:first).
        sum(&:to_i).
        to_s
    end.
    each_with_index.
    with_object({}) { |((a,v),i),g| g[i] = [v,*a] }
end

示例

h = {
  "0" => ["1", "true",  "21"],
  "1" => ["2", "true",  "21"],
  "2" => ["3", "false", "21"],
  "3" => ["4", "true",  "22"],
  "4" => ["5", "true",  "22"],
  "5" => ["6", "true",  "22"],
  "6" => ["7", "false", "21"]
}

group_em(h)
  #=> {0=>["3",  "true",  "21"],
  #    1=>["10", "false", "21"],
  #    2=>["15", "true",  "22"]}

说明

主要步骤

对于上面的哈希h,主要步骤如下。

p = h.group_by { |_,v| v.drop(1) }
  #=> {["true",  "21"]=>[["0", ["1", "true",  "21"]],
  #                      ["1", ["2", "true",  "21"]]],
  #    ["false", "21"]=>[["2", ["3", "false", "21"]],
  #                      ["6", ["7", "false", "21"]]],
  #    ["true",  "22"]=>[["3", ["4", "true",  "22"]],
  #                      ["4", ["5", "true",  "22"]],
  #                      ["5", ["6", "true",  "22"]]]}
q = p.transform_values do |a|
      a.transpose.
        last.
        map(&:first).
        sum(&:to_i).
        to_s
    end
  #=> {["true", "21"]=>"3", ["false", "21"]=>"10", ["true", "22"]=>"15"}
enum0 = q.each_with_index
  #=> #<Enumerator: {["true", "21"]=>"3", ["false", "21"]=>"10",
  #     ["true", "22"]=>"15"}:each_with_index>
enum1 = enum0.with_object({})
  #=> #<Enumerator: #<Enumerator: {["true", "21"]=>"3", ["false", "21"]=>"10",
  #     ["true", "22"]=>"15"}:each_with_index>:with_object({})>
enum1.each { |((a,v),i),g| g[i] = [v,*a] }
  #=> {0=>["3", "true", "21"],
  #    1=>["10", "false", "21"],
  #    2=>["15", "true", "22"]}

我们可以看到枚举器enum1通过将其转换为数组将生成并传递给该块的值:

enum1.to_a
  #=> [[[[["true", "21"], "3"], 0], []],
  #    [[[["false", "21"], "10"], 1], []],
  #    [[[["true", "22"], "15"], 2], []]]

如果将enum0的返回值与enum1的返回值进行比较,则可以将后者视为复合枚举器,尽管Ruby没有使用该术语。 / p>

Hash#transform_values

的详细信息

现在让我们更仔细地研究q的计算。 p的第一个值由Hash#transform_values(在MRI 2.4中首次出现)传递到块,并成为块变量a的值:

a = p.first.last
  #=> [["0", ["1", "true", "21"]], ["1", ["2", "true", "21"]]]

块的计算如下。

b = a.transpose
  #=> [["0", "1"], [["1", "true", "21"], ["2", "true", "21"]]]
c = b.last
  #=> [["1", "true", "21"], ["2", "true", "21"]]
d = c.map(&:first) # ~same as c.map { |a| a.first }
  #=> ["1", "2"] 
e = e.sum(&:to_i)  # ~same as e.sum { |s| s.to_i }
  #=> 3
e.to_s
  #=> "3"

我们看到值a已转换为"3"。用于计算q的其余计算是相似的。

文档链接

您可以在以下类ArraydroptransposelastfirstsumIntegerto_sStringto_i)和Enumeratorwith_objectnext )和模块Enumerablegroup_bymapeach_with_index)。

分解嵌套对象

我想再提一个棘手的问题。那是那一行

enum1.each { |((a,v),i),g| g[i] = [v,*a] }

我已经编写了块变量,以分解由枚举器enum1生成并传递到块的值。我敢肯定,对于新手来说,它看起来一定很气派,但如果您逐步进行,那还不错,正如我将要解释的那样。

首先,假设我有一个块变量renum1.each { |r|...})。生成第一个值并将其传递给块,为r分配一个值:

r = enum1.next
  #=> [[[["true", "21"], "3"], 0], []]

然后我们可以在块中执行以下语句以分解(消除歧义r,如下所示:

((a,v),i),g = r
  #=> [[[["true", "21"], "3"], 0], []]

产生以下任务:

a #=> ["true", "21"]
v #=> "3"
i #=> 0
g #=> []

|r|替换块中的|((a,v),i),g|是等效的,而且更简单。

如果您研究由enum1.next产生的嵌套数组中括号的位置,您将看到在编写块变量时如何确定需要括号的位置。嵌套数组和其他对象的这种分解是一种非常方便且功能强大的功能,即Ruby,这是一个未被充分利用的功能。

答案 2 :(得分:1)

只是出于好奇。

input.
  values.
  map { |i, *rest| [rest, i.to_i] }.
  group_by(&:shift).
  map do |*key, values|
    [values.flatten.sum.to_s, *key.flatten]
  end

答案 3 :(得分:1)

引用:Enumerable#group_byEnumerator#with_indexArray#to_h

key_sum = ->(group) { group.sum { |key, _| key.to_i }.to_s }
given_hash.values.group_by { |_, *rest| rest }. 
  map.with_index { |(key, group), idx| [idx, [key_sum.call(group), *key]] }.to_h
#=> {0=>["3", "true", "21"], 1=>["10", "false", "21"], 2=>["15", "true", "22"]}
  • 分组

    given_hash.values.group_by { |_, *rest| rest }
    #=> { ["true", "21"] => [["1", "true", "21"], ["2", "true", "21"]]...
    
  • key_sum函数

    key_sum = ->(group) { group.sum { |key, _| key.to_i }.to_s }
    key_sum.call([["1", "true", "21"], ["2", "true", "21"]]) #=> '3'
    
  • to_h

    [[0, ["3", "true", "21"]], [1, ["10", "false", "21"]], [2, ["15", "true", "22"]]].to_h
    #=> {0=>["3", "true", "21"], 1=>["10", "false", "21"], 2=>["15", "true", "22"]}
    

答案 4 :(得分:0)

尽管要解决很长的路要走,但这仍然可以回答您的问题。我敢肯定,有一些捷径可以更轻松地解决它,但是您要求对发生的事情进行清楚的解释,我希望这可以指导您解决问题。

# Start with a hash
hash = {
  "0" => ["1", "true", "21"],
  "1" => ["2", "true", "21"],
  "2" => ["3", "false", "21"],
  "3" => ["4", "true", "22"],
  "4" => ["5", "true", "22"],
  "5" => ["6", "true", "22"],
  "6" => ["7", "false", "21"]
}

# Extract just the values from the hash into an array
values = hash.values

added_values = values.map do |array|
  # Find all arrays that match this array's values at indices [1] and [2]
  matching = values.select { |a| a[1] == array[1] && a[2] == array[2] }
  sum = 0
  # Add the values at index 0 from each matching array
  matching.each { |match| sum += match[0].to_i }
  # Return a new array with these values
  [sum.to_s, array[1], array[2]]
end

# Reject any duplicative arrays
added_values.uniq!

# Convert the array back to a hash
added_values.each_with_index.each_with_object({}) { |(array, index), hash| hash[index] = array }

答案 5 :(得分:0)

哈希数组在ruby中具有最强大的内置功能。<​​/ p>

z = h.group_by { |k,v| v[1..2] }.keep_if { |k,v| v.length > 1 }
val = z.map { |k,v| [v.map { |x| x[1] }.map(&:first).map(&:to_i).inject(:+).to_s, k[0], k[1]] }
val.each_with_index.inject({}) { |m,(x,i)| m[i] = x; m }

=> {0 =>["3", "true", "21"], 1 =>["10", "false", "21"], 2 =>["15", "true", "22"]}

如果您知道这些功能,那么就不需要复杂的实现。 学习愉快:)

答案 6 :(得分:0)

h
.values
.group_by{|_, *a| a}
.map
.with_index{|(k, a), i| [i, [a.inject(0){|acc, (n, *)| acc + n.to_i}.to_s, *k]]}
.to_h
# => {0=>["3", "true", "21"], 1=>["10", "false", "21"], 2=>["15", "true", "22"]}