【问题标题】:Pushing Hash onto Array: last Hash overwriting previous array elements将哈希压入数组:最后一个哈希覆盖先前的数组元素
【发布时间】:2016-07-16 08:17:24
【问题描述】:

我有以下 Ruby 脚本:

arr = ['bob', 'jack', 'smith']
array_of_hashes = Array.new
hash = Hash.new

arr.each do |item|
  hash.clear
  hash[:name] = item
  array_of_hashes << hash
end

puts array_of_hashes

这会返回一个哈希数组,其 :name 键都来自最后一个元素。

[
    [0] {
        :name => "smith"
    },
    [1] {
        :name => "smith"
    },
    [2] {
        :name => "smith"
    }
]

我希望它返回以下内容,但我一直试图弄清楚为什么最后一个 Hash 元素会覆盖所有先前的数组元素:

[
    [0] {
        :name => "bob"
    },
    [1] {
        :name => "jack"
    },
    [2] {
        :name => "smith"
    }
]

编辑:谢谢大家的回答。很高兴有一些不同的方法来完成同样的事情。我已经测试了每一种解决方案,每一种都很棒。我最终使用了与我的原始代码最相似的那个;但我的用例是一个简单的本地脚本——它没有用于工业规模的应用程序——在这种情况下,我可能会选择不同的解决方案。

【问题讨论】:

    标签: arrays ruby loops hash


    【解决方案1】:

    仔细查看您的代码,您会发现您只创建了一个 Hash 实例。您的迭代器一直在用.clear 吹走早期的努力,将元素设置为当前迭代,并将对该Hash 对象的引用添加到array_of_hashes 中的不同数组位置。但归根结底,它们都指向同一个 Hash 对象,而 Hash 对象只包含您最后放入的内容。

    【讨论】:

      【解决方案2】:

      注意(并理解)@pjs 的回答和@tadman 的评论,因为他们解释了为什么你会得到你得到的结果。

      我实际上会做类似的事情:

      names = ['bob', 'jack', 'smith']
      
      # combine the results into an array of Hashes
      array_of_hashes =
        names.collect do |item|
          { name: item }
        end
      
      # output the results
      array_of_hashes # [{name: "bob"}, {name: "jack"},{name:"smith"}]
      

      一些注意事项:

      • Array#collect 方法将返回一个数组,因此您无需手动添加到已初始化的数组中。
      • 您可以为每个将在 collect 块中返回的项目返回一个新的哈希

      【讨论】:

      • 这里的关键是array &lt;&lt; hashhash对象引用 推送到数组而不是副本中。
      • 这似乎是最“红宝石”的方式。
      • 在没有“puts”的情况下测试代码并查看...它显示没有结果,抱歉。这就是我编辑你的答案的原因。试一试,然后自己编辑。
      • @Ed,我已经在 irb 和 pry 中对其进行了测试。代码工作正常。如果您将整个内容包裹在 def 中,它也可以工作。
      • 是的,在 ruby​​ 文件中运行代码,您需要执行puts 之类的操作才能将值输出给用户。像puts array_of_hashes.inspect 这样的东西会更接近我的意图,即显示array_of_hashes 的实际值。
      【解决方案3】:

      你应该尝试类似的东西

      arr = ['bob', 'jack', 'smith']
      array_of_hashes = Array.new
      
      arr.each { |item|
        hash = Hash.new
        hash[:name] = item
        array_of_hashes << hash
      }
      
      puts array_of_hashes
      

      每个新项目都会将一个新实例推入数组。

      【讨论】:

      • 感谢您的帖子 Ed;我真的很喜欢这段代码清楚地说明了我原始代码中的缺陷——也就是说,我只有一个哈希值,而这段代码为每个数组项创建了一个新的哈希值。
      • 我很高兴它有帮助,@singularity。感谢您的支持。
      • 我现在接受了这个答案,因为这是我最终要做的。这是从我的原始代码到工作脚本的最快方式。本质上,Hash.new 必须在 arr.each 块内。 .clear 也不需要。虽然它可能不是最优雅的解决方案,但它对我的场景来说是最实用的,并且可以完成工作。
      【解决方案4】:

      我会写

      [:name].product(arr).map { |pair| Hash[[pair]] }
        #=> [{:name=>"bob"}, {:name=>"jack"}, {:name=>"smith"}]
      

      或(对于 Ruby v2.0+)

      [:name].product(arr).map { |pair| [pair].to_h }
      

      步骤:

      a = [:name].product(arr)
        #=> [[:name, "bob"], [:name, "jack"], [:name, "smith"]]   
      enum = a.map
        # => #<Enumerator: [[:name, "bob"], [:name, "jack"], [:name, "smith"]]:map> 
      

      请注意,通过将方法Enumerator#each 发送到enum,我们可以获得所需的结果:

      enum.each { |pair| [pair].to_h }
        #=> [{:name=>"bob"}, {:name=>"jack"}, {:name=>"smith"}]
      

      Enumerator#each 将调用Array#each,因为a.class #=&gt; Array。)

      Enumerator#each 将块变量设置为enum 的第一个元素:

      pair = enum.next
        #=> [:name, "bob"]
      

      并进行块计算:

      [pair].to_h
        #=> [[:name, "bob"]].to_h
        #=> {:name=>"bob"}
      

      接下来,enum 的第二个元素 [:name, "jack"] 被映射到:

      pair = enum.next
        #=> [:name, "jack"] 
      [pair].to_h
        #=> [[:name, "jack"]].to_h
        #=> {:name=>"jack"}
      

      最后,

      pair = enum.next
        #=> [:name, "smith"] 
      [pair].to_h
        #=> [[:name, "smith"]].to_h
        #=> {:name=>"smith"} 
      

      【讨论】:

      • 这看起来很优雅。这有点过头了,但是对于 n00bs(包括我自己),要查看输出,您可以 puts this 或将其分配给 var - hash = [:name].product(arr).map { |pair| [pair].to_h }
      • 我会在早上详细说明。它没有最初看起来那么复杂。
      • 哇,感谢您的详细说明!这有助于揭开这些步骤的神秘面纱
      【解决方案5】:

      你也可以试试

      >>> names = ['bob', 'jack', 'smith']
      >>> names.inject([]){|s,p| s << {name: p}}
      => [{:name=>"bob"}, {:name=>"jack"}, {:name=>"smith"}]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-30
        • 1970-01-01
        • 2014-04-18
        • 2012-10-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多