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

Ruby 哈希中具有多个值的重复值之和,ruby,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"] }

给定如下所示的哈希:

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"]
}
{
  0 => ["3", "true", "21"],
  1 => ["10", "false", "21"],
  2 => ["15", "true", "22"]
}
我希望在索引1和索引2处具有相同元素的数组中找到位置0处的元素之和,并返回如下所示的散列:

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"]
}
{
  0 => ["3", "true", "21"],
  1 => ["10", "false", "21"],
  2 => ["15", "true", "22"]
}
因为有两个数组的索引1和2的值分别为true和21,所以我想对这两个数组的索引0的整数值求和


如何将问题顶部的示例散列转换为下面的结果散列?

我不是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}"

我不是一个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}"

这应该能回答你的问题,尽管要解决这个问题还有很长的路要走。我相信有捷径可以更容易地解决这个问题,但你要求对发生的事情进行清楚的解释,我希望这能指导你如何解决这个问题

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

这应该能回答你的问题,尽管要解决这个问题还有很长的路要走。我相信有捷径可以更容易地解决这个问题,但你要求对发生的事情进行清楚的解释,我希望这能指导你如何解决这个问题

# 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 }
代码

范例

解释

主要步骤

对于上面的散列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"]}
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"
如果将enum0的返回值与enum1的返回值进行比较,可以将后者视为复合枚举数,尽管Ruby不使用该术语

Hashtransform\u值的详细信息

现在让我们更仔细地看一下q的计算。p的第一个值被传递给在MRI 2.4中首次出现的块,并成为块变量a的值:

区块计算如下所示

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"]}
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的其余计算类似

文档链接

您可以在以下链接中找到我使用的方法的文档,这些方法用于类drop、transpose、last、first和sum、to_s、to_I和with_object和next,以及模块group_by、map和每个带有_索引的_

嵌套对象的分解

还有一点我想提一下。这就是路线

enum1.each { |((a,v),i),g| g[i] = [v,*a] }
我编写块变量的方式是分解枚举器enum1生成并传递给块的值。我敢肯定,对于一个新手来说,这看起来一定很有气势,但我会解释,如果你一步一步地去做,也没那么糟糕

首先,假设我有一个块变量r enum1.each{| r |…}。生成第一个值并将其传递给块,为r指定一个值:

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

((a,v),i),g = r
  #=> [[[["true", "21"], "3"], 0], []]
制作以下作业:

a #=> ["true", "21"]
v #=> "3"
i #=> 0
g #=> []
用| a,v,i,g |替换块中的| r |是等效的,而且更简单

如果研究enum1.next生成的嵌套数组中括号的位置,您将看到我在写入块变量时如何确定需要括号的位置。这种对嵌套数组和其他对象的分解是Ruby的一个非常方便和强大的功能,而Ruby的这一功能却没有得到充分利用。

代码

范例

解释

主要步骤

对于上面的散列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"]}
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"
如果将enum0的返回值与enum1的返回值进行比较,可以将后者视为复合枚举数,尽管Ruby不使用该术语

Hashtransform\u值的详细信息

现在让我们更仔细地看一下q的计算。p的第一个值被传递给在MRI 2.4中首次出现的块,并成为块变量a的值:

区块计算如下所示

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"]}
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的其余计算类似

文档链接

您可以在以下链接中找到我使用的方法的文档,这些方法用于类drop、transpose、last、first和sum、to_s、to_I和with_object和next,以及模块group_by、map和每个带有_索引的_

嵌套对象的分解

还有一点我想提一下。这就是路线

enum1.each { |((a,v),i),g| g[i] = [v,*a] }
我编写块变量的方式是分解枚举器enum1生成并传递给块的值。我敢肯定,对于一个新手来说,这看起来一定很有气势,但我会解释,如果你一步一步地去做,也没那么糟糕

首先,假设我有一个块变量r enum1.each{| r |…}。生成第一个值并将其传递给块,为r指定一个值:

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

((a,v),i),g = r
  #=> [[[["true", "21"], "3"], 0], []]
制作以下作业:

a #=> ["true", "21"]
v #=> "3"
i #=> 0
g #=> []
用| a,v,i,g |替换块中的| r |是等效的,而且更简单

如果研究enum1.next生成的嵌套数组中括号的位置,您将看到我在写入块变量时如何确定需要括号的位置。这种对嵌套数组和其他对象的分解是Ruby的一个非常方便和强大的功能,但这一功能还没有得到充分利用。

