루비에서 .each 루프의 끝을 알려줍니다.


91

다음과 같은 루프가있는 경우

users.each do |u|
  #some code
end

사용자는 여러 사용자의 해시입니다. 사용자 해시의 마지막 사용자이고 마지막 사용자에 대한 특정 코드 만 실행 하려는지 확인하는 가장 쉬운 조건부 논리는 무엇입니까?

users.each do |u|
  #code for everyone
  #conditional code for last user
    #code for the last user
  end
end

정말 해시를 의미합니까? 해시에서 주문하는 것이 항상 신뢰할 수있는 것은 아닙니다 (해시에 항목을 추가하는 방법과 사용중인 루비 버전에 따라 다름). 신뢰할 수없는 주문으로 '마지막'항목은 일관성이 없습니다. 질문의 코드와 당신이 얻는 대답은 배열에 더 적합하거나 열거 가능합니다. 예를 들어 해시는 each_with_index또는 last메서드 가 없습니다 .
Shadwell

1
HashEnumerable에 혼합되어 있으므로 each_with_index. 해시 키가 정렬되지 않은 경우에도 뷰를 렌더링 할 때 이러한 종류의 논리가 항상 나타납니다. 여기서 마지막 항목은 데이터 의미에서 실제로 "마지막"인지 여부에 관계없이 다르게 표시 될 수 있습니다.
Raphomet

물론, 해시에는 each_with_index사과가 있습니다. 네, 나는 그것이 올 것이라는 것을 알 수 있습니다. 질문을 명확히하려고합니다. 개인적으로 나에게 가장 좋은 대답은 사용 .last이지만 해시에만 적용되는 배열에는 적용되지 않습니다.
Shadwell

Ruby / Rails의 루프에서 Magic First 및 Last Indicator가 중복 됩니까? , 이것은 배열이 아닌 해시라는 점을 제외하고.
Andrew Grimm

1
@Andrew는 완전히 관련이 있다는 데 동의하지만, 굉장한 작은 대답은 그것이 정확히 속이 아닌 방법을 보여줍니다.
Sam Saffron

답변:


147
users.each_with_index do |u, index|
  # some code
  if index == users.size - 1
    # code for the last user
  end
end

4
이 문제는 조건문이 매번 실행됩니다. .last루프 외부에서 사용 하십시오.
bcackerman 2013-08-18

3
해시를 반복하는 경우 다음과 같이 작성해야합니다.users.each_with_index do |(key, value), index| #your code end
HUB

나는 완전히 같은 질문하지 알고 있지만,이 코드는 모든하지만 마지막 사용자에게 뭔가를 원한다면 나는 내가 무엇을보고 하였다 그 이후 upvoting 해요, 그래서 생각 잘 작동
흰 호랑이

38

당신이 모든 몇 가지 코드를 적용하고 양자 택일 / 또는 상황의 경우 에 마지막 사용자가 다음 몇 가지 고유 한 코드를 단지 마지막 사용자가 다른 솔루션 중 하나가 더 적합 할 수 있습니다.

그러나 모든 사용자에 대해 동일한 코드를 실행 하고 마지막 사용자에 대해 일부 추가 코드를 실행하는 것 같습니다 . 이 경우 더 정확하고 의도를 더 명확하게 설명합니다.

users.each do |u|
  #code for everyone
end

users.last.do_stuff() # code for last user

2
적절한 경우 조건부가 필요하지 않은 경우 +1. (물론 여기에서했습니다)
Jeremy

@meagar가 사용자를 두 번 반복하지 않습니까?
ant

@ant 아니오, 루프와 관련이없는 하나의 루프와 하나의 호출 .last이 있습니다.
meagar

@meagar 그래서 마지막에 도달하기 위해 마지막 요소까지 내부적으로 루프하지 않습니까? 반복하지 않고 직접 요소에 액세스하는 방법이 있습니까?
ant

1
@ant 아니요, 내부 루프가 없습니다 .last. 컬렉션은 이미 인스턴스화되었으며 단순히 배열에 액세스하는 것입니다. 컬렉션이 아직로드되지 않은 경우에도 (예 : 여전히 수화되지 않은 ActiveRecord 관계 였음) 마지막 값을 얻기 위해 루프 를 수행 last하지 않는 경우 매우 비효율적입니다. 마지막 레코드를 반환하도록 SQL 쿼리를 수정하기 만하면됩니다. 즉,이 컬렉션은 이미에 의해로드 되었으므로 . .eachx = [1,2,3]; x.last
meagar

20

최선의 접근 방식은 다음과 같습니다.

users.each do |u|
  #code for everyone
  if u.equal?(users.last)
    #code for the last user
  end
end

22
이 답변의 문제는 마지막 사용자가 목록의 앞부분에서도 발생하면 조건부 코드가 여러 번 호출된다는 것입니다.
evanrmurphy

