给出如下所示的哈希值:
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
和1
上具有相同元素的数组中位置2
的元素总和,并返回如下所示的哈希值:
{
0 => ["3", "true", "21"],
1 => ["10", "false", "21"],
2 => ["15", "true", "22"]
}
由于存在两个索引为1
和2
且值分别为"true"
和"21"
的数组,我想将索引0
的整数值求和例如,这两个数组。
如何将此问题顶部的示例哈希转换为其下方的结果哈希?
答案 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
的其余计算是相似的。
文档链接
您可以在以下类Array(drop
,transpose
,last
,first
和sum
,Integer(to_s
,String(to_i
)和Enumerator(with_object
和next
)和模块Enumerable(group_by
,map
和each_with_index
)。
分解嵌套对象
我想再提一个棘手的问题。那是那一行
enum1.each { |((a,v),i),g| g[i] = [v,*a] }
我已经编写了块变量,以分解由枚举器enum1
生成并传递到块的值。我敢肯定,对于新手来说,它看起来一定很气派,但如果您逐步进行,那还不错,正如我将要解释的那样。
首先,假设我有一个块变量r
(enum1.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_by,Enumerator#with_index,Array#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"]}