Ruby_Array_25

この記事は、以下のドキュメントを改変(自分なりに整理)して利用しています。

class Array (Ruby 3.0.0 リファレンスマニュアル)

インスタンスメソッド

select -> Enumerator
filter -> Enumerator
select {|item| ... } -> [object]
filter {|item| ... } -> [object]
  • 各要素に対してブロックを評価した値が真であった要素をすべて含む配列を返す
    • 真になる要素がひとつもなかった場合は空の配列を返す
  • ブロックを省略した場合は、各要素に対しブロックを評価し真になった値の配列を返すようなEnumeratorを返す
irb(main):001:0> arr = [1, 2, 3]
=> [1, 2, 3]

irb(main):002:0> enum = arr.select
=> #<Enumerator: [1, 2, 3]:select>

irb(main):003:0> enum.each {|v| v > 1 }
=> [2, 3]

irb(main):004:0> arr[1] = 0
=> 0

irb(main):005:0> arr
=> [1, 0, 3]

irb(main):008:0> enum
=> #<Enumerator: [1, 0, 3]:select>

irb(main):009:0> enum.each {|v| v > 1 }
=> [3]
irb(main):001:0> arr = [1, 2, 3]
=> [1, 2, 3]

irb(main):002:0> arr.select {|v| v > 1 }
=> [2, 3]

irb(main):003:0> arr.select {|v| v > 10 }
=> []

irb(main):004:0> arr
=> [1, 2, 3]
# selectとfilter、内部実装は同じみたい

[1] pry(main)> require 'pry-doc'
=> true

[2] pry(main)> show-source Array#select

From: array.c (C Method):
Owner: Array
Visibility: public
Signature: select()
Number of lines: 15

static VALUE
rb_ary_select(VALUE ary)
{
    VALUE result;
    long i;

    RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
    result = rb_ary_new2(RARRAY_LEN(ary));
    for (i = 0; i < RARRAY_LEN(ary); i++) {
        if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) {
            rb_ary_push(result, rb_ary_elt(ary, i));
        }
    }
    return result;
}

[3] pry(main)> show-source Array#filter

From: array.c (C Method):
Owner: Array
Visibility: public
Signature: filter()
Number of lines: 15

static VALUE
rb_ary_select(VALUE ary)
{
    VALUE result;
    long i;

    RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
    result = rb_ary_new2(RARRAY_LEN(ary));
    for (i = 0; i < RARRAY_LEN(ary); i++) {
        if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) {
            rb_ary_push(result, rb_ary_elt(ary, i));
        }
    }
    return result;
}
select! {|item| block } -> self | nil
select! -> Enumerator
filter! {|item| block } -> self | nil
filter! -> Enumerator
  • ブロックがfalseを返した要素を自身から削除する
  • 変更があった場合はselfを、変更がなかった場合はnilを返す
    • この挙動は絶対勘違いする
  • ブロックが与えられなかった場合は、自身とselect!、filter!から生成したEnumeratorオブジェクトを返す
irb(main):001:0> arr = [1, 2, 3]
=> [1, 2, 3]

irb(main):002:0> enum = arr.select!
=> #<Enumerator: [1, 2, 3]:select!>

irb(main):003:0> arr[1] = 10
=> 10

irb(main):004:0> enum
=> #<Enumerator: [1, 10, 3]:select!>

irb(main):005:0> enum.each {|v| v > 8 }
=> [10]

irb(main):006:0> arr
=> [10]

irb(main):007:0> enum.each {|v| v > 8 }
=> nil

irb(main):008:0> arr
=> [10]

irb(main):009:0> enum.each {|v| v > 15 }
=> []

irb(main):010:0> arr
=> []
irb(main):001:0> arr = [1, 2, 3]
=> [1, 2, 3]

irb(main):002:0> arr.select! {|v| v > 1 }
=> [2, 3]

irb(main):003:0> arr
=> [2, 3]

irb(main):004:0> arr.select! {|v| v > 0 }
=> nil

irb(main):005:0> arr
=> [2, 3]

irb(main):006:0> arr.select! {|v| v > 10 }
=> []

irb(main):007:0> arr
=> []

メモ

  • !付きの挙動、変更がなかったらnilを返すというのは覚えておきたい