Rubyで配列を反復処理する「正しい」方法は何ですか?


341

PHPは、そのすべてのいぼに関して、この点でかなり良いです。配列とハッシュの間に違いはありません(多分私は世間知らずですが、これは明らかに私には正しいようです)、どちらかを繰り返すだけです

foreach (array/hash as $key => $value)

Rubyには、このようなことを行う方法がたくさんあります。

array.length.times do |i|
end

array.each

array.each_index

for i in array

ハッシュはもっと理にかなっています。

hash.each do |key, value|

なぜこれを配列に対して実行できないのですか?私はただ一つの方法を覚えておきたい場合は、私が使用できると思いますeach_index(それが指標と使用可能な値の両方を作るので)、それはしなければならないために迷惑なんだarray[index]ばかりの代わりにvalue


ああ、忘れましたarray.each_with_index。しかし、これは行っ|value, key|たり来たりするので吸いhash.eachます|key, value|!これは正気ではないですか?


1
メソッド名は順序を暗示array#each_with_indexする|value, key|ので、私は使用を推測しますがhash#eachhash[key] = value構文を模倣するために使用される順序は?
Benjineer、2014年

3
Rubyでループを始めたばかりの場合は、select、reject、collect、inject、detectを使用
Matthew Carriere

回答:


560

これはすべての要素を反復します:

array = [1, 2, 3, 4, 5, 6]
array.each { |x| puts x }

プリント:

1
2
3
4
5
6

これは、すべての要素を反復処理して、値とインデックスを提供します。

array = ["A", "B", "C"]
array.each_with_index {|val, index| puts "#{val} => #{index}" }

プリント:

A => 0
B => 1
C => 2

あなたの質問からあなたがどちらを探しているのかはよくわかりません。


1
オフトピック配列のruby 1.9でeach_with_indexが見つかりません
CantGetANick 2012

19
@CantGetANick:Arrayクラスには、このメソッドを持つEnumerableモジュールが含まれています。
xuinkrbin。

88

私には正しい方法はないと思います。反復する方法はたくさんあり、それぞれに独自のニッチがあります。

  • each 多くの場合、インデックスについてはあまり気にしないので十分です。
  • each_ with _index Hash#eachのように動作します-値とインデックスを取得します。
  • each_index-インデックスのみ。これはあまり使いません。「length.times」と同じです。
  • map 反復する別の方法であり、1つの配列を別の配列に変換する場合に便利です。
  • select サブセットを選択するときに使用するイテレータです。
  • inject 合計や積の生成、または単一の結果の収集に役立ちます。

覚えるのは大変に思えるかもしれませんが、すべてを知らなくても問題ありません。しかし、さまざまな方法を学び、使用し始めると、コードはよりクリーンで明確になり、Rubyの習得に進むことになります。


すばらしい答えです!#rejectのような逆と#collectのようなエイリアスについて言及したいと思います。
Emily Tui Ch

60

Array-> |value,index|Hash-> |key,value|が正気ではないと言っているわけではありませんが(Horace Loebのコメントを参照)、このアレンジメントを期待する正気な方法があると言っています。

配列を扱うときは、配列の要素に注目します(インデックスは一時的なため、インデックスではありません)。メソッドはそれぞれインデックス付きです。つまり、each + index、または| each、index |、または|value,index|です。これは、オプションの引数として表示されるインデックスと一致します(例:|値|)。| value、index = nil |と同等です | value、index |と一致します。

私はハッシュを取り扱っております場合は、私は多くの場合、複数の値よりもキーに焦点を当てた、と私は通常、そのために、キーと値を扱っています、のいずれかkey => valueまたはhash[key] = value

ダックタイピングが必要な場合は、Brent Longboroughが示すように定義されたメソッドを明示的に使用するか、maxhawkinsが示すように暗黙的なメソッドを使用します。

Rubyはすべて、プログラマーに合うように言語に対応することであり、言語に合うように対応するプログラマーではありません。これが、非常に多くの方法がある理由です。何かについて考える方法はたくさんあります。Rubyでは、最も近いものを選択し、残りのコードは通常、非常にきちんと簡潔に抜け落ちます。

元の質問「Rubyで配列を反復処理するための「正しい」方法は何ですか?」については、コアとなる方法(つまり、強力な構文糖やオブジェクト指向の力がない場合)は次のようにすることだと思います。

for index in 0 ... array.size
  puts "array[#{index}] = #{array[index].inspect}"
end

しかし、Rubyはすべて、強力な構文上の砂糖とオブジェクト指向の力についてですが、いずれにしても、ここではハッシュと同等であり、キーを順序付けすることもしないこともできます。

for key in hash.keys.sort
  puts "hash[#{key.inspect}] = #{hash[key].inspect}"
end

