Ruby에서 배열을 반복하는 "올바른"방법은 무엇입니까?


341

모든 사마귀에 대해 PHP는이 카운트에서 꽤 좋습니다. 배열과 해시 사이에는 차이가 없으며 (아마도 순진하지만 분명히 나에게 맞는 것 같습니다) 두 가지 중 하나를 반복하면됩니다.

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

루비에는 이런 종류의 작업을 수행하는 여러 가지 방법이 있습니다.

array.length.times do |i|
end

array.each

array.each_index

for i in array

항상 사용하기 때문에 해시가 더 의미가 있습니다.

hash.each do |key, value|

왜 어레이에 대해 이것을 할 수 없습니까? 하나의 방법 만 기억하고 싶다면 each_index(색인과 값을 모두 사용할 수 있기 때문에) 사용할 수 있다고 생각 하지만 array[index]just 대신 대신 해야하는 번거 로움이 있습니다 value.


아 맞다 array.each_with_index. 이 이동하기 때문에,이 사람은 짜증 |value, key|hash.each간다 |key, value|! 이건 제정신이 아닙니까?


1
것 같아요 array#each_with_index용도를 |value, key|메소드 이름이 순서를 의미하기 때문에 순서가 사용되는 반면, hash#each모방 hash[key] = value구문?
Benjineer

답변:


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
주 제외 나는 배열의 루비 1.9에서
each_with_index를

19
@CantGetANick : Array 클래스에는이 메소드가있는 Enumerable 모듈이 포함되어 있습니다.
xuinkrbin.

88

올바른 길은 없다고 생각 합니다. 반복하는 방법에는 여러 가지가 있으며 각각 고유 한 틈새가 있습니다.

  • each 인덱스에 신경 쓰지 않기 때문에 많은 사용법에 충분합니다.
  • each_ with _index Hash # each처럼 작동합니다-값과 색인을 얻습니다.
  • each_index-색인 만. 나는 이것을 자주 사용하지 않습니다. "length.times"와 같습니다.
  • map 한 배열을 다른 배열로 변환하려고 할 때 유용한 반복 방법입니다.
  • select 부분 집합을 선택할 때 사용할 반복자입니다.
  • inject 합계 또는 곱을 생성하거나 단일 결과를 수집하는 데 유용합니다.

기억해야 할 것 같지만 걱정하지 마십시오. 모두 알지 않고도 얻을 수 있습니다. 그러나 다른 방법을 배우고 사용하기 시작하면 코드가 더 깨끗 해지고 명확 해지며 루비 마스터리로 향하게됩니다.


좋은 답변입니다! #reject와 같은 반대와 #collect와 같은 별칭을 언급하고 싶습니다.
Emily Tui Ch

60

나는 Array-> |value,index|Hash-> |key,value|가 미쳤다고 말하지 는 않지만 (Horace Loeb의 의견 참조)이 배열을 기대할 수있는 건전한 방법이 있다고 말하고 있습니다.

배열을 다룰 때 배열의 요소 (색인이 일시적이기 때문에 색인이 아님)에 중점을 둡니다. 이 방법은 각각 인덱스, 즉 각 + 인덱스 또는 | each, index | 또는 |value,index|입니다. 이는 인덱스가 선택적 인수로 간주되는 것과 일치합니다. 예 : | value | | value, index = nil |와 같습니다. | value, index |와 일치합니다.

해시를 다룰 때 종종 값보다 키에 더 집중하고 일반적으로 키와 값을 순서대로 key => value또는으로 처리 hash[key] = value합니다.

오리 타이핑을 원한다면 Brent Longborough가 보여준대로 정의 된 방법을 사용하거나 maxhawkins가 보여준 암시 적 방법을 사용하십시오.

루비는 프로그래머가 언어에 맞게 수용하는 것이 아니라 프로그래머에게 맞게 언어를 수용하는 것에 관한 것입니다. 이것이 많은 방법이있는 이유입니다. 무언가를 생각하는 방법은 너무 많습니다. Ruby에서 가장 가까운 코드를 선택하면 나머지 코드는 대개 매우 깔끔하고 간결하게 표시됩니다.

원래 질문에 대해서는 "루비에서 배열을 반복하는"올바른 "방법은 무엇입니까?"라고 생각합니다.

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

그러나 루비는 강력한 구문 설탕과 객체 지향적 인 힘에 관한 것이지만 어쨌든 해시와 동일하며 키를 주문할 수 있습니다.

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

그래서 제 대답은 "루비에서 배열을 반복하는"올바른 "방법은 여러분 (예 : 프로그래머 또는 프로그래밍 팀)과 프로젝트에 달려 있습니다." 더 나은 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

그러나 객체 지향 접근법보다는 순수한 루비를 가르치고 싶습니다 (아직도). 거꾸로 반복하고 싶습니다.

  • 배열을 만들지 않고 (0..1000000000 고려)
  • 모든 범위에서 작동 (예 : 정수가 아닌 문자열)
  • 추가적인 객체 지향적 힘을 사용하지 않는 것