只是出于好奇

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

input.
  values.
  map { |i, *rest| [rest, i.to_i] }.
  group_by(&:shift).
  map do |*key, values|
    [values.flatten.sum.to_s, *key.flatten]
  end
哈希和数组在ruby中具有最强大的内置函数

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"]}
如果您知道这些函数,那么就永远不需要复杂的实现。愉快学习:

哈希和数组在ruby中具有最强大的内置函数

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"]}
如果您知道这些函数,那么就永远不需要复杂的实现。快乐学习:

参考文献:

分组

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

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

参考文献:

分组

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

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



我已经读了很多遍了,我真的很难理解你的例子和你的目标是什么。我也看不到你做了什么试图自己解决这个问题。我建议你后退一步,重新思考这个问题,并尽可能清楚地解释它,牢记在心。@Anotherh我很感激你的反馈,我努力想弄清楚这一点。我过去读过《如何提出一个好问题》。除了分解键和值之外,我的尝试没有取得任何进展。。为了更有用[2,b,c][4,b,c][1,c,d],我想把第一个和第二个数组加在一起,得到[6,b,c][1,c,d],根据我对它的理解,我已经尽了最大努力来澄清你的问题,并在下面留下了答案。如果我的编辑与您的实际问题不一致,请随时回复。当您给出示例时,为所有输入值指定一个变量会很有帮助。这里可能是h={0=>…}。这样,读者就可以在评论和答案中引用这些变量,而无需对它们进行定义。@CarySwoveland在你为我编辑这篇文章之前发表评论的人,因为我之前试图解释的方式是无组织的。虽然我已经读了很多遍了,但我确实很难理解你的例子和你的目标是什么。我也看不到你做了什么试图自己解决这个问题。我建议你后退一步,重新思考这个问题,并尽可能清楚地解释它,牢记在心。@Anotherh我很感激你的反馈,我努力想弄清楚这一点。我过去读过《如何提出一个好问题》。除了分解键和值之外,我的尝试没有取得任何进展。。为了更有用[2,b,c][4,b,c][1,c,d],我想把第一个和第二个数组加在一起,得到[6,b,c][1,c,d],根据我对它的理解,我已经尽了最大努力来澄清你的问题,并在下面留下了答案。如果我的编辑与您的实际问题不一致,请随时回复。当您给出示例时,为所有输入值指定一个变量会很有帮助。这里可能是h={0=>…}。这样,读者就可以在评论和答案中引用这些变量,而无需对它们进行定义。@CarySwoveland在你为我编辑这篇文章之前发表评论的人,因为我之前试图解释的方式是无组织的。我确实有价值,但是非常感谢你的回复!希望这对你有意义。我感谢你的答复。我现在正在测试它!很乐意帮忙。请测试编辑的版本,因为我以前没有将字符串转换为int。非常感谢您的回复!希望这对你有意义。我感谢你的答复。我现在正在测试它!很乐意帮忙。请测试编辑的版本,因为我以前没有将字符串转换为int。非常感谢您这么做。我正试图最终能够自己思考这些东西!感谢您花时间回答我的问题。我想您可以通过尝试了解我的代码您能理解多少,从而了解Ruby和Ruby way。我已经为计算的每个步骤提供了相当多的细节,以及我使用的方法的文档链接。你不可能理解所有的事情,但我认为你甚至可以通过潜移默化从那些仍然神秘的部分中学到一些东西。请注意,我写这篇文章不仅是为了你,也是为了那些比你早几周或几个月就想精通这门语言的人。感谢你提供的详细信息,我将一行一行地尝试将其分解。我很感谢你抽出时间,非常感谢你这么做。我正试图最终能够自己思考这些东西!感谢您花时间回答我的问题。我想您可以通过尝试了解我的代码中有多少是您可以下载的,从而了解Ruby和Ruby方式
倒立。我已经为计算的每个步骤提供了相当多的细节,以及我使用的方法的文档链接。你不可能理解所有的事情,但我认为你甚至可以通过潜移默化从那些仍然神秘的部分中学到一些东西。请注意,我写这篇文章不仅是为了你,也是为了那些比你早几周或几个月就想精通这门语言的人。感谢你提供的详细信息,我将一行一行地尝试将其分解。感谢您抽出时间来做这件事并回答这个问题。了解回答这些问题的不同方法真的很有帮助谢谢你花时间做这件事并回答这个问题。这确实有助于了解回答这些问题的不同方法