したがって、私の答えは、「Rubyで配列を反復処理する「正しい」方法は、ユーザー(つまり、プログラマーまたはプログラミングチーム)とプロジェクトによって異なります」です。より優れたRubyプログラマーは、より良い選択を行います(その構文的能力および/またはオブジェクト指向アプローチのいずれか)。より優れたRubyプログラマーは、より多くの方法を探し続けています。


ここで、「RubyでRangeを逆方向に反復する「正しい」方法は何ですか?」という別の質問をしたいと思います。(この質問は、私がこのページにアクセスした方法です。)

それは(フォワードのために)行うのがいいです:

(1..10).each{|i| puts "i=#{i}" }

しかし、私は(逆向きに)やりたくありません:

(1..10).to_a.reverse.each{|i| puts "i=#{i}" }

ええと、私は実際にそれをやりすぎてもかまいませんが、私は逆方向に教えるときに、生徒に良い対称性を示したいと思います(つまり、最小限の違いで、たとえば、リバースを追加するか、ステップ-1を追加しますが、その他の変更)。あなたは(対称性のために)することができます:

(a=*1..10).each{|i| puts "i=#{i}" }

そして

(a=*1..10).reverse.each{|i| puts "i=#{i}" }

私はあまり好きではありませんが、あなたはできません

(*1..10).each{|i| puts "i=#{i}" }
(*1..10).reverse.each{|i| puts "i=#{i}" }
#
(1..10).step(1){|i| puts "i=#{i}" }
(1..10).step(-1){|i| puts "i=#{i}" }
#
(1..10).each{|i| puts "i=#{i}" }
(10..1).each{|i| puts "i=#{i}" }   # I don't want this though.  It's dangerous

最終的には

class Range

  def each_reverse(&block)
    self.to_a.reverse.each(&block)
  end

end

オブジェクト指向のアプローチではなく、純粋なRubyを教えたいと思っています(まだ)。私は逆方向に反復したいと思います:

  • 配列を作成せず(0..1000000000を検討)
  • 任意の範囲で機能する(例:整数だけでなく文字列)
  • 追加のオブジェクト指向パワーを使用せずに(つまり、クラスの変更なし)

これは、predメソッドを定義しないと不可能だと思います。つまり、Rangeクラスを変更して、それを使用します。これができたら教えてください。そうでなければ、残念ですが不可能の確認をお願いします。おそらくRuby 1.9がこれに対処します。

(お読みいただきありがとうございます。)


5
1.upto(10) do |i| puts i endそして10.downto(1) do puts i end、あなたが望む対称性を与えます。お役に立てば幸いです。文字列などで機能するかどうかはわかりません。
2012年

(1..10).to_a.sort {| x、y | y <=> x} .each {| i | puts "i =#{i}"}-逆のほうが遅いです。
Davidslv

でき[*1..10].each{|i| puts "i=#{i}" }ます。
Hauleth、2015

19

両方が必要な場合は、each_with_indexを使用してください。

ary.each_with_index { |val, idx| # ...

12

他の答えは問題ありませんが、もう1つの周辺機能を指摘したいと思います。配列は順序付けされていますが、ハッシュは1.8ではありません。(Ruby 1.9では、ハッシュはキーの挿入順で並べられます。)そのため、1.9より前のバージョンでは、常に明確な順序を持っていた配列と同じ方法/シーケンスでハッシュを反復処理することは意味がありません。PHPの連想配列のデフォルトの順序はわかりません(どうやら私のgoogle fuはそれを理解するのに十分なほど強力ではありません)。しかし、通常のPHP配列とPHPの連想配列をどのように検討できるのかはわかりません。連想配列の順序は定義されていないように見えるため、このコンテキストでは「同じ」にします。

そのため、Rubyの方法は私にはより明確で直感的に見えます。:)


1
ハッシュと配列は同じものです!配列は整数をオブジェクトにマップし、ハッシュはオブジェクトをオブジェクトにマップします。配列はハッシュの特殊なケースにすぎませんか?
トムリーマン

1
私が言ったように、配列は順序付けられたセットです。(一般的な意味での)マッピングは順不同です。キーセットを整数(配列など)に制限すると、キーセットに順序が付けられます。一般的なマッピング(ハッシュ/連想配列)では、キーに順序がない場合があります。
ピストス

@Horace:ハッシュと配列は同じではありません。一方が他方の特別なケースである場合、それらを同じにすることはできません。しかしさらに悪いことに、配列は特別な種類のハッシュではなく、それはそれらを表示する抽象的な方法にすぎません。上記のブレントが指摘したように、ハッシュと配列を交換可能に使用することは、コードのにおいを示している可能性があります。
ゼーン2014年

9

配列とハッシュで一貫して同じことを実行しようとすると、コードの匂いがするだけかもしれませんが、私が臭いのある半猿のパッチとしてブランド化されるリスクがある場合、一貫した動作を探しているなら、これでうまくいきます?:

class Hash
    def each_pairwise
        self.each { | x, y |
            yield [x, y]
        }
    end
end

