2016-03-28 14 views
3

私は、次の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" 
    } 
] 

私はそれが次のことを返すことを期待するだろうが、私は最後のハッシュ要素は、以前のすべての配列要素を上書きしている理由を理解しようとこだわっている:

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

編集:ありがとうございましたあなたの答え。同じことを達成するためにいくつかの異なる方法を用意するのはいいことです。私はそれぞれのソリューションをテストしましたが、それぞれが素晴らしいです。私は元のコードに最も似ていたものを使用して終了しました。私の使用例はシンプルでローカルなスクリプトです - 産業規模のアプリケーションでは使用されていません - その場合、おそらく別のソリューションを選択します。

答えて

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 

のようなものを試してみてください。

+0

おかげでこれを試すことができます。私はこのコードが元のコードの欠陥を明確に示しているのが本当に好きです。つまり、私は1つのハッシュしか扱っておらず、このコードは各配列アイテムに新しいハッシュを作成します。 – singularity

+0

私はそれが助けてくれることを嬉しく思っています。 upvoteありがとう。 –

+0

私はこの回答を受け入れました。なぜなら、私がやったことです。私の元のコードから作業スクリプトまでの最速の方法でした。基本的に、 'Hash.new'は' arr.each'ブロック内になければなりませんでした。 '.clear'も必要ではありません。最も洗練されたソリューションではないかもしれませんが、それは私のシナリオにとって最も実用的であり、仕事を完了させます。 – singularity

5

コードを注意深く見てみると、Hashという1つのインスタンスしか作成されていないことがわかります。あなたのイテレータは、以前の努力を吹き飛ばして、.clearで要素を現在の反復に設定し、そのHashオブジェクトへの参照をarray_of_hashesの異なる配列位置に追加します。しかし、今日の終わりには、すべてが同じHashオブジェクトを指しており、Hashオブジェクトには、最後に入れたものだけが含まれています。

4

なぜあなたが得ている結果を得ているかを説明する@padと@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方法は、配列を返しますあなた自身を初期化しました。
  • あなたはちょうど私がRubyのv2の

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

    か(を書くでしょう

+2

ここで重要なのは、 'array << hash'はオブジェクト参照*をコピーにではなく配列に' hash'にプッシュすることです。 – tadman

+0

これはこれについて最も「ルビー的な」方法のように思えます。 – singularity

+0

"puts"を除いたコードをテストしてみてください...結果は表示されません、ごめんなさい。だから私はあなたの答えを編集した。試してみてください。 –

2

collectブロックに返される項目ごとに新しいハッシュを返すことができます。0+)

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

手順:方法にEnumerator#eachenumとを送信することによって、我々は所望の結果を得ること

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

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

Enumerator#eachArray#eacha.class #=> Arrayのでを呼び出します。 )

Enumerator#eachenumの最初の要素をブロック変数を設定:

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

とブロック演算が実行される:

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

次に、enumの第2の要素、[: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"} 
+0

これは優雅に思えます。私の頭の中ではちょっとですが、n00bs(自分自身を含む)の場合、出力を見るには、これを 'put 'するか、var - ' hash = [:name] .product(arr).map {|ペア| [ペア] .to_h} ' – singularity

+0

私は朝に詳述します。それは当初よりも複雑ではありません。 –

+0

うわー、精巧さのおかげで!それはかなりのステップをdemystifyするのに役立ちます – singularity

0

あなたもあなたのポストのエドのため

>>> names = ['bob', 'jack', 'smith'] 
>>> names.inject([]){|s,p| s << {name: p}} 
=> [{:name=>"bob"}, {:name=>"jack"}, {:name=>"smith"}] 
関連する問題