【问题标题】:Reduce hash with key, value and index as block parameters以键、值和索引作为块参数减少哈希
【发布时间】:2018-09-15 05:12:34
【问题描述】:
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 }

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

【问题讨论】:

  • 您很幸运(到目前为止)收到了三个非常好的答案。我建议您研究@mudasobwa 的答案,以更深入地了解正在发生的事情。

标签: ruby


【解决方案1】:

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】:

    如果对块参数有疑问,请创建 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| ...
    

    【讨论】:

    • 很遗憾,reduce 没有返回枚举数。
    • @Stefan 另一个不将reduce 与可变累加器一起使用的原因:)
    • 很公平 ;-)
    • 使用next的好提示。
    • 很好的答案,尤其是对枚举数的关注。考虑编写( (k, v), idx ), memo = enum.next 并简单地显示分配给四个块变量的值。
    【解决方案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_indexwith_object 链接来实现,如下所示:

    h.each_with_index.with_object([]) { |((k,v), i), memo| memo << [k,v,i].inspect }
    #=> ["a", 1, 0]
    #=> ["b", 2, 1]
    #=> []
    

    看到最后一行输出的数组了吗?那是我们的memo 对象,它与我们上面使用的reduce 不同。

    【讨论】:

      猜你喜欢
      • 2012-12-02
      • 2013-06-27
      • 2019-03-17
      • 2018-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-30
      • 1970-01-01
      相关资源
      最近更新 更多