class Array
    def each_pairwise
        self.each_with_index { | x, y |
            yield [y, x]
        }
    end
end

["a","b","c"].each_pairwise { |x,y|
    puts "#{x} => #{y}"
}

{"a" => "Aardvark","b" => "Bogle","c" => "Catastrophe"}.each_pairwise { |x,y|
    puts "#{x} => #{y}"
}

7

質問にリストされている4つのオプションを、制御の自由度に基づいて整理します。必要に応じて、別のものを使用することもできます。

  1. 単に値を調べます:

    array.each
  2. 単にインデックスを通過します:

    array.each_index
  3. インデックスとインデックス変数を確認します。

    for i in array
  4. 制御ループ数+インデックス変数:

    array.length.times do | i |

5

私はハッシュを使用して(CampingMarkabyで)メニューを構築しようとしていました。

各項目には2つの要素があります。メニューラベルURLなので、ハッシュは正しいように見えましたが、「ホーム」の「/」URLは常に最後に表示され(ハッシュに期待したとおり)、メニュー項目は間違って表示されました注文。

配列を使用each_sliceして仕事をします:

['Home', '/', 'Page two', 'two', 'Test', 'test'].each_slice(2) do|label,link|
   li {a label, :href => link}
end

各メニュー項目(CSS ID名など)に追加の値を追加することは、スライス値を増やすことを意味します。つまり、ハッシュに似ていますが、任意の数のアイテムで構成されるグループがあります。完璧です。

だから、これは解決策をうっかりヒントしてくれたことに感謝するだけです!

明白ですが、述べる価値があります。配列の長さがスライス値で割り切れるかどうかを確認することをお勧めします。


4

(Railsが行うように)列挙可能なミックスインを使用する場合、リストされたphpスニペットと同様のことを行うことができます。each_sliceメソッドを使用して、ハッシュをフラット化するだけです。

require 'enumerator' 

['a',1,'b',2].to_a.flatten.each_slice(2) {|x,y| puts "#{x} => #{y}" }

# is equivalent to...

{'a'=>1,'b'=>2}.to_a.flatten.each_slice(2) {|x,y| puts "#{x} => #{y}" }

モンキーパッチが少なくて済みます。

ただし、これは、再帰的な配列または配列値を持つハッシュがある場合に問題を引き起こします。Ruby 1.9では、この問題は、再帰の深さを指定するflattenメソッドのパラメーターで解決されています。

# Ruby 1.8
[1,2,[1,2,3]].flatten
=> [1,2,1,2,3]

# Ruby 1.9
[1,2,[1,2,3]].flatten(0)
=> [1,2,[1,2,3]]

これがコードの匂いなのかどうかはわかりません。通常、何かを反復するために後方に曲がらなければならない場合、一歩下がって、問題を間違って攻撃していることに気付きます。


2

正しい方法は、あなたが最も快適に感じる方法であり、あなたがやりたいことを行います。プログラミングでは、物事を行うための「正しい」方法が1つになることはめったにありません。多くの場合、選択する方法は複数あります。

物事の特定の方法に慣れている場合は、それが機能しない場合を除いて、それだけを行ってください。次に、より良い方法を見つける時が来ました。


2

Ruby 2.1では、each_with_indexメソッドが削除されました。代わりに、each_indexを使用できます

例:

a = [ "a", "b", "c" ]
a.each_index {|x| print x, " -- " }

生成する:

0 -- 1 -- 2 --

4
本当じゃない。承認された回答のコメントに記載されているeach_with_indexように、ドキュメントに表示されない理由は、Enumerableモジュールによって提供されるためです。
ihaztehcodez 2015年

0

配列とハッシュの両方を反復するために同じ方法を使用することは、たとえばパーサーやJSONファイルの読み取りなどからしばしば生じるネストされたハッシュと配列の構造を処理するために意味があります。

まだ言及されていない賢い方法の1つは、標準ライブラリ拡張のRuby Facetsライブラリでそれを行う方法です。ここから:

class Array

  # Iterate over index and value. The intention of this
  # method is to provide polymorphism with Hash.
  #
  def each_pair #:yield:
    each_with_index {|e, i| yield(i,e) }
  end

end

Hash#each_pairエイリアスはすでに存在しHash#eachます。したがって、このパッチの後でArray#each_pair、ハッシュと配列の両方を反復処理するために交換可能に使用することもできます。これにより、Array#each_with_indexと比較してブロック引数が逆になったOPの狂気が修正されHash#eachます。使用例:

my_array = ['Hello', 'World', '!']
my_array.each_pair { |key, value| pp "#{key}, #{value}" }

# result: 
"0, Hello"
"1, World"
"2, !"

my_hash = { '0' => 'Hello', '1' => 'World', '2' => '!' }
my_hash.each_pair { |key, value| pp "#{key}, #{value}" }

# result: 
"0, Hello"
"1, World"
"2, !"
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.