1
을 사용해야 u.equal?(users.last)합니다. equal?메소드는 객체의 값이 아닌 object_id를 비교합니다. 그러나 이것은 기호 및 숫자와 함께 작동하지 않습니다.
Nafaa Boutefer

11

시도 했습니까 each_with_index?

users.each_with_index do |u, i|
  if users.size-1 == i
     #code for last items
  end
end

6
h = { :a => :aa, :b => :bb }
h.each_with_index do |(k,v), i|
  puts ' Put last element logic here' if i == h.size - 1
end

3

때로는 논리를 모든 사용자에 대해 하나와 마지막 사용자에 대해 하나씩 두 부분으로 분리하는 것이 더 좋습니다. 그래서 나는 다음과 같이 할 것입니다.

users[0...-1].each do |user|
  method_for_all_users user
end

method_for_all_users users.last
method_for_last_user users.last

3

@meager의 접근 방식은 마지막 사용자를 제외한 모든 사용자에게 일부 코드를 적용한 다음 마지막 사용자에게만 일부 고유 코드를 적용하는 상황에 대해서도 사용할 수 있습니다.

users[0..-2].each do |u|
  #code for everyone except the last one, if the array size is 1 it gets never executed
end

users.last.do_stuff() # code for last user

이렇게하면 조건부가 필요하지 않습니다!


@BeniBela 아니요, [-1]은 마지막 요소입니다. [-0]이 없습니다. 따라서 마지막 두 번째는 [-2]입니다.
coderuby

users[0..-2]있는 그대로 정확합니다 users[0...-1]. 다른 범위 사업자 참고 ..대를 ...참조 stackoverflow.com/a/9690992/67834
엘리엇 사익스

2

또 다른 해결책은 StopIteration에서 구출하는 것입니다.

user_list = users.each

begin
  while true do
    user = user_list.next
    user.do_something
  end
rescue StopIteration
  user.do_something
end

5
이것은이 문제에 대한 좋은 해결책이 아닙니다. 예외는 단순한 흐름 제어를 위해 남용되어서는 안되며 예외적 인 상황을 위한 것 입니다.
meagar

@meagar 이것은 실제로 거의 아주 멋지다. 나는 구조되지 않은 예외 또는 더 높은 방법으로 전달되어야하는 예외는 예외적 인 상황에만 적용되어야 한다는 데 동의합니다 . 그러나 이것은 반복자가 끝나는 위치에 대한 루비의 기본 지식에 액세스 할 수있는 깔끔한 (그리고 분명히 유일한) 방법입니다. 물론 선호하는 다른 방법이 있다면. 제가이 문제를 해결해야 할 유일한 문제는 첫 번째 항목에 대해 건너 뛰고 아무것도하지 않는 것 같습니다. 그것은 투박함의 경계를 넘을 것입니다.
Adamantish

2
@meagar "권한의 주장"에 대해 죄송하지만 Matz는 동의하지 않습니다. 실제로 StopIteration루프 종료를 처리하는 정확한 이유를 위해 설계되었습니다. Matz의 책에서 : "이는 비정상적으로 보일 수 있습니다. 예상치 못한 예외적 인 이벤트가 아닌 예상 된 종료 조건에 대해 예외가 발생합니다. ( StopIterationStandardErrorand 의 자손입니다 IndexError. 이름에 "오류"가 있습니다.) Ruby는이 외부 반복 기법에서 Python을 따릅니다. (자세히 ...)
BobRodes

(...) 루프 종료를 예외로 처리하여 루프 논리를 매우 간단하게 만듭니다. next특별한 반복 종료 값에 대해 의 반환 값을 확인할 필요가 next?next
없으며을

1
@Adamantish를 만날 때 loop do암묵적 이라는 점에 유의해야합니다 . 특히 외부에서 객체를 반복 할 때 사용됩니다 . 끝에서 반복 하고 종료합니다. 거기 에 넣을 필요가 없습니다 . ( 또는 을 사용하는 경우해야합니다 .)rescueStopIterationEnumeratorloop do; my_enum.next; endmy_enumrescue StopIterationwhileuntil
BobRodes

0

일부 루비 버전의 경우 해시에 대한 마지막 방법이 없습니다.

h = { :a => :aa, :b => :bb }
last_key = h.keys.last
h.each do |k,v|
    puts "Put last key #{k} and last value #{v}" if last_key == k
end

다른 사람의 대답을 향상시키는 대답입니까? '마지막'방법이 언급 된 답변과이 문제를 극복하기 위해 제안하는 내용을 게시 해주세요.
Artemix

가없는 Ruby 버전의 경우 last키 집합이 정렬되지 않으므로이 답변은 어쨌든 잘못된 / 무작위 결과를 반환합니다.
meagar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.