将Hash推送到Array:last Hash覆盖以前的数组元素

时间:2016-03-28 23:44:31

标签: arrays ruby loops hash

我有以下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

返回一个哈希数组,其名称:名称键全部来自最后一个元素。

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

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

[
    [0] {
        :name => "bob"
    },
    [1] {
        :name => "jack"
    },
    [2] {
        :name => "smith"
    }
]
编辑:谢谢大家的回答。很高兴有一些不同的方法来完成同样的事情。我测试了每个解决方案,每个解决方案都很棒。我最终使用的是与原始代码最相似的那个;但我的用例是一个简单的本地脚本 - 它不是在工业大小的应用程序中使用 - 在这种情况下,我可能会选择不同的解决方案。

5 个答案:

答案 0 :(得分:5)

仔细查看您的代码,您会发现您只创建了Hash的单个实例。你的迭代器继续用.clear吹走早期的工作,将元素设置为当前迭代,并将对{1}}对象的引用添加到Hash中的不同数组位置。但是在一天结束时,它们都指向同一个array_of_hashes对象,而Hash对象只包含你放入它的最后一个东西。

答案 1 :(得分:4)

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

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

const openni::SensorInfo* sinfo = device.getSensorInfo(openni::SENSOR_DEPTH);
const openni::Array< openni::VideoMode>& modesDepth = sinfo->getSupportedVideoModes();
depth.stop();
rc = depth.setVideoMode(modesDepth[4]);
depth.start();

一些注意事项:

  • 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区块中返回的每个项目返回一个新的哈希

答案 2 :(得分: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

将新实例推送到数组中以用于每个新项目。

答案 3 :(得分:2)

我会写

[: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 #=> 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"} 

答案 4 :(得分:0)

你也可以尝试这个

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