使用键,值和索引减少散列作为块参数

时间:2018-04-05 11:34:43

标签: ruby

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 }

我想在没有猴子修补的情况下这样做。

3 个答案:

答案 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], []]

返回的值包括:

  • keyvalue的数组,加入:
  • 数组index,加入:
  • 带有累加器的数组(对于reduce它会在前面,如果reduce在没有阻止积分的情况下调用枚举器给@Stefan进行挑剔。)

因此,分解它的正确括号是:

#   ⇓ ⇓     ⇓       ⇓
# [ [ [:a, 1],    0 ],    [] ]
{ | ( (k,  v),  idx ),   memo| ...
相关问题