pred메서드 를 정의하지 않으면 이것이 불가능하다고 생각합니다. 즉, Range 클래스를 사용하여 수정합니다. 이 작업을 수행 할 수있는 경우 알려주십시오. 그렇지 않으면 불가능한 확인은 실망 스럽지만 감사하겠습니다. 아마도 루비 1.9가 이것을 해결합니다.

(이 글을 읽어 주셔서 감사합니다.)


5
1.upto(10) do |i| puts i end그리고 10.downto(1) do puts i end당신이 원하는 대칭을 제공합니다. 희망이 도움이됩니다. 문자열 등에서 작동하는지 확실하지 않습니다.
Suren

(1..10) .to_a.sort {| x, y | y <=> x} .each {| i | "i = # {i}"를 넣습니다.}-반대가 더 느립니다.
Davidslv

당신은 할 수 있습니다 [*1..10].each{|i| puts "i=#{i}" }.
Hauleth

19

둘 다 필요할 때 each_with_index를 사용하십시오.

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

12

다른 대답은 괜찮지 만 다른 주변 장치를 지적하고 싶었습니다. 배열은 순서가 있지만 해시는 1.8이 아닙니다. (Ruby 1.9에서 해시는 키의 삽입 순서에 따라 정렬됩니다.) 따라서 1.9 이전에는 항상 명확한 순서를 가진 Arrays와 같은 방식 / 순서로 해시를 반복하는 것이 이치에 맞지 않습니다. PHP 연관 배열의 기본 순서가 무엇인지 알지 못합니다 (아마도 내 Google fu는 그 정도를 알아낼 정도로 강하지 않습니다).하지만 일반 PHP 배열과 PHP 연관 배열을 어떻게 고려할 수 있는지 모르겠습니다. 연관 배열의 순서는 정의되어 있지 않기 때문에이 문맥에서 "동일"합니다.

따라서 루비 방식은 더 명확하고 직관적입니다. :)


1
해시와 배열은 동일합니다! 배열은 정수를 객체에 매핑하고 해시는 객체를 객체에 매핑합니다. 배열은 해시의 특별한 경우입니다.
Tom Lehman

1
내가 말했듯이 배열은 순서 집합입니다. 일반적인 의미에서 매핑은 순서가 없습니다. 키 세트를 정수 (예 : 배열)로 제한하면 키 세트에 순서가 있습니다. 일반 매핑 (해시 / 연관 배열)에서는 키 순서가 없을 수 있습니다.
Pistos

@Horace : 해시와 배열이 동일하지 않습니다. 하나가 다른 하나의 특별한 경우라면 동일 할 수 없습니다. 그러나 더 나쁜 것은, 배열은 특별한 종류의 해시가 아니며, 그것들을 보는 추상적 인 방법 일뿐입니다. 브렌트가 지적했듯이, 해시와 배열을 상호 교환하여 사용하면 코드 냄새가 날 수 있습니다.
Zane

9

배열과 해시가 일관되게 같은 일을 시도 할 수 는 일관된 동작을 찾고 있다면,하는 codorous 반 원숭이 패쳐로 브랜드 내 존재의 위험에서하는 것과 코드 냄새,하지만이 트릭을 할 ? :

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

귀하의 질문에 나열된 네 가지 옵션은 자유로이 정리 된 것입니다. 필요한 것에 따라 다른 것을 사용하고 싶을 수도 있습니다.

  1. 간단히 값을 살펴보십시오.

    array.each
  2. 간단히 인덱스를 살펴보십시오.

    array.each_index
  3. 인덱스 + 인덱스 변수를 살펴보십시오.

    for i in array
  4. 제어 루프 수 + 인덱스 변수 :

    array.length.times do | i |

5

해시를 사용하여 메뉴 ( 캠핑Markaby ) 를 만들려고했습니다 .

각 항목에는 메뉴 레이블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

당신이 사용하는 경우 열거 (레일처럼) 믹스 인을 당신은 나와있는 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}" }

적은 원숭이 패치가 필요합니다.

그러나 재귀 배열이나 배열 값이있는 해시가 있으면 문제가 발생합니다. 루비 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

올바른 방법은 당신이 가장 편안하게 느끼고 원하는 것을하는 것입니다. 프로그래밍에는 일을하는 '올바른'방법이 거의 없으며, 더 자주 선택할 수있는 여러 가지 방법이 있습니다.

당신이 일을하는 특정 방법으로 편안하다면, 그것이 효과가 없다면 그것을하십시오. 그러면 더 좋은 길을 찾을 때입니다.


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

0

배열과 해시를 반복하는 데 동일한 방법을 사용하는 것은 예를 들어 파서에서 종종 발생하는 중첩 해시 및 배열 구조를 처리하고 JSON 파일을 읽는 등의 의미가 있습니다.

아직 언급되지 않은 한 가지 영리한 방법 은 표준 라이브러리 확장 의 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.