Enumerable 모듈

Enumerable 모듈

컬렉션 클래스에 "열거" 능력을 주고 싶을 때, 이 Enumerable 모듈이 가장 먼저 손이 가는 도구예요. 요소를 조회하고, 뽑아내고, 검색하고, 정렬하고, 반복하는 데 유용한 메서드들을 제공하죠.

여기 있는 것들 (What's Here)

Enumerable 모듈이 컬렉션 클래스를 위해 제공하는 메서드를 용도별로 정리하면 이렇게 돼요.

조회 메서드 (Methods for Querying)

요소 자체가 아니라 Enumerable에 대한 정보를 돌려주는 메서드예요:

  • member?(include?의 별칭): self == objecttrue, 아니면 false
  • all?: 모든 요소가 기준을 만족하면 true, 아니면 false
  • any?: 어떤 요소가 기준을 만족하면 true, 아니면 false
  • none?: 어떤 요소도 기준을 만족하지 않으면 true, 아니면 false
  • one?: 정확히 하나의 요소만 기준을 만족하면 true, 아니면 false
  • count: (주어지면) 인자나 블록 기준에 따른 요소의 개수
  • tally: 각 요소의 등장 횟수를 담은 새 Hash

가져오는 메서드 (Methods for Fetching)

Enumerable을 수정하지 않고 항목을 돌려주는 메서드예요.

처음·끝·전체 요소:

  • to_a(entries의 별칭): 모든 요소
  • first: 첫 요소 또는 앞의 요소들
  • take: 지정한 개수의 앞 요소들
  • drop: 지정한 개수의 뒤 요소들
  • take_while: 블록이 시키는 대로 앞 요소들
  • drop_while: 블록이 시키는 대로 뒤 요소들

최소·최대 값 요소:

  • min: <=> 또는 주어진 블록 기준으로 가장 작은 요소
  • max: 가장 큰 요소
  • minmax: 가장 작은 요소와 가장 큰 요소를 담은 2-요소 Array
  • min_by: 주어진 블록 기준으로 가장 작은 요소
  • max_by: 주어진 블록 기준으로 가장 큰 요소
  • minmax_by: 주어진 블록 기준으로 가장 작고 큰 요소

그룹·슬라이스·분할:

  • group_by: 요소를 그룹으로 나누는 Hash
  • partition: 블록 기준으로 요소를 두 개의 새 Array로 분할
  • slice_after: 주어진 object 또는 블록 기준으로 self를 분할한 항목들의 새 Enumerator
  • slice_before: 위와 같지만 앞쪽 기준
  • slice_when: 블록 기준으로 self를 분할한 항목들의 새 Enumerator
  • chunk: 블록이 시키는 대로 요소를 청크로 조직
  • chunk_while: 블록이 시키는 대로 요소를 청크로 조직

검색·필터 메서드

기준을 만족하는 요소를 돌려주는 메서드예요:

  • find(detect의 별칭): 블록이 고른 요소
  • find_all(filter, select의 별칭): 블록이 고른 요소들
  • find_index: 주어진 객체 또는 블록이 고른 요소의 인덱스
  • reject: 블록이 거부하지 않은 요소들
  • uniq: 중복되지 않은 요소들

정렬 메서드

정렬된 순서로 요소를 돌려주는 메서드예요:

  • sort: <=> 또는 주어진 블록으로 정렬
  • sort_by: 주어진 블록으로 정렬

반복 메서드

  • each_entry: 각 연속 요소로 블록 호출 (each와 살짝 다름)
  • each_with_index: 각 연속 요소와 그 인덱스로 블록 호출
  • each_with_object: 각 연속 요소와 주어진 객체로 블록 호출
  • each_slice: 겹치지 않는 연속 슬라이스로 블록 호출
  • each_cons: 겹치는 연속 슬라이스로 블록 호출 (each_slice와 다름)
  • reverse_each: 각 요소를 역순으로 블록 호출

기타 메서드

  • collect(map의 별칭): 블록이 돌려준 객체들
  • filter_map: 블록이 돌려준 truthy 객체들
  • flat_map(collect_concat의 별칭): 블록이 돌려준 객체들을 펼친 것
  • grep: 주어진 객체로 고른 요소 또는 블록이 돌려준 객체들
  • grep_v: 주어진 객체로 고르지 않은 요소 또는 블록이 돌려준 객체들
  • inject(reduce의 별칭): 모든 요소를 결합해 만든 객체
  • sum: + 메서드로 합한 요소의 합
  • zip: 각 요소를 다른 enumerable의 요소와 결합, n-튜플을 돌려주거나 각각으로 블록 호출
  • cycle: 각 요소를 반복해서 블록 호출

사용법 (Usage)

컬렉션 클래스에서 Enumerable 모듈을 쓰려면:

  • Include하고:
    include Enumerable
    
  • 컬렉션의 연속 요소를 내보내는 each 메서드를 구현해요. each는 거의 모든 Enumerable 메서드가 호출하거든요.

예시:

class Foo
  include Enumerable
  def each
    yield 1
    yield 1, 2
    yield
  end
end
Foo.new.each_entry{ |element| p element }

출력:

1
[1, 2]
nil

Ruby 클래스들의 Enumerable

이 Ruby 코어 클래스들이 Enumerable을 include(또는 extend)해요:

  • ARGF, Array, Dir, Enumerator, ENV(extend), Hash, IO, Range, Struct

이 Ruby 표준 라이브러리 클래스들이 Enumerable을 include해요:

  • CSV, CSV::Table, CSV::Row, Set

Enumerable의 거의 모든 메서드는 include한 클래스의 each를 호출해요:

  • Hash#each는 다음 키-값 쌍을 2-요소 Array로 내보내요.
  • Struct#each는 다음 이름-값 쌍을 2-요소 Array로 내보내요.
  • 위의 다른 클래스들에서는 each가 컬렉션의 다음 객체를 내보내요.

확장 메서드 (Extended Methods)

Enumerable 클래스는 확장 메서드를 정의할 수 있어요. 이 섹션은 참고용으로 확장 메서드의 표준 동작을 설명해요.

size

Enumerator는 size 메서드를 갖고 있어요. Enumerator.new에 넘긴 size 함수 인자를 사용하죠.

e = Enumerator.new(-> { 3 }) {|y| p y; y.yield :a; y.yield :b; y.yield :c; :z }
p e.size #=> 3
p e.next #=> :a
p e.next #=> :b
p e.next #=> :c
begin
  e.next
rescue StopIteration
  p $!.result #=> :z
end

size 함수의 결과는 반복 횟수(즉 Enumerator::Yielder#yield가 호출되는 횟수)를 나타내야 해요. 위 예시에서 블록은 yield를 세 번 호출하고, size 함수 -> { 3 }이 그에 맞게 3을 돌려줘요. size 함수 결과는 정수, Float::INFINITY, nil 중 하나일 수 있어요. 정수는 yield가 호출될 정확한 횟수, Float::INFINITY는 무한 횟수, nil은 결정하기 어렵거나 불가능함을 뜻해요.

많은 반복 메서드는 블록을 주지 않으면 적절한 size 함수를 가진 Enumerator 객체를 돌려줘요.

["a", "b", "c"].each.size #=> 3
{a: "x", b: "y", c: "z"}.each.size #=> 3
(0..20).to_a.permutation.size #=> 51090942171709440000
loop.size #=> Float::INFINITY
(1..100).drop_while.size #=> nil  # size depends on the block's behavior
STDIN.each.size #=> nil # cannot be computed without consuming input
File.open("/etc/resolv.conf").each.size #=> nil # cannot be computed without reading the file

Range 기반 enumerator의 size 동작은 begin 요소에 따라 달라요:

  • begin 요소가 Integer면 size는 Integer 또는 Float::INFINITY를 돌려줘요.
  • begin 요소가 succ 메서드를 가진(Integer가 아닌) 객체면 size는 nil을 돌려줘요. (크기를 계산하려면 succ를 반복 호출해야 해서 너무 느릴 수 있어요.)
  • begin 요소가 succ 메서드가 없으면 size는 TypeError를 던져요.
(10..42).each.size #=> 33
(10..42.9).each.size #=> 33 (the #end element may be a non-integer numeric)
(10..).each.size #=> Float::INFINITY
("a".."z").each.size #=> nil
("a"..).each.size #=> nil
(1.0..9.0).each.size # raises TypeError (Float does not have #succ)
(..10).each.size # raises TypeError (beginless range has nil as its #begin)

Enumerable 모듈 자체는 size 메서드를 정의하지 않아요. include한 클래스가 자신만의 size 메서드를 정의할 수 있는데, 그런 size 메서드는 Enumerator#size와 일관성이 있는 게 좋아요. ArrayHashEnumerator#size와 일치하는 size를 구현해요. IODir은 size를 정의하지 않는데, 대응하는 enumerator의 size 함수가 nil을 돌려주므로 이것도 일관돼요. 다만 클래스의 size 메서드가 반드시 Enumerator#size와 일치해야 하는 건 아니에요. 예를 들어 File#size는 파일의 줄 수가 아니라 바이트 수를 돌려줘요.

인스턴스 메서드

  • all? → true or false — 모든 요소가 주어진 기준을 만족하는지 돌려줘요. self에 요소가 없으면 true를 돌려주고 인자·블록은 쓰지 않아요. 인자와 블록 없이 호출하면 모든 요소가 truthy인지 돌려줘요.

    (1..4).all?           # => true
    [1, 2, nil].all?      # => false
    [].all?               # => true
    

    패턴 인자와 블록 없이 호출하면 각 요소에 대해 pattern === element인지 돌려줘요:

    (1..4).all?(Integer)                 # => true
    %w[bar baz bat bam].all?(/ba/)       # => true
    [].all?(Integer)                     # => true
    

    블록을 주면 모든 요소에 대해 블록이 truthy 값을 돌려주는지 돌려줘요:

    (1..4).all? {|element| element < 5 }                    # => true
    {foo: 0, bar: 1, baz: 2}.all? {|key, value| value < 3 } # => true
    

    관련: any?, none?, one?.

  • any? → true or false — 어떤 요소가 주어진 기준을 만족하는지 돌려줘요. self에 요소가 없으면 false를 돌려줘요. 인자·블록 없이 호출하면 어떤 요소가 truthy인지: [1, false, nil].any? # => true, [].any? # => false. 패턴 인자와 함께: [nil, false, 0].any?(Integer) # => true. 블록과 함께: (1..4).any? {|element| element < 2 } # => true. 관련: all?, none?, one?.

  • chain(*enums) → enumerator — 이 enumerator와 주어진 enumerable들로 만든 enumerator 객체를 돌려줘요.

    e = (1..3).chain([4, 5])
    e.to_a #=> [1, 2, 3, 4, 5]
    
  • chunk {|array| ... } → enumerator — 돌려줄 enumerator의 각 요소는 2-요소 배열이에요: (블록이 돌려준 값, 그 값이 나온 요소와 이후 블록이 같은 값을 돌려준 모든 요소로 이뤄진 "청크" 배열). 즉 이전 값과 다른 블록 반환값은 새 청크를 시작하고, 이전 값과 같은 반환값은 같은 청크를 이어가요.

    e = (0..10).chunk {|i| (i / 3).floor } # => #<Enumerator: ...>
    e.next # => [0, [0, 1, 2]]
    e.next # => [1, [3, 4, 5]]
    e.next # => [2, [6, 7, 8]]
    e.next # => [3, [9, 10]]
    

    특수 심볼 :_alone을 쓰면 요소를 자기만의 별도 청크로 강제할 수 있어요:

    a = [0, 0, 1, 1]
    e = a.chunk{|i| i.even? ? :_alone : true }
    e.to_a # => [[:_alone, [0]], [:_alone, [0]], [true, [1, 1]]]
    

    특수 심볼 :_separator 또는 nil을 쓰면 요소를 무시(어떤 청크에도 포함하지 않음)하도록 강제할 수 있어요:

    a = [0, 0, -1, 1, 1]
    e = a.chunk{|i| i < 0 ? :_separator : true }
    e.to_a # => [[true, [0, 0]], [true, [1, 1]]]
    

    separated는 청크를 끝낸다는 점에 주의하세요.

  • chunk_while {|elt_before, elt_after| bool } → an_enumerator — 각 청크된 요소들에 대한 enumerator를 만들어요. 청크의 시작은 블록이 정의해요. 인접 요소 elt_beforeelt_after 사이를, 블록이 false를 돌려주는 지점에서 나눠요. 블록은 receiver enumerator 길이 - 1번 호출돼요. 결과 enumerator는 청크된 요소들을 배열로 내보내요.

    a = [1,2,4,9,10,11,12,15,16,19,20,21]
    b = a.chunk_while {|i, j| i+1 == j }
    p b.to_a #=> [[1, 2], [4], [9, 10, 11, 12], [15, 16], [19, 20, 21]]
    c = b.map {|a| a.length < 3 ? a : "#{a.first}-#{a.last}" }
    p c #=> [[1, 2], [4], "9-12", [15, 16], "19-21"]
    d = c.join(",")
    p d #=> "1,2,4,9-12,15,16,19-21"
    

    인접한 짝수·홀수도 청크할 수 있어요:

    a = [7, 5, 9, 2, 0, 7, 9, 4, 2, 0]
    p a.chunk_while {|i, j| i.even? == j.even? }.to_a
    #=> [[7, 5, 9], [2, 0], [7, 9], [4, 2, 0]]
    

    Enumerable#slice_when은 블록이 true를 돌려주는 지점에서 나눈다는 점만 빼고 같아요.

  • compact → arraynil이 아닌 모든 요소의 배열을 돌려줘요: [nil, 0, nil, 'a'].compact # => [0, "a"]

  • count → integer — (주어지면) 인자나 블록 기준에 따른 요소 개수를 돌려줘요. 인자·블록 없이: 요소 개수. object 인자: ==로 같은 요소 개수. 블록: truthy를 돌려주는 요소 개수.

    [0, 1, 2].count                # => 3
    [0, 1, 2, 1].count(1)           # => 2
    [0, 1, 2, 3].count {|element| element < 2} # => 2
    
  • cycle(n = nil) {|element| ...} → nil — 양의 정수 인자 n과 블록과 함께 호출하면, 각 요소로 n번 블록을 호출하고 nil을 돌려줘요. count가 0이거나 음수면 블록을 호출하지 않아요. nnil이면 무한히 돌려요. 블록이 없으면 Enumerator를 돌려줘요.

    a = []
    (1..4).cycle(3) {|element| a.push(element) } # => nil
    a # => [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]
    
  • drop(n) → array — 양의 정수 n에 대해 첫 n개 요소를 제외한 모두를 담은 배열을 돌려줘요: (1..4).drop(3) # => [4], (1..4).drop(50) # => []

  • drop_while {|element| ... } → array — 블록이 truthy를 돌려주는 동안 연속 요소로 블록을 호출하고, 그 이후의 모든 요소 배열을 돌려줘요: (1..4).drop_while{|i| i < 3 } # => [3, 4]. 블록이 없으면 Enumerator를 돌려줘요.

  • each_cons(n) { ... } → self — 각 연속되는 겹치는 n-튜플 요소로 블록을 호출하고 self를 돌려줘요.

    a = []
    (1..5).each_cons(3) {|element| a.push(element) }
    a # => [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
    
  • each_entry(*args) {|element| ... } → self — yield에서 여러 값을 배열로 변환하면서 각 요소로 주어진 블록을 호출하고 self를 돌려줘요. 블록이 없으면 Enumerator를 돌려줘요.

    a = []
    (1..4).each_entry {|element| a.push(element) } # => 1..4
    a # => [1, 2, 3, 4]
    
  • each_slice(n) { ... } → self — 각 연속되는 겹치지 않는 n-튜플 요소로 블록을 호출하고 self를 돌려줘요: (1..10).each_slice(3) {|tuple| a.push(tuple) }. 블록이 없으면 Enumerator를 돌려줘요.

  • each_with_index(*args) {|element, i| ... } → self*argsself.each를 호출해요. 블록을 주면 각 요소와 그 인덱스를 받아요. 블록이 없으면 Enumerator를 돌려줘요.

    h = {}
    (1..4).each_with_index {|element, i| h[element] = i } # => 1..4
    h # => {1=>0, 2=>1, 3=>2, 4=>3}
    
  • each_with_object(object) { |(*args), memo_object| ... } → object — 각 요소마다 객체와 함께 블록을 한 번씩 호출하고 object를 돌려줘요.

    (1..4).each_with_object([]) {|i, a| a.push(i**2) }
    # => [1, 4, 9, 16]
    
  • filter_map {|element| ... } → array — 블록이 돌려준 truthy 요소들을 담은 배열을 돌려줘요.

    (0..9).filter_map {|i| i * 2 if i.even? } # => [0, 4, 8, 12, 16]
    
  • find(if_none_proc = nil) {|element| ... } → object or nil — 블록이 truthy를 돌려주는 첫 요소를 돌려줘요. 발견하지 못하면 if_none_proc을 호출하고 그 반환값을 돌려줘요. 블록이 없으면 Enumerator를 돌려줘요.

    (0..9).find {|element| element > 2}                # => 3
    (0..9).find(proc {false}) {|element| element > 12} # => false
    
  • find_index(object) → integer or nil — 지정된 기준을 만족하는 첫 요소의 인덱스를 돌려주거나, 없으면 nil을 돌려줘요. object 인자: ==인 첫 요소의 인덱스. 블록: truthy를 돌려주는 첫 요소의 인덱스. 인자·블록 없이: Enumerator.

    ['a', 'b', 'c', 'b'].find_index('b') # => 1
    
  • first → element or nil — 첫 요소를 돌려주거나, 없으면 nil. 정수 n과 함께: 존재하는 첫 n개 요소를 담은 배열.

    (1..4).first        # => 1
    (1..4).first(2)     # => [1, 2]
    [].first            # => nil
    
  • flat_map {|element| ... } → array — 블록이 돌려준 객체들을 펼친 배열을 돌려줘요. (collect_concat의 별칭)

    [0, 1, 2, 3].flat_map {|element| [element, -element] } # => [0, 0, 1, -1, 2, -2, 3, -3]
    
  • grep(pattern) → array — 주어진 패턴과 일치하는 요소 기반 객체 배열을 돌려줘요. 블록 없이: pattern === element가 true인 각 요소. 블록과 함께: 각 일치 요소로 블록을 호출하고 반환된 각 객체를 담은 배열.

    a = ['foo', 'bar', 'car', 'moo']
    a.grep(/ar/) # => ["bar", "car"]
    a.grep(/ar/) {|element| element.upcase } # => ["BAR", "CAR"]
    

    grep_v는 반대예요 (pattern === element가 false인 요소).

  • group_by {|element| ... } → hash — 블록과 함께 호출하면 해시를 돌려줘요. 각 키는 블록 반환값, 각 값은 그 키를 돌려준 요소들의 배열.

    g = (1..6).group_by {|i| i%3 }
    g # => {1=>[1, 4], 2=>[2, 5], 0=>[3, 6]}
    
  • include?(object) → true or false — 어떤 요소에 대해 object == element인지.

    (1..4).include?(2)  # => true
    (1..4).include?(5)  # => false
    
  • inject(symbol) → object — 감소기(reducer)를 초기값과 Enumerable의 첫 요소에 적용한 결과를 돌려줘요.

    [ a, b, c, d ].inject(i) { |r, v| fn(r, v) }fn(fn(fn(fn(i, a), b), c), d)로 생각하면 돼요. injectreduce로도 불려요. 호출 시퀀스: 가장 장황한 형태는 enum.inject(initial_value) do |result, next_value| ... end예요.

    product = [ 2, 3, 4 ].inject(1) do |result, next_value|
      result * next_value
    end
    product #=> 24
    

    첫 번째 단축키: 기본 초기값: 초기값을 생략하면 inject는 첫 요소를 result, 둘째 요소를 next_value로 블록을 호출해요. 단, 블록 결과가 다시 첫 인자로 넘겨질 수 있을 때만 쓸 수 있어요:

    [ 2, 3, 4 ].inject do |result, next_value|
      result * next_value
    end
    

    두 번째 단축키: 감소기 함수: inject에 감소기 함수의 이름(심볼)을 넘길 수 있어요. 그 함수는 결과 값 타입에 정의돼 있어야 하고, 컬렉션의 다음 값을 단일 인자로 받으며, 갱신된 결과를 돌려줘야 해요.

    s = [ "cat", " ", "dog" ].inject("", :concat)
    s #=> "cat dog"
    s = [ "cat", " ", "dog" ].inject("The result is:", :+)
    s #=> "The result is: cat dog"
    

    세 번째 단축키: 초기값 없는 감소기: 감소기가 자신을 받아들일 수 있는 값을 돌려주면 초기값을 넘기지 않아도 돼요:

    product = [ 2, 3, 4 ].inject(:*)
    product # => 24
    nested = {foo: 0, bar: 1}.inject([], :push)
    nested # => [[:foo, 0], [:bar, 1]]
    
  • lazy → lazy_enumeratorEnumerator::Lazy를 돌려줘요. 대부분의 Enumerable 메서드를 재정의해 열거를 연기하고, 필요할 때만 값을 열거해요.

    def pythagorean_triples
      (1..Float::INFINITY).lazy.flat_map {|z|
        (1..z).flat_map {|x|
          (x..z).select {|y|
            x**2 + y**2 == z**2
          }.map {|y|
            [x, y, z]
          }
        }
      }
    end
    p pythagorean_triples.take(10).force # take is lazy, so force is needed
    p pythagorean_triples.first(10)      # first is eager
    
  • map {|element| ... } → array — 블록이 돌려준 객체들의 배열을 돌려줘요. 블록이 없으면 Enumerator.

    (0..4).map {|i| i*i } # => [0, 1, 4, 9, 16]
    
  • max → element — 주어진 기준에 따른 최대 요소를 돌려줘요. 같은 값의 순서는 결정적이지 않고 불안정할 수 있어요. 인자·블록 없이 <=>로 비교. 정수 n과 함께: 존재하는 첫 n개 최대 요소. 블록: 두 요소 a,b로 호출되어 a<b면 음수, a==b면 0, a>b면 양수를 돌려줘야 해요.

    (1..4).max                   # => 4
    (1..4).max(2)                # => [4, 3]
    %w[xxx x xxxx xx].max {|a, b| a.size <=> b.size } # => "xxxx"
    

    관련: min, minmax, max_by.

  • max_by {|element| ... } → element — 블록이 최대 값을 돌려주는 요소를 돌려줘요. 정수 n과 함께: 블록이 최대 값을 돌려주는 n개 요소를 담은 배열. 블록 없이 Enumerator.

  • min → element — 최소 요소. 관련: min_by, minmax, max.

    (1..4).min                   # => 1
    (1..4).min(2)                # => [1, 2]
    %w[xxx x xxxx xx].min {|a, b| a.size <=> b.size } # => "x"
    
  • min_by {|element| ... } → element — 블록이 최소 값을 돌려주는 요소. n과 함께: 최소 값의 n개 요소 배열.

  • minmax → [minimum, maximum] — 최소·최대 요소를 담은 2-요소 배열. 관련: min, max, minmax_by.

  • minmax_by {|element| ... } → [minimum, maximum] — 블록이 최소·최대 값을 돌려주는 요소들을 담은 2-요소 배열.

  • none? → true or false — 어떤 요소도 주어진 기준을 만족하지 않는지. 인자·블록 없이: truthy인 요소가 없는지. 패턴: pattern === element인 요소가 없는지. 블록: truthy를 돌려주는 요소가 없는지. 관련: one?, all?, any?.

  • one? → true or false — 정확히 하나의 요소만 기준을 만족하는지. 관련: none?, all?, any?.

  • partition {|element| ... } → [true_array, false_array] — 두 배열로 이뤄진 배열을 돌려줘요. 첫 번째는 블록이 truthy를 돌려주는 요소들, 다른 하나는 나머지 전부.

    p = (1..4).partition {|i| i.even? }
    p # => [[2, 4], [1, 3]]
    
  • reject {|element| ... } → array — 블록이 거부한 객체들의 배열을 돌려줘요 (블록이 nil 또는 false를 돌려주는 요소들). 블록 없이 Enumerator. 관련: select.

  • reverse_each(*args) {|element| ... } → self — 각 요소를 역순으로 블록 호출하고 self를 돌려줘요. 블록 없이 Enumerator.

    a = []
    (1..4).reverse_each {|element| a.push(-element) } # => 1..4
    a # => [-4, -3, -2, -1]
    
  • select {|element| ... } → array — 블록이 고른 요소들을 담은 배열. 관련: reject.

    (0..9).select {|element| element % 3 == 0 } # => [0, 3, 6, 9]
    
  • slice_after(pattern) → an_enumerator — 각 청크된 요소들에 대한 enumerator를 만들어요. 청크의 끝은 pattern과 블록이 정의해요. pattern === elt 또는 블록이 그 요소에 대해 true를 돌려주면 그 요소가 청크의 끝이에요. ===blockenum의 첫 요소부터 마지막 요소까지 호출돼요. 결과 enumerator는 청크된 요소들을 배열로 내보내요.

    lines = ["foo\n", "bar\\\n", "baz\n", "\n", "qux\n"]
    e = lines.slice_after(/(?<!\\)\n\z/)
    p e.to_a
    #=> [["foo\n"], ["bar\\\n", "baz\n"], ["\n"], ["qux\n"]]
    
  • slice_before(pattern) → enumeratorpattern 인자와 함께, 패턴이 요소들을 배열("슬라이스")로 분할하는 데 쓰이는 enumerator를 돌려줘요. element === pattern(또는 첫 요소)이면 그 요소가 새 슬라이스를 시작해요. 블록과 함께: 블록 반환값이 truthy(또는 첫 요소)이면 새 슬라이스.

    a = %w[foo bar fop for baz fob fog bam foy]
    e = a.slice_before(/ba/) # => #<Enumerator: ...>
    e.each {|array| p array }
    

    출력:

    ["foo"]
    ["bar", "fop", "for"]
    ["baz", "fob", "fog"]
    ["bam", "foy"]
    
  • slice_when {|elt_before, elt_after| bool } → an_enumerator — 각 청크된 요소들에 대한 enumerator. 청크 시작은 블록이 정의해요. 인접 요소 사이를, 블록이 true를 돌려주는 지점에서 나눠요.

    a = [1,2,4,9,10,11,12,15,16,19,20,21]
    b = a.slice_when {|i, j| i+1 != j }
    p b.to_a #=> [[1, 2], [4], [9, 10, 11, 12], [15, 16], [19, 20, 21]]
    

    chunk_while은 블록이 false를 돌려주는 지점에서 나눈다는 점만 빼고 같아요.

  • sort → arrayself의 정렬된 요소를 담은 배열을 돌려줘요. 블록 없이: 요소 자신의 <=>로 비교. 블록: 두 요소 a,b로 호출되어 순서를 결정. 관련 sort_by는 Schwartzian transform을 구현하며 키 계산이나 비교가 비쌀 때 유용해요.

    %w[b c a d].sort              # => ["a", "b", "c", "d"]
    a = %w[b c a d]
    a.sort {|a, b| b <=> a }      # => ["d", "c", "b", "a"]
    
  • sort_by {|element| ... } → array — 블록이 각 요소에 대해 돌려준 값에 따라 정렬된 self의 요소 배열을 돌려줘요. sort_by는 원래 컬렉션 요소와 매핑된 값을 담은 튜플 배열을 만들기 때문에, 키셋이 단순할 때는 꽤 비싸요. ary.sort_by { ... }.reverse!로 특정 순서의 역순을 만들 수 있어요.

  • sum(initial_value = 0) → number — 블록 없이: initial_value와 요소들의 합. 블록과 함께: initial_value와 블록 반환값들의 합. 가능하면 가우스 합 공식 n(n+1)/2로 계산해요.

    (1..100).sum          # => 5050
    (1..100).sum(1)       # => 5051
    ('a'..'d').sum('foo') # => "fooabcd"
    (1..4).sum {|i| i*i } # => 30
    
  • take(n) → array — 음이 아닌 정수 n에 대해 첫 n개 요소.

    (1..4).take(2) # => [1, 2]
    (1..4).take(0) # => []
    
  • take_while {|element| ... } → array — 블록이 truthy를 돌려주는 동안 연속 요소로 블록을 호출하고, 그 시점까지의 모든 요소 배열을 돌려줘요: (1..4).take_while{|i| i < 3 } # => [1, 2]. 블록 없이 Enumerator.

  • tally(hash = {}) → hashhash 인자가 없으면: self의 고유 요소를 키로, 각 요소의 등장 횟수를 정수 값으로 하는 새 해시를 돌려줘요. hash 인자를 주면 그 hash를 (가능하면) 보강해 돌려줘요. 각 요소에 대해 키가 없으면 값 0으로 추가하고 값을 1 증가시켜요. 여러 enumerable에 걸쳐 합산을 누적할 때 유용해요.

    %w[a b c b c a c b].tally # => {"a"=>2, "b"=>3, "c"=>3}
    
    h = {}                   # => {}
    %w[a c d b c a].tally(h) # => {"a"=>2, "c"=>2, "d"=>1, "b"=>1}
    %w[b a z].tally(h)       # => {"a"=>3, "c"=>2, "d"=>1, "b"=>2, "z"=>1}
    
  • to_a(*args) → arrayself의 항목들을 담은 배열을 돌려줘요: (0..4).to_a # => [0, 1, 2, 3, 4]

  • to_h(*args) → hashself가 2-요소 배열로 이뤄져 있으면, 각 배열이 만드는 키-값 쌍의 해시를 돌려줘요. 블록을 주면 그 블록이 2-요소 배열을 돌려줘야 해요. 요소가 2-요소 배열이 아니고 블록도 없으면 예외.

    [[:foo, 0], [:bar, 1], [:baz, 2]].to_h # => {:foo=>0, :bar=>1, :baz=>2}
    (0..3).to_h {|i| [i, i ** 2]} # => {0=>0, 1=>1, 2=>4, 3=>9}
    
  • uniq → array — 블록 없이: e0.eql?(e1)인 두 요소가 없는, 고유 요소만 담은 새 배열. 블록: 블록이 고유한 값을 돌려주는 요소만 담은 새 배열.

    %w[a b c c b a a b c].uniq       # => ["a", "b", "c"]
    a = [0, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1]
    a.uniq {|i| i.even? ? i : 0 }   # => [0, 2, 4]
    
  • zip(*other_enums) → array — 블록 없이, self.size 크기의 새 배열 new_array를 돌려줘요. 각 요소는 크기가 other_enums.size+1인 배열이며 self의 n번째 요소와 각 other_enum의 n번째 요소를 담아요. 다른 enumerable이 더 작으면 self.size까지 nil로 채우고, 더 크면 뒤 요소를 무시해요. 블록과 함께: 각 하위 배열로 블록 호출하고 nil을 돌려줘요.

    a = [:a0, :a1, :a2, :a3]
    b = [:b0, :b1, :b2]
    c = [:c0, :c1]
    d = a.zip(b, c)
    d # => [[:a0, :b0, :c0], [:a1, :b1, :c1], [:a2, :b2, nil], [:a3, nil, nil]]
    

출처: Ruby 4.0 API - Enumerable