h = { "a" => 1, "b" => 2 }
有没有办法减少哈希并将键,值和索引作为块参数?
作为一个起点,我可以迭代哈希获取键,值和索引:
h.each_with_index { |(k,v), i| puts [k,v,i].inspect }
# => ["a", 1, 0]
# => ["b", 2, 1]
然而,当我添加reduce
时,我似乎失去了将键和值作为单独值的能力,而是将它们作为双元素数组提供:
h.each_with_index.reduce([]) { |memo, (kv,i)| puts [kv,i].inspect }
# => [["a", 1], 0]
# => [["b", 2], 1]
这没关系,我可以在块中kv[0]
和kv[1]
,但我喜欢这样的事情:
h.each_with_index.reduce([]) { |memo, (k,v), i| puts [k,v,i].inspect }
我想在没有猴子修补的情况下这样做。
答案 0 :(得分:3)
也许是这样的?:
h.each_with_index.reduce([]) { |memo, ((k,v), i)| puts [k,v,i].inspect }
#=> ["a", 1, 0]
#=> ["b", 2, 1]
#=> nil
您只需要确定范围:((k,v), i)
。
请记住reduce
,我们总是必须在块结束时返回对象。这是一种额外的开销,除非最后一个操作不在返回对象本身的memo
对象上。否则它不会返回所需的结果。
使用each_with_index
链接with_object
就可以实现同样的目的:
h.each_with_index.with_object([]) { |((k,v), i), memo| memo << [k,v,i].inspect }
#=> ["a", 1, 0]
#=> ["b", 2, 1]
#=> []
在输出的最后一行看到数组?这是我们的memo
对象,与我们上面使用的reduce
相同。
答案 1 :(得分:3)
Enumerable#each_with_index
在块中产生两个值:项及其索引。当为Hash
调用它时,该项是一个包含两个元素的数组:键和关联值。
当您声明块参数|(k,v), i|
时,事实上,您将第一个块参数(项)解构为其两个组件:键和值。没有块h.each_with_index
产生Enumerator
,它产生先前使用的块包含在数组中的两个参数。
此数组是Enumerator#reduce
的第二个参数。
你可以通过运行来判断:
irb> h.each_with_index.reduce([]) { |memo, j| p j }
[["a", 1], 0]
[["b", 2], 1]
现在,您的问题的答案很简单:只需解构j
即可获得:
irb> h.each_with_index.reduce([]) { |memo, ((k,v), i)| puts [k,v,i].inspect }
["a", 1, 0]
["b", 2, 1]
当然,您应memo << [k,v,i]
或使用其他其他规则将值放入memo
并返回memo
以获得最终所需结果。
答案 2 :(得分:3)
如果对块参数有什么疑问,请创建一个Enumerator
的实例并在其上调用#next
:
▶ h = {a: 1, b: 2}
#⇒ {:a=>1, :b=>2}
▶ enum = h.each.with_index.with_object([])
#⇒ #<Enumerator: ...>
▶ enum.next
#⇒ [[[:a, 1], 0], []]
返回的值包括:
key
和value
的数组,加入:index
,加入:reduce
它会在前面,如果reduce
在没有阻止积分的情况下调用枚举器给@Stefan进行挑剔。)因此,分解它的正确括号是:
# ⇓ ⇓ ⇓ ⇓
# [ [ [:a, 1], 0 ], [] ]
{ | ( (k, v), idx ), memo| ...