Spec-Zone.ru › Ruby 3.1

модуль Enumerable

Что здесь

Модуль Enumerable предоставляет методы, полезные для класса коллекций для:

  • Запроса

  • Извлечения

  • Поиска

  • Сортировки

  • Итерации

  • И многое другое…

Методы для запроса

Эти методы возвращают информацию об Enumerable, помимо самих элементов:

include?, member?

Возвращает true если self == object, false в противном случае.

all?

Возвращает true если все элементы удовлетворяют заданному критерию; false в противном случае.

any?

Возвращает true если хотя бы один элемент удовлетворяет заданному критерию; false в противном случае.

none?

Возвращает true если ни один элемент не удовлетворяет заданному критерию; false в противном случае.

one?

Возвращает true если ровно один элемент удовлетворяет заданному критерию; false в противном случае.

count

Возвращает количество элементов, исходя из аргумента или блока критерия, если он задан.

tally

Возвращает новую Hash, содержащую количество вхождений каждого элемента.

Методы для извлечения

Эти методы возвращают записи из Enumerable, не изменяя его:

Начальные, конечные или все элементы:

entries, to_a

Возвращает все элементы.

first

Возвращает первый элемент или начальные элементы.

take

Возвращает заданное число начальных элементов.

drop

Возвращает заданное число конечных элементов.

take_while

Возвращает начальные элементы, как указано заданным блоком.

drop_while

Возвращает конечные элементы, как указано заданным блоком.

Минимальное и максимальное значение элементов:

min

Возвращает элементы с наименьшими значениями среди элементов, как определено <=> или заданным блоком.

max

Возвращает элементы с наибольшими значениями среди элементов, как определено <=> или заданным блоком.

minmax

Возвращает массив из 2 элементов, содержащий наименьший и наибольший элементы.

min_by

Возвращает наименьший элемент, как определено заданным блоком.

max_by

Возвращает наибольший элемент, как определено заданным блоком.

minmax_by

Возвращает наименьший и наибольший элементы, как определено заданным блоком.

Группы, срезы и разделения:

group_by

Возвращает Hash, который разделяет элементы на группы.

partition

Возвращает элементы, разделенные на два новых массива, как определено заданным блоком.

slice_after

Возвращает новый Enumerator, записи которого представляют собой разбиение self, основанное либо на заданном object или заданном блоке.

slice_before

Возвращает новый Enumerator, записи которого представляют собой разбиение self, основанное либо на заданном object или заданном блоке.

slice_when

Возвращает новый Enumerator, записи которого представляют собой разбиение self на основе заданного блока.

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

Вызывает блок с каждым последующим элементом в обратном порядке.

Другие методы

map, collect

Возвращает объекты, возвращаемые блоком.

filter_map

Возвращает истинные объекты, возвращаемые блоком.

flat_map, collect_concat

Возвращает уплощенные объекты, возвращаемые блоком.

grep

Возвращает элементы, выбранные заданным объектом или объектами, возвращаемыми заданным блоком.

grep_v

Возвращает элементы, выбранные заданным объектом или объектами, возвращаемыми заданным блоком.

reduce, inject

Возвращает объект, образованный путем объединения всех элементов.

sum

Возвращает сумму элементов, используя метод +++.

zip

Объединяет каждый элемент с элементами других перечислителей; возвращает n-кортежи или вызывает блок с каждым.

cycle

Вызывает блок с каждым элементом, циклически повторяясь.

Использование

Чтобы использовать модуль Enumerable в классе коллекции:

  • Включите его:

    include Enumerable
    
  • Реализуйте метод #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

Enumerable в классах ядра Ruby

В некоторых классах Ruby содержится Enumerable:

  • Array

  • Dir

  • Hash

  • IO

  • Range

  • Set

  • Struct

Практически все методы в Enumerable вызывают метод #each в включающем классе:

  • Hash#each возвращает следующую пару ключ-значение в виде массива из 2 элементов.

  • Struct#each возвращает следующую пару имя-значение в виде массива из 2 элементов.

  • Для других классов выше, #each возвращает следующий объект из коллекции.

О примерах

Примеры кода для методов Enumerable:

  • Всегда демонстрируют использование одного или нескольких классов, подобных массиву (часто сам массив).

  • Иногда демонстрируют использование класса, подобного хэшу. Однако для некоторых методов такое использование не имеет смысла, и поэтому оно не показано. Пример: tally найдёт ровно по одному экземпляру каждого элемента хэша.

Методы публичного экземпляра

all? → true или false Показать исходный код
all?(pattern) → true или false
all? {|element| ... } → true или false
static VALUE
enum_all(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo = MEMO_ENUM_NEW(Qtrue);
    WARN_UNUSED_BLOCK(argc);
    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(all), (VALUE)memo);
    return memo->v1;
}

Возвращает, соответствуют ли все элементы заданному критерию.

Без аргумента и без блока возвращает, являются ли все элементы истинными:

(1..4).all?           # => true
%w[a b c d].all?      # => true
[1, 2, nil].all?      # => false
['a','b', false].all? # => false
[].all?               # => true

С аргументом pattern и без блока, возвращает, верно ли, что для каждого элемента element, pattern === element:

(1..4).all?(Integer)                 # => true
(1..4).all?(Numeric)                 # => true
(1..4).all?(Float)                   # => false
%w[bar baz bat bam].all?(/ba/)       # => true
%w[bar baz bat bam].all?(/bar/)      # => false
%w[bar baz bat bam].all?('ba')       # => false
{foo: 0, bar: 1, baz: 2}.all?(Array) # => true
{foo: 0, bar: 1, baz: 2}.all?(Hash)  # => false
[].all?(Integer)                     # => true

С заданным блоком, возвращает, возвращает ли блок истинное значение для каждого элемента:

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

Связанные: any?, none? one?.

any? → true или false Показать исходный код
any?(pattern) → true или false
any? {|element| ... } → true или false
static VALUE
enum_any(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo = MEMO_ENUM_NEW(Qfalse);
    WARN_UNUSED_BLOCK(argc);
    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(any), (VALUE)memo);
    return memo->v1;
}

Возвращает, соответствует ли хоть один элемент заданному критерию.

Без аргумента и без блока возвращает, является ли хоть один элемент истинным:

(1..4).any?          # => true
%w[a b c d].any?     # => true
[1, false, nil].any? # => true
[].any?              # => false

С аргументом pattern и без блока, возвращает, верно ли, что для любого элемента element, pattern === element:

[nil, false, 0].any?(Integer)        # => true
[nil, false, 0].any?(Numeric)        # => true
[nil, false, 0].any?(Float)          # => false
%w[bar baz bat bam].any?(/m/)        # => true
%w[bar baz bat bam].any?(/foo/)      # => false
%w[bar baz bat bam].any?('ba')       # => false
{foo: 0, bar: 1, baz: 2}.any?(Array) # => true
{foo: 0, bar: 1, baz: 2}.any?(Hash)  # => false
[].any?(Integer)                     # => false

С заданным блоком, возвращает, возвращает ли блок истинное значение для любого элемента:

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

Связанные: all?, none?, one?.

chain(*enums) → перечислитель Показать исходный код
static VALUE
enum_chain(int argc, VALUE *argv, VALUE obj)
{
    VALUE enums = rb_ary_new_from_values(1, &obj);
    rb_ary_cat(enums, argv, argc);
    return new_enum_chain(enums);
}

Возвращает объект перечислителя, сгенерированный из этого перечислителя и перечисленных перечислителей.

e = (1..3).chain([4, 5])
e.to_a #=> [1, 2, 3, 4, 5]
chunk {|array| ... } → перечислитель Показать исходный код
static VALUE
enum_chunk(VALUE enumerable)
{
    VALUE enumerator;

    RETURN_SIZED_ENUMERATOR(enumerable, 0, 0, enum_size);

    enumerator = rb_obj_alloc(rb_cEnumerator);
    rb_ivar_set(enumerator, id_chunk_enumerable, enumerable);
    rb_ivar_set(enumerator, id_chunk_categorize, rb_block_proc());
    rb_block_call(enumerator, idInitialize, 0, 0, chunk_i, enumerator);
    return enumerator;
}

Каждый элемент возвращаемого перечислителя — это массив из 2 элементов, состоящий из:

  • Значения, возвращенного блоком.

  • Массива («группа») содержащего элемент, для которого было возвращено это значение, и все последующие элементы, для которых блок вернул то же значение:

Так что:

  • Каждое значение возвращаемое блоком, отличное от предыдущего, начинает новую группу.

  • Каждое значение возвращаемое блоком, такое же, как предыдущее, продолжает ту же группу.

Пример:

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

Метод chunk особенно полезен для перечислителя, который уже отсортирован. В этом примере подсчитывается количество слов для каждой начальной буквы в большом массиве слов:

# Get sorted words from a web page.
url = 'https://raw.githubusercontent.com/eneko/data-repository/master/data/words.txt'
words = URI::open(url).readlines
# Make chunks, one for each letter.
e = words.chunk {|word| word.upcase[0] } # => #<Enumerator: ...>
# Display 'A' through 'F'.
e.each {|c, words| p [c, words.length]; break if c == 'F' }

Вывод:

["A", 17096]
["B", 11070]
["C", 19901]
["D", 10896]
["E", 8736]
["F", 6860]

Вы можете использовать специальный символ :_alone для принудительного помещения элемента в отдельную группу:

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

Например, вы можете поместить каждую строку, содержащую URL, в свою собственную группу:

pattern = /http/
open(filename) { |f|
  f.chunk { |line| line =~ pattern ? :_alone : true }.each { |key, lines|
    pp lines
  }
}

Вы можете использовать специальный символ :_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]]]

Обратите внимание, что разделитель не заканчивает группу:

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

Например, последовательность дефисов в журнале svn может быть устранена следующим образом:

sep = "-"*72 + "\n"
IO.popen("svn log README") { |f|
  f.chunk { |line|
    line != sep || nil
  }.each { |_, lines|
    pp lines
  }
}
#=> ["r20018 | knu | 2008-10-29 13:20:42 +0900 (Wed, 29 Oct 2008) | 2 lines\n",
#    "\n",
#    "* README, README.ja: Update the portability section.\n",
#    "\n"]
#   ["r16725 | knu | 2008-05-31 23:34:23 +0900 (Sat, 31 May 2008) | 2 lines\n",
#    "\n",
#    "* README, README.ja: Add a note about default C flags.\n",
#    "\n"]
#   ...

Абзацы, разделенные пустыми строками, могут быть обработаны следующим образом:

File.foreach("README").chunk { |line|
  /\A\s*\z/ !~ line || nil
}.each { |_, lines|
  pp lines
}
chunk_while {|element, next_element| ... } → перечислитель Показать исходный код
static VALUE
enum_chunk_while(VALUE enumerable)
{
    VALUE enumerator;
    VALUE pred;

    pred = rb_block_proc();

    enumerator = rb_obj_alloc(rb_cEnumerator);
    rb_ivar_set(enumerator, id_slicewhen_enum, enumerable);
    rb_ivar_set(enumerator, id_slicewhen_pred, pred);
    rb_ivar_set(enumerator, id_slicewhen_inverted, Qtrue);

    rb_block_call(enumerator, idInitialize, 0, 0, slicewhen_i, enumerator);
    return enumerator;
}

Возвращаемый Enumerator использует блок для разбиения элементов на массивы («группы»); вызывает блок с каждым элементом и его преемником; начинает новую группу только в том случае, если блок возвращает истинное значение:

Пример:

a = [1, 2, 4, 9, 10, 11, 12, 15, 16, 19, 20, 21]
e = a.chunk_while {|i, j| j == i + 1 }
e.each {|array| p array }

Вывод:

[1, 2]
[4]
[9, 10, 11, 12]
[15, 16]
[19, 20, 21]
collect -> перечислитель Показать исходный код
static VALUE
enum_collect(VALUE obj)
{
    VALUE ary;
    int min_argc, max_argc;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    ary = rb_ary_new();
    min_argc = rb_block_min_max_arity(&max_argc);
    rb_lambda_call(obj, id_each, 0, 0, collect_i, min_argc, max_argc, ary);

    return ary;
}

Возвращает массив объектов, возвращенных блоком.

С заданным блоком вызывает блок с последовательными элементами; возвращает массив объектов, возвращенных блоком:

(0..4).map {|i| i*i }                               # => [0, 1, 4, 9, 16]
{foo: 0, bar: 1, baz: 2}.map {|key, value| value*2} # => [0, 2, 4]

Без заданного блока возвращает перечислитель.

Также псевдоним: map
collect_concat()

Возвращает массив сглаженных объектов, возвращенных блоком.

С заданным блоком вызывает блок с последовательными элементами; возвращает сглаженный массив объектов, возвращенных блоком:

[0, 1, 2, 3].flat_map {|element| -element }                    # => [0, -1, -2, -3]
[0, 1, 2, 3].flat_map {|element| [element, -element] }         # => [0, 0, 1, -1, 2, -2, 3, -3]
[[0, 1], [2, 3]].flat_map {|e| e + [100] }                     # => [0, 1, 100, 2, 3, 100]
{foo: 0, bar: 1, baz: 2}.flat_map {|key, value| [key, value] } # => [:foo, 0, :bar, 1, :baz, 2]

Без заданного блока возвращает перечислитель.

Псевдоним: collect_concat.

Псевдоним для: flat_map
compact → массив Показать исходный код
static VALUE
enum_compact(VALUE obj)
{
    VALUE ary;

    ary = rb_ary_new();
    rb_block_call(obj, id_each, 0, 0, compact_i, ary);

    return ary;
}

Возвращает массив всех элементов, не являющихся nil:

a = [nil, 0, nil, 'a', false, nil, false, nil, 'a', nil, 0, nil]
a.compact # => [0, "a", false, false, "a", 0]
count → целое число Показать исходный код
count(object) → целое число
count {|element| ... } → целое число
static VALUE
enum_count(int argc, VALUE *argv, VALUE obj)
{
    VALUE item = Qnil;
    struct MEMO *memo;
    rb_block_call_func *func;

    if (argc == 0) {
        if (rb_block_given_p()) {
            func = count_iter_i;
        }
        else {
            func = count_all_i;
        }
    }
    else {
        rb_scan_args(argc, argv, "1", &item);
        if (rb_block_given_p()) {
            rb_warn("given block not used");
        }
        func = count_i;
    }

    memo = MEMO_NEW(item, 0, 0);
    rb_block_call(obj, id_each, 0, 0, func, (VALUE)memo);
    return imemo_count_value(memo);
}

Возвращает количество элементов, основанное на критериях аргумента или блока, если они заданы.

Без аргумента и без блока возвращает количество элементов:

[0, 1, 2].count                # => 3
{foo: 0, bar: 1, baz: 2}.count # => 3

С заданным аргументом object возвращает количество элементов, которые == к object:

[0, 1, 2, 1].count(1)           # => 2

С заданным блоком вызывает блок с каждым элементом и возвращает количество элементов, для которых блок возвращает истинное значение:

[0, 1, 2, 3].count {|element| element < 2}              # => 2
{foo: 0, bar: 1, baz: 2}.count {|key, value| value < 2} # => 2
cycle(n = nil) {|element| ...} → nil Показать исходный код
cycle(n = nil) → перечислитель
static VALUE
enum_cycle(int argc, VALUE *argv, VALUE obj)
{
    VALUE ary;
    VALUE nv = Qnil;
    long n, i, len;

    rb_check_arity(argc, 0, 1);

    RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_cycle_size);
    if (!argc || NIL_P(nv = argv[0])) {
        n = -1;
    }
    else {
        n = NUM2LONG(nv);
        if (n <= 0) return Qnil;
    }
    ary = rb_ary_new();
    RBASIC_CLEAR_CLASS(ary);
    rb_block_call(obj, id_each, 0, 0, cycle_i, ary);
    len = RARRAY_LEN(ary);
    if (len == 0) return Qnil;
    while (n < 0 || 0 < --n) {
        for (i=0; i<len; i++) {
            enum_yield_array(RARRAY_AREF(ary, i));
        }
    }
    return Qnil;
}

При вызове с положительным целым аргументом n и блоком, вызывает блок с каждым элементом, а затем делает это снова, пока не выполнит это n раз; возвращает nil:

a = []
(1..4).cycle(3) {|element| a.push(element) } # => nil
a # => [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]
a = []
('a'..'d').cycle(2) {|element| a.push(element) }
a # => ["a", "b", "c", "d", "a", "b", "c", "d"]
a = []
{foo: 0, bar: 1, baz: 2}.cycle(2) {|element| a.push(element) }
a # => [[:foo, 0], [:bar, 1], [:baz, 2], [:foo, 0], [:bar, 1], [:baz, 2]]

Если счетчик равен нулю или отрицателен, блок не вызывается.

Если при вызове с блоком n равен nil, цикл продолжается бесконечно.

Если блок не указан, возвращает Enumerator.

detect(*args)

Возвращает первый элемент, для которого блок возвращает истинное значение.

При наличии блока вызывает блок с последовательными элементами коллекции; возвращает первый элемент, для которого блок возвращает истинное значение:

(0..9).find {|element| element > 2}                # => 3

Если такой элемент не найден, вызывает if_none_proc и возвращает его возвращаемое значение.

(0..9).find(proc {false}) {|element| element > 12} # => false
{foo: 0, bar: 1, baz: 2}.find {|key, value| key.start_with?('b') }            # => [:bar, 1]
{foo: 0, bar: 1, baz: 2}.find(proc {[]}) {|key, value| key.start_with?('c') } # => []

Без блока возвращает перечислитель.

Псевдоним для: find
drop(n) → массив Показать исходный код
static VALUE
enum_drop(VALUE obj, VALUE n)
{
    VALUE result;
    struct MEMO *memo;
    long len = NUM2LONG(n);

    if (len < 0) {
        rb_raise(rb_eArgError, "attempt to drop negative size");
    }

    result = rb_ary_new();
    memo = MEMO_NEW(result, 0, len);
    rb_block_call(obj, id_each, 0, 0, drop_i, (VALUE)memo);
    return result;
}

Для положительного целого числа n, возвращает массив, содержащий все элементы, кроме первых n элементов:

r = (1..4)
r.drop(3)  # => [4]
r.drop(2)  # => [3, 4]
r.drop(1)  # => [2, 3, 4]
r.drop(0)  # => [1, 2, 3, 4]
r.drop(50) # => []

h = {foo: 0, bar: 1, baz: 2, bat: 3}
h.drop(2) # => [[:baz, 2], [:bat, 3]]
drop_while {|element| ... } → массив Показать исходный код
drop_while → перечислитель
static VALUE
enum_drop_while(VALUE obj)
{
    VALUE result;
    struct MEMO *memo;

    RETURN_ENUMERATOR(obj, 0, 0);
    result = rb_ary_new();
    memo = MEMO_NEW(result, 0, FALSE);
    rb_block_call(obj, id_each, 0, 0, drop_while_i, (VALUE)memo);
    return result;
}

Вызывает блок с последовательными элементами до тех пор, пока блок возвращает истинное значение; возвращает массив всех элементов после этой точки:

(1..4).drop_while{|i| i < 3 } # => [3, 4]
h = {foo: 0, bar: 1, baz: 2}
a = h.drop_while{|element| key, value = *element; value < 2 }
a # => [[:baz, 2]]

Без блока возвращает Enumerator.

each_cons(n) { ... } → self Показать исходный код
each_cons(n) → enumerator
static VALUE
enum_each_cons(VALUE obj, VALUE n)
{
    long size = NUM2LONG(n);
    struct MEMO *memo;
    int arity;

    if (size <= 0) rb_raise(rb_eArgError, "invalid size");
    RETURN_SIZED_ENUMERATOR(obj, 1, &n, enum_each_cons_size);
    arity = rb_block_arity();
    if (enum_size_over_p(obj, size)) return obj;
    memo = MEMO_NEW(rb_ary_new2(size), dont_recycle_block_arg(arity), size);
    rb_block_call(obj, id_each, 0, 0, each_cons_i, (VALUE)memo);

    return obj;
}

Вызывает блок для каждой последовательной перекрывающейся n-кортежа элементов; возвращает self:

a = []
(1..5).each_cons(3) {|element| a.push(element) }
a # => [[1, 2, 3], [2, 3, 4], [3, 4, 5]]

a = []
h = {foo: 0,  bar: 1, baz: 2, bam: 3}
h.each_cons(2) {|element| a.push(element) }
a # => [[[:foo, 0], [:bar, 1]], [[:bar, 1], [:baz, 2]], [[:baz, 2], [:bam, 3]]]

Без блока возвращает Enumerator.

each_entry(*args) {|element| ... } → self Показать исходный код
each_entry(*args) → enumerator
static VALUE
enum_each_entry(int argc, VALUE *argv, VALUE obj)
{
    RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);
    rb_block_call(obj, id_each, argc, argv, each_val_i, 0);
    return obj;
}

Вызывает заданный блок для каждого элемента, преобразуя несколько значений из yield в массив; возвращает self:

a = []
(1..4).each_entry {|element| a.push(element) } # => 1..4
a # => [1, 2, 3, 4]

a = []
h = {foo: 0, bar: 1, baz:2}
h.each_entry {|element| a.push(element) }
# => {:foo=>0, :bar=>1, :baz=>2}
a # => [[:foo, 0], [:bar, 1], [:baz, 2]]

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

Вывод:

1
[1, 2]
nil

Без блока возвращает Enumerator.

each_slice(n) { ... } → self Показать исходный код
each_slice(n) → enumerator
static VALUE
enum_each_slice(VALUE obj, VALUE n)
{
    long size = NUM2LONG(n);
    VALUE ary;
    struct MEMO *memo;
    int arity;

    if (size <= 0) rb_raise(rb_eArgError, "invalid slice size");
    RETURN_SIZED_ENUMERATOR(obj, 1, &n, enum_each_slice_size);
    size = limit_by_enum_size(obj, size);
    ary = rb_ary_new2(size);
    arity = rb_block_arity();
    memo = MEMO_NEW(ary, dont_recycle_block_arg(arity), size);
    rb_block_call(obj, id_each, 0, 0, each_slice_i, (VALUE)memo);
    ary = memo->v1;
    if (RARRAY_LEN(ary) > 0) rb_yield(ary);

    return obj;
}

Вызывает блок для каждого последовательного непересекающегося n-кортежа элементов; возвращает self:

a = []
(1..10).each_slice(3) {|tuple| a.push(tuple) }
a # => [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

a = []
h = {foo: 0, bar: 1, baz: 2, bat: 3, bam: 4}
h.each_slice(2) {|tuple| a.push(tuple) }
a # => [[[:foo, 0], [:bar, 1]], [[:baz, 2], [:bat, 3]], [[:bam, 4]]]

Без блока возвращает Enumerator.

each_with_index(*args) {|element, i| ..... } → self Показать исходный код
each_with_index(*args) → enumerator
static VALUE
enum_each_with_index(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo;

    RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);

    memo = MEMO_NEW(0, 0, 0);
    rb_block_call(obj, id_each, argc, argv, each_with_index_i, (VALUE)memo);
    return obj;
}

При наличии блока вызывает блок с каждым элементом и его индексом; возвращает self:

h = {}
(1..4).each_with_index {|element, i| h[element] = i } # => 1..4
h # => {1=>0, 2=>1, 3=>2, 4=>3}

h = {}
%w[a b c d].each_with_index {|element, i| h[element] = i }
# => ["a", "b", "c", "d"]
h # => {"a"=>0, "b"=>1, "c"=>2, "d"=>3}

a = []
h = {foo: 0, bar: 1, baz: 2}
h.each_with_index {|element, i| a.push([i, element]) }
# => {:foo=>0, :bar=>1, :baz=>2}
a # => [[0, [:foo, 0]], [1, [:bar, 1]], [2, [:baz, 2]]]

Без блока возвращает Enumerator.

each_with_object(object) { |(*args), memo_object| ... } → object Показать исходный код
each_with_object(object) → enumerator
static VALUE
enum_each_with_object(VALUE obj, VALUE memo)
{
    RETURN_SIZED_ENUMERATOR(obj, 1, &memo, enum_size);

    rb_block_call(obj, id_each, 0, 0, each_with_object_i, memo);

    return memo;
}

Вызывает блок один раз для каждого элемента, передавая элемент и указанный объект:

(1..4).each_with_object([]) {|i, a| a.push(i**2) } # => [1, 4, 9, 16]
h.each_with_object({}) {|element, h| k, v = *element; h[v] = k }
# => {0=>:foo, 1=>:bar, 2=>:baz}

Без блока возвращает Enumerator.

entries(*args)

Возвращает массив, содержащий элементы в self:

(0..4).to_a # => [0, 1, 2, 3, 4]

Enumerable#entries — псевдоним для Enumerable#to_a.

Псевдоним для: to_a
filter()

Возвращает массив, содержащий элементы, выбранные блоком.

При наличии блока вызывает блок с последовательными элементами; возвращает массив тех элементов, для которых блок возвращает истинное значение:

(0..9).select {|element| element % 3 == 0 } # => [0, 3, 6, 9]
a = {foo: 0, bar: 1, baz: 2}.select {|key, value| key.start_with?('b') }
a # => {:bar=>1, :baz=>2}

Без блока возвращает Enumerator.

Связанно с: reject.

Псевдоним для: find_all
filter_map {|element| ... } → array Показать исходный код
filter_map → enumerator
static VALUE
enum_filter_map(VALUE obj)
{
    VALUE ary;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    ary = rb_ary_new();
    rb_block_call(obj, id_each, 0, 0, filter_map_i, ary);

    return ary;
}

Возвращает массив, содержащий истинные значения, возвращённые блоком.

При наличии блока вызывает блок с последовательными элементами; возвращает массив, содержащий каждое истинное значение, возвращённое блоком:

(0..9).filter_map {|i| i * 2 if i.even? }                              # => [0, 4, 8, 12, 16]
{foo: 0, bar: 1, baz: 2}.filter_map {|key, value| key if value.even? } # => [:foo, :baz]

При отсутствии блока возвращает Enumerator.

find(if_none_proc = nil) {|element| ... } → object or nil Показать исходный код
find(if_none_proc = nil) → enumerator
static VALUE
enum_find(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo;
    VALUE if_none;

    if_none = rb_check_arity(argc, 0, 1) ? argv[0] : Qnil;
    RETURN_ENUMERATOR(obj, argc, argv);
    memo = MEMO_NEW(Qundef, 0, 0);
    rb_block_call(obj, id_each, 0, 0, find_i, (VALUE)memo);
    if (memo->u3.cnt) {
        return memo->v1;
    }
    if (!NIL_P(if_none)) {
        return rb_funcallv(if_none, id_call, 0, 0);
    }
    return Qnil;
}

Возвращает первый элемент, для которого блок возвращает истинное значение.

При наличии блока вызывает блок с последовательными элементами коллекции; возвращает первый элемент, для которого блок возвращает истинное значение:

(0..9).find {|element| element > 2}                # => 3

Если такой элемент не найден, вызывает if_none_proc и возвращает его возвращаемое значение.

(0..9).find(proc {false}) {|element| element > 12} # => false
{foo: 0, bar: 1, baz: 2}.find {|key, value| key.start_with?('b') }            # => [:bar, 1]
{foo: 0, bar: 1, baz: 2}.find(proc {[]}) {|key, value| key.start_with?('c') } # => []

Без блока возвращает Enumerator.

Также алиасируется как: detect
find_all -> enumerator Показать исходный код
static VALUE
enum_find_all(VALUE obj)
{
    VALUE ary;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    ary = rb_ary_new();
    rb_block_call(obj, id_each, 0, 0, find_all_i, ary);

    return ary;
}

Возвращает массив, содержащий элементы, выбранные блоком.

При наличии блока вызывает блок с последовательными элементами; возвращает массив тех элементов, для которых блок возвращает истинное значение:

(0..9).select {|element| element % 3 == 0 } # => [0, 3, 6, 9]
a = {foo: 0, bar: 1, baz: 2}.select {|key, value| key.start_with?('b') }
a # => {:bar=>1, :baz=>2}

Без блока возвращает Enumerator.

Связанно с: reject.

Также алиасируется как: select, filter
find_index(object) → integer or nil Показать исходный код
find_index {|element| ... } → integer or nil
find_index → enumerator
static VALUE
enum_find_index(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo;  /* [return value, current index, ] */
    VALUE condition_value = Qnil;
    rb_block_call_func *func;

    if (argc == 0) {
        RETURN_ENUMERATOR(obj, 0, 0);
        func = find_index_iter_i;
    }
    else {
        rb_scan_args(argc, argv, "1", &condition_value);
        if (rb_block_given_p()) {
            rb_warn("given block not used");
        }
        func = find_index_i;
    }

    memo = MEMO_NEW(Qnil, condition_value, 0);
    rb_block_call(obj, id_each, 0, 0, func, (VALUE)memo);
    return memo->v1;
}

Возвращает индекс первого элемента, удовлетворяющего заданному критерию, или nil, если такой элемент не найден.

При передаче аргумента object, возвращает индекс первого элемента, который == object:

['a', 'b', 'c', 'b'].find_index('b') # => 1

При наличии блока вызывает блок с последовательными элементами; возвращает индекс первого элемента, для которого блок возвращает истинное значение:

['a', 'b', 'c', 'b'].find_index {|element| element.start_with?('b') } # => 1
{foo: 0, bar: 1, baz: 2}.find_index {|key, value| value > 1 }         # => 2

Без аргумента и блока возвращает Enumerator.

first → element or nil Показать исходный код
first(n) → array
static VALUE
enum_first(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo;
    rb_check_arity(argc, 0, 1);
    if (argc > 0) {
        return enum_take(obj, argv[0]);
    }
    else {
        memo = MEMO_NEW(Qnil, 0, 0);
        rb_block_call(obj, id_each, 0, 0, first_i, (VALUE)memo);
        return memo->v1;
    }
}

Возвращает первый элемент или элементы.

Без аргумента возвращает первый элемент или nil, если такового нет:

(1..4).first                   # => 1
%w[a b c].first                # => "a"
{foo: 1, bar: 1, baz: 2}.first # => [:foo, 1]
[].first                       # => nil

С целым аргументом n, возвращает массив, содержащий первые n существующие элементы:

(1..4).first(2)                   # => [1, 2]
%w[a b c d].first(3)              # => ["a", "b", "c"]
%w[a b c d].first(50)             # => ["a", "b", "c", "d"]
{foo: 1, bar: 1, baz: 2}.first(2) # => [[:foo, 1], [:bar, 1]]
[].first(2)                       # => []
flat_map {|element| ... } → array Показать исходный код
flat_map → enumerator
static VALUE
enum_flat_map(VALUE obj)
{
    VALUE ary;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    ary = rb_ary_new();
    rb_block_call(obj, id_each, 0, 0, flat_map_i, ary);

    return ary;
}

Возвращает массив с уплощенными объектами, возвращёнными блоком.

При наличии блока вызывает блок с последовательными элементами; возвращает уплощённый массив объектов, возвращённых блоком:

[0, 1, 2, 3].flat_map {|element| -element }                    # => [0, -1, -2, -3]
[0, 1, 2, 3].flat_map {|element| [element, -element] }         # => [0, 0, 1, -1, 2, -2, 3, -3]
[[0, 1], [2, 3]].flat_map {|e| e + [100] }                     # => [0, 1, 100, 2, 3, 100]
{foo: 0, bar: 1, baz: 2}.flat_map {|key, value| [key, value] } # => [:foo, 0, :bar, 1, :baz, 2]

Без блока возвращает Enumerator.

Псевдоним: collect_concat.

Также алиасируется как: collect_concat
grep(pattern) → array Показать исходный код
grep(pattern) {|element| ... } → array
static VALUE
enum_grep(VALUE obj, VALUE pat)
{
    return enum_grep0(obj, pat, Qtrue);
}

Возвращает массив объектов, основанных на элементах self, которые соответствуют заданному шаблону.

Без блока возвращает массив, содержащий каждый элемент, для которого pattern === element является true:

a = ['foo', 'bar', 'car', 'moo']
a.grep(/ar/)                   # => ["bar", "car"]
(1..10).grep(3..8)             # => [3, 4, 5, 6, 7, 8]
['a', 'b', 0, 1].grep(Integer) # => [0, 1]

При наличии блока вызывает блок с каждым соответствующим элементом и возвращает массив, содержащий каждый объект, возвращённый блоком:

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

Связанно с: grep_v.

grep_v(pattern) → массив Показать исходный код
grep_v(pattern) {|элемент| ... } → массив
static VALUE
enum_grep_v(VALUE obj, VALUE pat)
{
    return enum_grep0(obj, pat, Qfalse);
}

Возвращает массив объектов, основанный на элементах self, которые не соответствуют заданному шаблону.

Без блока возвращает массив, содержащий каждый элемент, для которого pattern === element не false:

a = ['foo', 'bar', 'car', 'moo']
a.grep_v(/ar/)                   # => ["foo", "moo"]
(1..10).grep_v(3..8)             # => [1, 2, 9, 10]
['a', 'b', 0, 1].grep_v(Integer) # => ["a", "b"]

С блоком вызывает блок с каждым элементом, не соответствующим шаблону, и возвращает массив, содержащий каждый объект, возвращённый блоком:

a = ['foo', 'bar', 'car', 'moo']
a.grep_v(/ar/) {|element| element.upcase } # => ["FOO", "MOO"]

Связанно с: grep.

group_by {|элемент| ... } → словарь Показать исходный код
group_by → перечислитель
static VALUE
enum_group_by(VALUE obj)
{
    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    return enum_hashify(obj, 0, 0, group_by_i);
}

С блоком возвращает словарь:

  • Каждый ключ — это возвращаемое значение из блока.

  • Каждое значение — это массив тех элементов, для которых блок вернул этот ключ.

Примеры:

g = (1..6).group_by {|i| i%3 }
g # => {1=>[1, 4], 2=>[2, 5], 0=>[3, 6]}
h = {foo: 0, bar: 1, baz: 0, bat: 1}
g = h.group_by {|key, value| value }
g # => {0=>[[:foo, 0], [:baz, 0]], 1=>[[:bar, 1], [:bat, 1]]}

Без блока возвращает Enumerator.

include?(объект) → true или false

Возвращает, является ли для любого элемента object == element:

(1..4).include?(2)                       # => true
(1..4).include?(5)                       # => false
(1..4).include?('2')                     # => false
%w[a b c d].include?('b')                # => true
%w[a b c d].include?('2')                # => false
{foo: 0, bar: 1, baz: 2}.include?(:foo)  # => true
{foo: 0, bar: 1, baz: 2}.include?('foo') # => false
{foo: 0, bar: 1, baz: 2}.include?(0)     # => false

Enumerable#member? — псевдоним для Enumerable#include?.

Псевдоним для: member?
inject(символ) → объект Показать исходный код
inject(начальное_операнд, символ) → объект
inject {|мемо, операнд| ... } → объект
inject(начальное_операнд) {|мемо, операнд| ... } → объект
static VALUE
enum_inject(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo;
    VALUE init, op;
    rb_block_call_func *iter = inject_i;
    ID id;

    switch (rb_scan_args(argc, argv, "02", &init, &op)) {
      case 0:
        init = Qundef;
        break;
      case 1:
        if (rb_block_given_p()) {
            break;
        }
        id = rb_check_id(&init);
        op = id ? ID2SYM(id) : init;
        init = Qundef;
        iter = inject_op_i;
        break;
      case 2:
        if (rb_block_given_p()) {
            rb_warning("given block not used");
        }
        id = rb_check_id(&op);
        if (id) op = ID2SYM(id);
        iter = inject_op_i;
        break;
    }

    if (iter == inject_op_i &&
        SYMBOL_P(op) &&
        RB_TYPE_P(obj, T_ARRAY) &&
        rb_method_basic_definition_p(CLASS_OF(obj), id_each)) {
        return ary_inject_op(obj, init, op);
    }

    memo = MEMO_NEW(init, Qnil, op);
    rb_block_call(obj, id_each, 0, 0, iter, (VALUE)memo);
    if (memo->v1 == Qundef) return Qnil;
    return memo->v1;
}

Возвращает объект, образованный из операндов с помощью:

  • Метода, имя которого указано в symbol.

  • Блока, которому передается каждый операнд.

С аргументом метода symbol, объединяет операнды, используя метод:

# Sum, without initial_operand.
(1..4).inject(:+)     # => 10
# Sum, with initial_operand.
(1..4).inject(10, :+) # => 20

С блоком передает каждый операнд в блок:

# Sum of squares, without initial_operand.
(1..4).inject {|sum, n| sum + n*n }    # => 30
# Sum of squares, with initial_operand.
(1..4).inject(2) {|sum, n| sum + n*n } # => 32

Операнды

Если аргумент initial_operand не указан, операндами для inject являются просто элементы self. Примеры вызовов и их операнды:

  • (1..4).inject(:+)

    [1, 2, 3, 4].

  • (1...4).inject(:+)

    [1, 2, 3].

  • ('a'..'d').inject(:+)

    ['a', 'b', 'c', 'd'].

  • ('a'...'d').inject(:+)

    ['a', 'b', 'c'].

Примеры с первым операндом (который является self.first) различных типов:

# Integer.
(1..4).inject(:+)                # => 10
# Float.
[1.0, 2, 3, 4].inject(:+)        # => 10.0
# Character.
('a'..'d').inject(:+)            # => "abcd"
# Complex.
[Complex(1, 2), 3, 4].inject(:+) # => (8+2i)

Если аргумент initial_operand указан, операндами для inject являются это значение плюс элементы self. Примеры вызовов и их операнды:

  • (1..4).inject(10, :+)

    [10, 1, 2, 3, 4].

  • (1...4).inject(10, :+)

    [10, 1, 2, 3].

  • ('a'..'d').inject('e', :+)

    ['e', 'a', 'b', 'c', 'd'].

  • ('a'...'d').inject('e', :+)

    ['e', 'a', 'b', 'c'].

Примеры с initial_operand различных типов:

# Integer.
(1..4).inject(2, :+)               # => 12
# Float.
(1..4).inject(2.0, :+)             # => 12.0
# String.
('a'..'d').inject('foo', :+)       # => "fooabcd"
# Array.
%w[a b c].inject(['x'], :push)     # => ["x", "a", "b", "c"]
# Complex.
(1..4).inject(Complex(2, 2), :+)   # => (12+2i)

Объединение заданным методом

Если аргумент имени метода symbol задан, операнды объединяются этим методом:

  • Первый и второй операнды объединяются.

  • Результат объединяется с третьим операндом.

  • Результат объединяется с четвёртым операндом.

  • И так далее.

Возвращаемое значение от inject — результат последнего объединения.

Этот вызов inject вычисляет сумму операндов:

(1..4).inject(:+) # => 10

Примеры с различными методами:

# Integer addition.
(1..4).inject(:+)                # => 10
# Integer multiplication.
(1..4).inject(:*)                # => 24
# Character range concatenation.
('a'..'d').inject('', :+)        # => "abcd"
# String array concatenation.
%w[foo bar baz].inject('', :+)   # => "foobarbaz"
# Hash update.
h = [{foo: 0, bar: 1}, {baz: 2}, {bat: 3}].inject(:update)
h # => {:foo=>0, :bar=>1, :baz=>2, :bat=>3}
# Hash conversion to nested arrays.
h = {foo: 0, bar: 1}.inject([], :push)
h # => [[:foo, 0], [:bar, 1]]

Объединение заданным блоком

Если задан блок, операнды передаются в блок:

  • Первый вызов передает первый и второй операнды.

  • Второй вызов передает результат первого вызова вместе с третьим операндом.

  • Третий вызов передает результат второго вызова вместе с четвёртым операндом.

  • И так далее.

Возвращаемое значение от inject — это возвращаемое значение последнего вызова блока.

Этот вызов inject даёт блок, который выводит мемо и элемент, а также суммирует элементы:

(1..4).inject do |memo, element|
  p "Memo: #{memo}; element: #{element}"
  memo + element
end # => 10

Вывод:

"Memo: 1; element: 2"
"Memo: 3; element: 3"
"Memo: 6; element: 4"

Enumerable#reduce — псевдоним для Enumerable#inject.

Также алиас для: reduce
lazy → ленивый_перечислитель Показать исходный код
static VALUE
enumerable_lazy(VALUE obj)
{
    VALUE result = lazy_to_enum_i(obj, sym_each, 0, 0, lazyenum_size, rb_keyword_given_p());
    /* Qfalse indicates that the Enumerator::Lazy has no method name */
    rb_ivar_set(result, id_method, Qfalse);
    return result;
}

Возвращает Enumerator::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
# show first ten pythagorean triples
p pythagorean_triples.take(10).force # take is lazy, so force is needed
p pythagorean_triples.first(10)      # first is eager
# show pythagorean triples less than 100
p pythagorean_triples.take_while { |*, z| z < 100 }.force
map {|элемент| ... } → массив
map → перечислитель

Возвращает массив объектов, возвращённых блоком.

С блоком вызывает блок с последовательными элементами; возвращает массив объектов, возвращённых блоком:

(0..4).map {|i| i*i }                               # => [0, 1, 4, 9, 16]
{foo: 0, bar: 1, baz: 2}.map {|key, value| value*2} # => [0, 2, 4]

Без блока возвращает перечислитель.

Псевдоним для: collect
max → элемент Показать исходный код
max(n) → массив
max {|a, b| ... } → элемент
max(n) {|a, b| ... } → массив
static VALUE
enum_max(int argc, VALUE *argv, VALUE obj)
{
    VALUE memo;
    struct max_t *m = NEW_CMP_OPT_MEMO(struct max_t, memo);
    VALUE result;
    VALUE num;

    if (rb_check_arity(argc, 0, 1) && !NIL_P(num = argv[0]))
       return rb_nmin_run(obj, num, 0, 1, 0);

    m->max = Qundef;
    m->cmp_opt.opt_methods = 0;
    m->cmp_opt.opt_inited = 0;
    if (rb_block_given_p()) {
        rb_block_call(obj, id_each, 0, 0, max_ii, (VALUE)memo);
    }
    else {
        rb_block_call(obj, id_each, 0, 0, max_i, (VALUE)memo);
    }
    result = m->max;
    if (result == Qundef) return Qnil;
    return result;
}

Возвращает элемент с максимальным значением по заданному критерию. Порядок равных элементов неопределён и может быть неустойчивым.

Без аргумента и без блока возвращает максимальный элемент, используя собственный метод <=> для сравнения:

(1..4).max                   # => 4
(-4..-1).max                 # => -1
%w[d c b a].max              # => "d"
{foo: 0, bar: 1, baz: 2}.max # => [:foo, 0]
[].max                       # => nil

С положительным целым аргументом n и без блока возвращает массив, содержащий первые n максимальных элементов, которые существуют:

(1..4).max(2)                   # => [4, 3]
(-4..-1).max(2)                # => [-1, -2]
%w[d c b a].max(2)              # => ["d", "c"]
{foo: 0, bar: 1, baz: 2}.max(2) # => [[:foo, 0], [:baz, 2]]
[].max(2)                       # => []

С блоком блок определяет максимальные элементы. Блок вызывается с двумя элементами a и b, и должен возвращать:

  • Отрицательное целое число, если a < b.

  • Ноль, если a == b.

  • Положительное целое число, если a > b.

С блоком и без аргумента возвращает максимальный элемент, как определено блоком:

%w[xxx x xxxx xx].max {|a, b| a.size <=> b.size } # => "xxxx"
h = {foo: 0, bar: 1, baz: 2}
h.max {|pair1, pair2| pair1[1] <=> pair2[1] }     # => [:baz, 2]
[].max {|a, b| a <=> b }                          # => nil

С блоком и положительным целым аргументом n возвращает массив, содержащий первые n максимальных элементов, как определено блоком.

%w[xxx x xxxx xx].max(2) {|a, b| a.size <=> b.size } # => ["xxxx", "xxx"]
h = {foo: 0, bar: 1, baz: 2}
h.max(2) {|pair1, pair2| pair1[1] <=> pair2[1] }
# => [[:baz, 2], [:bar, 1]]
[].max(2) {|a, b| a <=> b }                          # => []

Связанно с: min, minmax, max_by.

max_by {|элемент| ... } → элемент Показать исходный код
max_by(n) {|элемент| ... } → массив
max_by → перечислитель
max_by(n) → перечислитель
static VALUE
enum_max_by(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo;
    VALUE num;

    rb_check_arity(argc, 0, 1);

    RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);

    if (argc && !NIL_P(num = argv[0]))
        return rb_nmin_run(obj, num, 1, 1, 0);

    memo = MEMO_NEW(Qundef, Qnil, 0);
    rb_block_call(obj, id_each, 0, 0, max_by_i, (VALUE)memo);
    return memo->v2;
}

Возвращает элементы, для которых блок возвращает максимальные значения.

С блоком и без аргумента возвращает элемент, для которого блок возвращает максимальное значение:

(1..4).max_by {|element| -element }                    # => 1
%w[a b c d].max_by {|element| -element.ord }           # => "a"
{foo: 0, bar: 1, baz: 2}.max_by {|key, value| -value } # => [:foo, 0]
[].max_by {|element| -element }                        # => nil

С блоком и положительным целым аргументом n возвращает массив, содержащий n элементов, для которых блок возвращает максимальные значения:

(1..4).max_by(2) {|element| -element }
# => [1, 2]
%w[a b c d].max_by(2) {|element| -element.ord }
# => ["a", "b"]
{foo: 0, bar: 1, baz: 2}.max_by(2) {|key, value| -value }
# => [[:foo, 0], [:bar, 1]]
[].max_by(2) {|element| -element }
# => []

Возвращает Enumerator, если блок не задан.

Связанно с: max, minmax, min_by.

member?(object) -> true or false Показать исходный код
static VALUE
enum_member(VALUE obj, VALUE val)
{
    struct MEMO *memo = MEMO_NEW(val, Qfalse, 0);

    rb_block_call(obj, id_each, 0, 0, member_i, (VALUE)memo);
    return memo->v2;
}

Возвращает значение true, если для любого элемента object == element, в противном случае false:

(1..4).include?(2)                       # => true
(1..4).include?(5)                       # => false
(1..4).include?('2')                     # => false
%w[a b c d].include?('b')                # => true
%w[a b c d].include?('2')                # => false
{foo: 0, bar: 1, baz: 2}.include?(:foo)  # => true
{foo: 0, bar: 1, baz: 2}.include?('foo') # => false
{foo: 0, bar: 1, baz: 2}.include?(0)     # => false

Enumerable#member? является псевдонимом для Enumerable#include?.

Также алиасируется как: include?
min → элемент Показать исходный код
min(n) → массив
min {|a, b| ... } → элемент
min(n) {|a, b| ... } → массив
static VALUE
enum_min(int argc, VALUE *argv, VALUE obj)
{
    VALUE memo;
    struct min_t *m = NEW_CMP_OPT_MEMO(struct min_t, memo);
    VALUE result;
    VALUE num;

    if (rb_check_arity(argc, 0, 1) && !NIL_P(num = argv[0]))
       return rb_nmin_run(obj, num, 0, 0, 0);

    m->min = Qundef;
    m->cmp_opt.opt_methods = 0;
    m->cmp_opt.opt_inited = 0;
    if (rb_block_given_p()) {
        rb_block_call(obj, id_each, 0, 0, min_ii, memo);
    }
    else {
        rb_block_call(obj, id_each, 0, 0, min_i, memo);
    }
    result = m->min;
    if (result == Qundef) return Qnil;
    return result;
}

Возвращает элемент с минимальным значением согласно заданному критерию. Порядок элементов с одинаковым значением не определен и может быть неустойчивым.

Без аргумента и без блока возвращает минимальный элемент, используя собственный метод элементов <=> для сравнения:

(1..4).min                   # => 1
(-4..-1).min                 # => -4
%w[d c b a].min              # => "a"
{foo: 0, bar: 1, baz: 2}.min # => [:bar, 1]
[].min                       # => nil

С положительным целочисленным аргументом n, и без блока, возвращает массив, содержащий первые n минимальных элементов, которые существуют:

(1..4).min(2)                   # => [1, 2]
(-4..-1).min(2)                 # => [-4, -3]
%w[d c b a].min(2)              # => ["a", "b"]
{foo: 0, bar: 1, baz: 2}.min(2) # => [[:bar, 1], [:baz, 2]]
[].min(2)                       # => []

С блоком, блок определяет минимальные элементы. Блок вызывается с двумя элементами a и b, и должен возвращать:

  • Отрицательное целое число, если a < b.

  • Ноль, если a == b.

  • Положительное целое число, если a > b.

С блоком и без аргумента возвращает минимальный элемент, определяемый блоком:

%w[xxx x xxxx xx].min {|a, b| a.size <=> b.size } # => "x"
h = {foo: 0, bar: 1, baz: 2}
h.min {|pair1, pair2| pair1[1] <=> pair2[1] } # => [:foo, 0]
[].min {|a, b| a <=> b }                          # => nil

С блоком и положительным целочисленным аргументом n возвращает массив, содержащий первые n минимальных элементов, определяемых блоком.

%w[xxx x xxxx xx].min(2) {|a, b| a.size <=> b.size } # => ["x", "xx"]
h = {foo: 0, bar: 1, baz: 2}
h.min(2) {|pair1, pair2| pair1[1] <=> pair2[1] }
# => [[:foo, 0], [:bar, 1]]
[].min(2) {|a, b| a <=> b }                          # => []

Связанные: min_by, minmax, max.

min_by {|element| ... } → элемент Показать исходный код
min_by(n) {|element| ... } → массив
min_by → перечислитель
min_by(n) → перечислитель
static VALUE
enum_min_by(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo;
    VALUE num;

    rb_check_arity(argc, 0, 1);

    RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);

    if (argc && !NIL_P(num = argv[0]))
        return rb_nmin_run(obj, num, 1, 0, 0);

    memo = MEMO_NEW(Qundef, Qnil, 0);
    rb_block_call(obj, id_each, 0, 0, min_by_i, (VALUE)memo);
    return memo->v2;
}

Возвращает элементы, для которых блок возвращает минимальные значения.

С блоком и без аргумента возвращает элемент, для которого блок возвращает минимальное значение:

(1..4).min_by {|element| -element }                    # => 4
%w[a b c d].min_by {|element| -element.ord }           # => "d"
{foo: 0, bar: 1, baz: 2}.min_by {|key, value| -value } # => [:baz, 2]
[].min_by {|element| -element }                        # => nil

С блоком и положительным целочисленным аргументом n возвращает массив, содержащий n элементы, для которых блок возвращает минимальные значения:

(1..4).min_by(2) {|element| -element }
# => [4, 3]
%w[a b c d].min_by(2) {|element| -element.ord }
# => ["d", "c"]
{foo: 0, bar: 1, baz: 2}.min_by(2) {|key, value| -value }
# => [[:baz, 2], [:bar, 1]]
[].min_by(2) {|element| -element }
# => []

Возвращает Enumerator, если блок не задан.

Связанные: min, minmax, max_by.

minmax → [минимум, максимум] Показать исходный код
minmax {|a, b| ... } → [минимум, максимум]
static VALUE
enum_minmax(VALUE obj)
{
    VALUE memo;
    struct minmax_t *m = NEW_CMP_OPT_MEMO(struct minmax_t, memo);

    m->min = Qundef;
    m->last = Qundef;
    m->cmp_opt.opt_methods = 0;
    m->cmp_opt.opt_inited = 0;
    if (rb_block_given_p()) {
        rb_block_call(obj, id_each, 0, 0, minmax_ii, memo);
        if (m->last != Qundef)
            minmax_ii_update(m->last, m->last, m);
    }
    else {
        rb_block_call(obj, id_each, 0, 0, minmax_i, memo);
        if (m->last != Qundef)
            minmax_i_update(m->last, m->last, m);
    }
    if (m->min != Qundef) {
        return rb_assoc_new(m->min, m->max);
    }
    return rb_assoc_new(Qnil, Qnil);
}

Возвращает массив из 2 элементов, содержащий минимальный и максимальный элементы согласно заданному критерию. Порядок элементов с одинаковым значением не определен и может быть неустойчивым.

Без аргумента и без блока возвращает минимальный и максимальный элементы, используя собственный метод элементов <=> для сравнения:

(1..4).minmax                   # => [1, 4]
(-4..-1).minmax                 # => [-4, -1]
%w[d c b a].minmax              # => ["a", "d"]
{foo: 0, bar: 1, baz: 2}.minmax # => [[:bar, 1], [:foo, 0]]
[].minmax                       # => [nil, nil]

С блоком возвращает минимальный и максимальный элементы, определяемые блоком:

%w[xxx x xxxx xx].minmax {|a, b| a.size <=> b.size } # => ["x", "xxxx"]
h = {foo: 0, bar: 1, baz: 2}
h.minmax {|pair1, pair2| pair1[1] <=> pair2[1] }
# => [[:foo, 0], [:baz, 2]]
[].minmax {|a, b| a <=> b }                          # => [nil, nil]

Связанные: min, max, minmax_by.

minmax_by {|element| ... } → [минимум, максимум] Показать исходный код
minmax_by → перечислитель
static VALUE
enum_minmax_by(VALUE obj)
{
    VALUE memo;
    struct minmax_by_t *m = NEW_MEMO_FOR(struct minmax_by_t, memo);

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    m->min_bv = Qundef;
    m->max_bv = Qundef;
    m->min = Qnil;
    m->max = Qnil;
    m->last_bv = Qundef;
    m->last = Qundef;
    rb_block_call(obj, id_each, 0, 0, minmax_by_i, memo);
    if (m->last_bv != Qundef)
        minmax_by_i_update(m->last_bv, m->last_bv, m->last, m->last, m);
    m = MEMO_FOR(struct minmax_by_t, memo);
    return rb_assoc_new(m->min, m->max);
}

Возвращает массив из 2 элементов, содержащий элементы, для которых блок возвращает минимальное и максимальное значения:

(1..4).minmax_by {|element| -element }
# => [4, 1]
%w[a b c d].minmax_by {|element| -element.ord }
# => ["d", "a"]
{foo: 0, bar: 1, baz: 2}.minmax_by {|key, value| -value }
# => [[:baz, 2], [:foo, 0]]
[].minmax_by {|element| -element }
# => [nil, nil]

Возвращает Enumerator, если блок не задан.

Связанные: max_by, minmax, min_by.

none? → true или false Показать исходный код
none?(шаблон) → true или false
none? {|элемент| ... } → true или false
static VALUE
enum_none(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo = MEMO_ENUM_NEW(Qtrue);

    WARN_UNUSED_BLOCK(argc);
    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(none), (VALUE)memo);
    return memo->v1;
}

Возвращает true, если ни один элемент не удовлетворяет заданному критерию.

Без аргумента и без блока возвращает true, если ни один элемент не является истинным:

(1..4).none?           # => false
[nil, false].none?     # => true
{foo: 0}.none?         # => false
{foo: 0, bar: 1}.none? # => false
[].none?               # => true

С аргументом pattern и без блока, возвращает true, если ни для одного элемента element, pattern === element:

[nil, false, 1.1].none?(Integer)      # => true
%w[bar baz bat bam].none?(/m/)        # => false
%w[bar baz bat bam].none?(/foo/)      # => true
%w[bar baz bat bam].none?('ba')       # => true
{foo: 0, bar: 1, baz: 2}.none?(Hash)  # => true
{foo: 0}.none?(Array)                 # => false
[].none?(Integer)                     # => true

С блоком, возвращает true, если блок не возвращает истинное значение ни для одного элемента:

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

Связанные: one?, all?, any?.

one? → true или false Показать исходный код
one?(шаблон) → true или false
one? {|элемент| ... } → true или false
static VALUE
enum_one(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo = MEMO_ENUM_NEW(Qundef);
    VALUE result;

    WARN_UNUSED_BLOCK(argc);
    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(one), (VALUE)memo);
    result = memo->v1;
    if (result == Qundef) return Qfalse;
    return result;
}

Возвращает true, если ровно один элемент удовлетворяет заданному критерию.

Без аргумента и без блока возвращает true, если ровно один элемент является истинным:

(1..1).one?           # => true
[1, nil, false].one?  # => true
(1..4).one?           # => false
{foo: 0}.one?         # => true
{foo: 0, bar: 1}.one? # => false
[].one?               # => false

С аргументом pattern и без блока, возвращает true, если для ровно одного элемента element, pattern === element:

[nil, false, 0].one?(Integer)        # => true
[nil, false, 0].one?(Numeric)        # => true
[nil, false, 0].one?(Float)          # => false
%w[bar baz bat bam].one?(/m/)        # => true
%w[bar baz bat bam].one?(/foo/)      # => false
%w[bar baz bat bam].one?('ba')       # => false
{foo: 0, bar: 1, baz: 2}.one?(Array) # => false
{foo: 0}.one?(Array)                 # => true
[].one?(Integer)                     # => false

С блоком, возвращает true, если блок возвращает истинное значение ровно для одного элемента:

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

Связанные: none?, all?, any?.

partition {|элемент| ... } → [массив_истина, массив_ложь] Показать исходный код
partition → перечислитель
static VALUE
enum_partition(VALUE obj)
{
    struct MEMO *memo;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    memo = MEMO_NEW(rb_ary_new(), rb_ary_new(), 0);
    rb_block_call(obj, id_each, 0, 0, partition_i, (VALUE)memo);

    return rb_assoc_new(memo->v1, memo->v2);
}

С блоком возвращает массив из двух массивов:

  • Первый содержит элементы, для которых блок возвращает истинное значение.

  • Второй содержит все остальные элементы.

Примеры:

p = (1..4).partition {|i| i.even? }
p # => [[2, 4], [1, 3]]
p = ('a'..'d').partition {|c| c < 'c' }
p # => [["a", "b"], ["c", "d"]]
h = {foo: 0, bar: 1, baz: 2, bat: 3}
p = h.partition {|key, value| key.start_with?('b') }
p # => [[[:bar, 1], [:baz, 2], [:bat, 3]], [[:foo, 0]]]
p = h.partition {|key, value| value < 2 }
p # => [[[:foo, 0], [:bar, 1]], [[:baz, 2], [:bat, 3]]]

Без блока возвращает Enumerator.

Связанные: Enumerable#group_by.

reduce(p1 = v1, p2 = v2)

Возвращает объект, сформированный из операндов с помощью:

  • Метода, имя которого symbol.

  • Блока, в который передаётся каждый операнд.

С аргументом имени метода symbol, комбинирует операнды, используя метод:

# Sum, without initial_operand.
(1..4).inject(:+)     # => 10
# Sum, with initial_operand.
(1..4).inject(10, :+) # => 20

С блоком, передаёт каждый операнд в блок:

# Sum of squares, without initial_operand.
(1..4).inject {|sum, n| sum + n*n }    # => 30
# Sum of squares, with initial_operand.
(1..4).inject(2) {|sum, n| sum + n*n } # => 32

Операнды

Если аргумент initial_operand не указан, операнды для inject — это просто элементы self. Примеры вызовов и их операнды:

  • (1..4).inject(:+)

    [1, 2, 3, 4].

  • (1...4).inject(:+)

    [1, 2, 3].

  • ('a'..'d').inject(:+)

    ['a', 'b', 'c', 'd'].

  • ('a'...'d').inject(:+)

    ['a', 'b', 'c'].

Примеры с первым операндом (который self.first) различных типов:

# Integer.
(1..4).inject(:+)                # => 10
# Float.
[1.0, 2, 3, 4].inject(:+)        # => 10.0
# Character.
('a'..'d').inject(:+)            # => "abcd"
# Complex.
[Complex(1, 2), 3, 4].inject(:+) # => (8+2i)

Если аргумент initial_operand указан, операнды для inject — это это значение плюс элементы self. Примеры вызовов и их операнды:

  • (1..4).inject(10, :+)

    [10, 1, 2, 3, 4].

  • (1...4).inject(10, :+)

    [10, 1, 2, 3].

  • ('a'..'d').inject('e', :+)

    ['e', 'a', 'b', 'c', 'd'].

  • ('a'...'d').inject('e', :+)

    ['e', 'a', 'b', 'c'].

Примеры с initial_operand различных типов:

# Integer.
(1..4).inject(2, :+)               # => 12
# Float.
(1..4).inject(2.0, :+)             # => 12.0
# String.
('a'..'d').inject('foo', :+)       # => "fooabcd"
# Array.
%w[a b c].inject(['x'], :push)     # => ["x", "a", "b", "c"]
# Complex.
(1..4).inject(Complex(2, 2), :+)   # => (12+2i)

Комбинирование с заданным методом

Если аргумент имени метода symbol указан, операнды комбинируются с помощью этого метода:

  • Первый и второй операнды комбинируются.

  • Результат комбинируется с третьим операндом.

  • Результат комбинируется с четвёртым операндом.

  • И так далее.

Возвращаемое значение от inject — результат последнего комбинирования.

Этот вызов inject вычисляет сумму операндов:

(1..4).inject(:+) # => 10

Примеры с различными методами:

# Integer addition.
(1..4).inject(:+)                # => 10
# Integer multiplication.
(1..4).inject(:*)                # => 24
# Character range concatenation.
('a'..'d').inject('', :+)        # => "abcd"
# String array concatenation.
%w[foo bar baz].inject('', :+)   # => "foobarbaz"
# Hash update.
h = [{foo: 0, bar: 1}, {baz: 2}, {bat: 3}].inject(:update)
h # => {:foo=>0, :bar=>1, :baz=>2, :bat=>3}
# Hash conversion to nested arrays.
h = {foo: 0, bar: 1}.inject([], :push)
h # => [[:foo, 0], [:bar, 1]]

Комбинирование с заданным блоком

Если указан блок, операнды передаются в блок:

  • Первый вызов передаёт первый и второй операнды.

  • Второй вызов передаёт результат первого вызова вместе с третьим операндом.

  • Третий вызов передаёт результат второго вызова вместе с четвёртым операндом.

  • И так далее.

Возвращаемое значение от inject — возвращаемое значение последнего вызова блока.

Этот вызов inject передаёт блок, который записывает данные и суммирует элементы:

(1..4).inject do |memo, element|
  p "Memo: #{memo}; element: #{element}"
  memo + element
end # => 10

Вывод:

"Memo: 1; element: 2"
"Memo: 3; element: 3"
"Memo: 6; element: 4"

Enumerable#reduce — псевдоним для Enumerable#inject.

Псевдоним для: inject
reject {|element| ... } → array Показать исходный код
reject → enumerator
static VALUE
enum_reject(VALUE obj)
{
    VALUE ary;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    ary = rb_ary_new();
    rb_block_call(obj, id_each, 0, 0, reject_i, ary);

    return ary;
}

Возвращает массив объектов, отклоненных блоком.

При указанном блоке вызывает блок с последующими элементами; возвращает массив тех элементов, для которых блок возвращает nil или false.

(0..9).reject {|i| i * 2 if i.even? }                             # => [1, 3, 5, 7, 9]
{foo: 0, bar: 1, baz: 2}.reject {|key, value| key if value.odd? } # => {:foo=>0, :baz=>2}

Без блока возвращает Enumerator.

Связанно с: select.

reverse_each(*args) {|element| ... } → self Показать исходный код
reverse_each(*args) → enumerator
static VALUE
enum_reverse_each(int argc, VALUE *argv, VALUE obj)
{
    VALUE ary;
    long len;

    RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);

    ary = enum_to_a(argc, argv, obj);

    len = RARRAY_LEN(ary);
    while (len--) {
        long nlen;
        rb_yield(RARRAY_AREF(ary, len));
        nlen = RARRAY_LEN(ary);
        if (nlen < len) {
            len = nlen;
        }
    }

    return obj;
}

При указанном блоке вызывает блок с каждым элементом, но в обратном порядке; возвращает self:

a = []
(1..4).reverse_each {|element| a.push(-element) } # => 1..4
a # => [-4, -3, -2, -1]

a = []
%w[a b c d].reverse_each {|element| a.push(element) }
# => ["a", "b", "c", "d"]
a # => ["d", "c", "b", "a"]

a = []
h.reverse_each {|element| a.push(element) }
# => {:foo=>0, :bar=>1, :baz=>2}
a # => [[:baz, 2], [:bar, 1], [:foo, 0]]

Без блока возвращает Enumerator.

select {|element| ... } → array
select → enumerator

Возвращает массив, содержащий выбранные блоком элементы.

При указанном блоке вызывает блок с последующими элементами; возвращает массив тех элементов, для которых блок возвращает истинное значение:

(0..9).select {|element| element % 3 == 0 } # => [0, 3, 6, 9]
a = {foo: 0, bar: 1, baz: 2}.select {|key, value| key.start_with?('b') }
a # => {:bar=>1, :baz=>2}

Без блока возвращает Enumerator.

Связанно с: reject.

Псевдоним для: find_all
slice_after(pattern) → enumerator Показать исходный код
slice_after {|array| ... } → enumerator
static VALUE
enum_slice_after(int argc, VALUE *argv, VALUE enumerable)
{
    VALUE enumerator;
    VALUE pat = Qnil, pred = Qnil;

    if (rb_block_given_p()) {
        if (0 < argc)
            rb_raise(rb_eArgError, "both pattern and block are given");
        pred = rb_block_proc();
    }
    else {
        rb_scan_args(argc, argv, "1", &pat);
    }

    enumerator = rb_obj_alloc(rb_cEnumerator);
    rb_ivar_set(enumerator, id_sliceafter_enum, enumerable);
    rb_ivar_set(enumerator, id_sliceafter_pat, pat);
    rb_ivar_set(enumerator, id_sliceafter_pred, pred);

    rb_block_call(enumerator, idInitialize, 0, 0, sliceafter_i, enumerator);
    return enumerator;
}

С аргументом pattern, возвращает перечислитель, который использует шаблон для разбиения элементов на массивы («куски»). Элемент завершает текущий кусок, если element === pattern:

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

Вывод:

["foo", "bar"]
["fop", "for", "baz"]
["fob", "fog", "bam"]
["foy"]

С блоком возвращает перечислитель, который использует блок для разбиения элементов на массивы. Элемент завершает текущий кусок, если его возвращаемое значение блока истинно:

e = (1..20).slice_after {|i| i % 4 == 2 } # => #<Enumerator: ...>
e.each {|array| p array }

Вывод:

[1, 2]
[3, 4, 5, 6]
[7, 8, 9, 10]
[11, 12, 13, 14]
[15, 16, 17, 18]
[19, 20]

Другие методы класса Enumerator и модуля Enumerable, такие как map, также могут использоваться.

Например, продолжения строк (строки заканчиваются обратной косой чертой) можно объединить следующим образом:

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"]]
p e.map {|ll| ll[0...-1].map {|l| l.sub(/\\\n\z/, "") }.join + ll.last }
#=>["foo\n", "barbaz\n", "\n", "qux\n"]
slice_before(pattern) → enumerator Показать исходный код
slice_before {|array| ... } → enumerator
static VALUE
enum_slice_before(int argc, VALUE *argv, VALUE enumerable)
{
    VALUE enumerator;

    if (rb_block_given_p()) {
        if (argc != 0)
            rb_error_arity(argc, 0, 0);
        enumerator = rb_obj_alloc(rb_cEnumerator);
        rb_ivar_set(enumerator, id_slicebefore_sep_pred, rb_block_proc());
    }
    else {
        VALUE sep_pat;
        rb_scan_args(argc, argv, "1", &sep_pat);
        enumerator = rb_obj_alloc(rb_cEnumerator);
        rb_ivar_set(enumerator, id_slicebefore_sep_pat, sep_pat);
    }
    rb_ivar_set(enumerator, id_slicebefore_enumerable, enumerable);
    rb_block_call(enumerator, idInitialize, 0, 0, slicebefore_i, enumerator);
    return enumerator;
}

С аргументом pattern, возвращает перечислитель, который использует шаблон для разбиения элементов на массивы («куски»). Элемент начинает новый кусок, если element === pattern (или если это первый элемент).

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"]

С блоком возвращает перечислитель, который использует блок для разбиения элементов на массивы. Элемент начинает новый кусок, если его возвращаемое значение блока истинно (или если это первый элемент):

e = (1..20).slice_before {|i| i % 4 == 2 } # => #<Enumerator: ...>
e.each {|array| p array }

Вывод:

[1]
[2, 3, 4, 5]
[6, 7, 8, 9]
[10, 11, 12, 13]
[14, 15, 16, 17]
[18, 19, 20]

Другие методы класса Enumerator и модуля Enumerable, такие как to_a, map, и т. д., также могут использоваться.

Например, итерация по записям ChangeLog может быть реализована следующим образом:

# iterate over ChangeLog entries.
open("ChangeLog") { |f|
  f.slice_before(/\A\S/).each { |e| pp e }
}

# same as above.  block is used instead of pattern argument.
open("ChangeLog") { |f|
  f.slice_before { |line| /\A\S/ === line }.each { |e| pp e }
}

«svn proplist -R» производит многострочный вывод для каждого файла. Их можно разбить на части следующим образом:

IO.popen([{"LC_ALL"=>"C"}, "svn", "proplist", "-R"]) { |f|
  f.lines.slice_before(/\AProp/).each { |lines| p lines }
}
#=> ["Properties on '.':\n", "  svn:ignore\n", "  svk:merge\n"]
#   ["Properties on 'goruby.c':\n", "  svn:eol-style\n"]
#   ["Properties on 'complex.c':\n", "  svn:mime-type\n", "  svn:eol-style\n"]
#   ["Properties on 'regparse.c':\n", "  svn:eol-style\n"]
#   ...

Если блоку нужно сохранять состояние по нескольким элементам, можно использовать локальные переменные. Например, три или более последовательных возрастающих числа можно объединить следующим образом (см. chunk_while для лучшего способа):

a = [0, 2, 3, 4, 6, 7, 9]
prev = a[0]
p a.slice_before { |e|
  prev, prev2 = e, prev
  prev2 + 1 != e
}.map { |es|
  es.length <= 2 ? es.join(",") : "#{es.first}-#{es.last}"
}.join(",")
#=> "0,2-4,6,7,9"

Однако локальные переменные следует использовать осторожно, если перечислитель результата перебирается дважды или более. Локальные переменные должны инициализироваться для каждой итерации. Можно использовать Enumerator.new для этого.

# Word wrapping.  This assumes all characters have same width.
def wordwrap(words, maxwidth)
  Enumerator.new {|y|
    # cols is initialized in Enumerator.new.
    cols = 0
    words.slice_before { |w|
      cols += 1 if cols != 0
      cols += w.length
      if maxwidth < cols
        cols = w.length
        true
      else
        false
      end
    }.each {|ws| y.yield ws }
  }
end
text = (1..20).to_a.join(" ")
enum = wordwrap(text.split(/\s+/), 10)
puts "-"*10
enum.each { |ws| puts ws.join(" ") } # first enumeration.
puts "-"*10
enum.each { |ws| puts ws.join(" ") } # second enumeration generates same result as the first.
puts "-"*10
#=> ----------
#   1 2 3 4 5
#   6 7 8 9 10
#   11 12 13
#   14 15 16
#   17 18 19
#   20
#   ----------
#   1 2 3 4 5
#   6 7 8 9 10
#   11 12 13
#   14 15 16
#   17 18 19
#   20
#   ----------

mbox содержит серию почтовых сообщений, которые начинаются с строки Unix From. Таким образом, каждое сообщение можно извлечь, используя разбиение по строке Unix From.

# parse mbox
open("mbox") { |f|
  f.slice_before { |line|
    line.start_with? "From "
  }.each { |mail|
    unix_from = mail.shift
    i = mail.index("\n")
    header = mail[0...i]
    body = mail[(i+1)..-1]
    body.pop if body.last == "\n"
    fields = header.slice_before { |line| !" \t".include?(line[0]) }.to_a
    p unix_from
    pp fields
    pp body
  }
}

# split mails in mbox (slice before Unix From line after an empty line)
open("mbox") { |f|
  emp = true
  f.slice_before { |line|
    prevemp = emp
    emp = line == "\n"
    prevemp && line.start_with?("From ")
  }.each { |mail|
    mail.pop if mail.last == "\n"
    pp mail
  }
}
slice_when {|element, next_element| ... } → enumerator Показать исходный код
static VALUE
enum_slice_when(VALUE enumerable)
{
    VALUE enumerator;
    VALUE pred;

    pred = rb_block_proc();

    enumerator = rb_obj_alloc(rb_cEnumerator);
    rb_ivar_set(enumerator, id_slicewhen_enum, enumerable);
    rb_ivar_set(enumerator, id_slicewhen_pred, pred);
    rb_ivar_set(enumerator, id_slicewhen_inverted, Qfalse);

    rb_block_call(enumerator, idInitialize, 0, 0, slicewhen_i, enumerator);
    return enumerator;
}

Возвращаемый перечислитель использует блок для разбиения элементов на массивы («куски»); он вызывает блок с каждым элементом и его преемником; начинает новый кусок только в том случае, если блок возвращает истинное значение:

a = [0, 1, 2, 4, 5, 6, 8, 9]
e = a.slice_when {|i, j| j != i + 1 }
e.each {|array| p array }

Вывод:

[0, 1, 2]
[4, 5, 6]
[8, 9]
sort → array Показать исходный код
sort {|a, b| ... } → array
static VALUE
enum_sort(VALUE obj)
{
    return rb_ary_sort_bang(enum_to_a(0, 0, obj));
}

Возвращает массив, содержащий отсортированные элементы self.

Без блока сортировка сравнивает элементы с помощью метода <=>:

%w[b c a d].sort              # => ["a", "b", "c", "d"]
{foo: 0, bar: 1, baz: 2}.sort # => [[:bar, 1], [:baz, 2], [:foo, 0]]

С указанным блоком, сравнения в блоке определяют порядок. Блок вызывается с двумя элементами a и b, и должен возвращать:

  • Отрицательное целое число, если a < b.

  • Ноль, если a == b.

  • Положительное целое число, если a > b.

Примеры:

a = %w[b c a d]
a.sort {|a, b| b <=> a } # => ["d", "c", "b", "a"]
h = {foo: 0, bar: 1, baz: 2}
h.sort {|a, b| b <=> a } # => [[:foo, 0], [:baz, 2], [:bar, 1]]

См. также sort_by. Он реализует преобразование Шварца, которое полезно, когда вычисление ключа или сравнение является дорогостоящим.

sort_by {|элемент| ... } → массив Показать исходный код
sort_by → перечислитель
static VALUE
enum_sort_by(VALUE obj)
{
    VALUE ary, buf;
    struct MEMO *memo;
    long i;
    struct sort_by_data *data;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    if (RB_TYPE_P(obj, T_ARRAY) && RARRAY_LEN(obj) <= LONG_MAX/2) {
        ary = rb_ary_new2(RARRAY_LEN(obj)*2);
    }
    else {
        ary = rb_ary_new();
    }
    RBASIC_CLEAR_CLASS(ary);
    buf = rb_ary_tmp_new(SORT_BY_BUFSIZE*2);
    rb_ary_store(buf, SORT_BY_BUFSIZE*2-1, Qnil);
    memo = MEMO_NEW(0, 0, 0);
    data = (struct sort_by_data *)&memo->v1;
    RB_OBJ_WRITE(memo, &data->ary, ary);
    RB_OBJ_WRITE(memo, &data->buf, buf);
    data->n = 0;
    rb_block_call(obj, id_each, 0, 0, sort_by_i, (VALUE)memo);
    ary = data->ary;
    buf = data->buf;
    if (data->n) {
        rb_ary_resize(buf, data->n*2);
        rb_ary_concat(ary, buf);
    }
    if (RARRAY_LEN(ary) > 2) {
        RARRAY_PTR_USE(ary, ptr,
                       ruby_qsort(ptr, RARRAY_LEN(ary)/2, 2*sizeof(VALUE),
                                  sort_by_cmp, (void *)ary));
    }
    if (RBASIC(ary)->klass) {
        rb_raise(rb_eRuntimeError, "sort_by reentered");
    }
    for (i=1; i<RARRAY_LEN(ary); i+=2) {
        RARRAY_ASET(ary, i/2, RARRAY_AREF(ary, i));
    }
    rb_ary_resize(ary, RARRAY_LEN(ary)/2);
    RBASIC_SET_CLASS_RAW(ary, rb_cArray);

    return ary;
}

При заданном блоке возвращает массив элементов self, отсортированный по значению, возвращаемому блоком для каждого элемента. Порядок равных элементов не определен и может быть неустойчивым.

Примеры:

a = %w[xx xxx x xxxx]
a.sort_by {|s| s.size }        # => ["x", "xx", "xxx", "xxxx"]
a.sort_by {|s| -s.size }       # => ["xxxx", "xxx", "xx", "x"]
h = {foo: 2, bar: 1, baz: 0}
h.sort_by{|key, value| value } # => [[:baz, 0], [:bar, 1], [:foo, 2]]
h.sort_by{|key, value| key }   # => [[:bar, 1], [:baz, 0], [:foo, 2]]

Без заданного блока возвращает Enumerator.

Текущая реализация sort_by генерирует массив кортежей, содержащих исходный элемент коллекции и сопоставленное значение. Это делает sort_by довольно затратным, когда наборы ключей простые.

require 'benchmark'

a = (1..100000).map { rand(100000) }

Benchmark.bm(10) do |b|
  b.report("Sort")    { a.sort }
  b.report("Sort by") { a.sort_by { |a| a } }
end

Результат:

user     system      total        real
Sort        0.180000   0.000000   0.180000 (  0.175469)
Sort by     1.980000   0.040000   2.020000 (  2.013586)

Однако рассмотрим случай, когда сравнение ключей является нетривиальной операцией. Следующий код сортирует некоторые файлы по времени изменения, используя базовый метод sort.

files = Dir["*"]
sorted = files.sort { |a, b| File.new(a).mtime <=> File.new(b).mtime }
sorted   #=> ["mon", "tues", "wed", "thurs"]

Эта сортировка неэффективна: она генерирует два новых объекта File во время каждого сравнения. Несколько лучший подход — использовать метод Kernel#test для непосредственного получения времени изменения.

files = Dir["*"]
sorted = files.sort { |a, b|
  test(?M, a) <=> test(?M, b)
}
sorted   #=> ["mon", "tues", "wed", "thurs"]

Это всё ещё генерирует много ненужных объектов Time. Более эффективный способ — кешировать ключи сортировки (времена изменения в данном случае) перед сортировкой. Пользователи Perl часто называют этот подход преобразованием Шварца, по имени Рэнди Шварца. Мы создаём временный массив, где каждый элемент — массив, содержащий наш ключ сортировки вместе с именем файла. Мы сортируем этот массив, а затем извлекаем имя файла из результата.

sorted = Dir["*"].collect { |f|
   [test(?M, f), f]
}.sort.collect { |f| f[1] }
sorted   #=> ["mon", "tues", "wed", "thurs"]

Это именно то, что делает sort_by внутренне.

sorted = Dir["*"].sort_by { |f| test(?M, f) }
sorted   #=> ["mon", "tues", "wed", "thurs"]

Для получения обратного порядка можно использовать следующее:

ary.sort_by { ... }.reverse!
sum(initial_value = 0) → число Показать исходный код
sum(initial_value = 0) {|элемент| ... } → объект
static VALUE
enum_sum(int argc, VALUE* argv, VALUE obj)
{
    struct enum_sum_memo memo;
    VALUE beg, end;
    int excl;

    memo.v = (rb_check_arity(argc, 0, 1) == 0) ? LONG2FIX(0) : argv[0];
    memo.block_given = rb_block_given_p();
    memo.n = 0;
    memo.r = Qundef;

    if ((memo.float_value = RB_FLOAT_TYPE_P(memo.v))) {
        memo.f = RFLOAT_VALUE(memo.v);
        memo.c = 0.0;
    }
    else {
        memo.f = 0.0;
        memo.c = 0.0;
    }

    if (RTEST(rb_range_values(obj, &beg, &end, &excl))) {
        if (!memo.block_given && !memo.float_value &&
                (FIXNUM_P(beg) || RB_BIGNUM_TYPE_P(beg)) &&
                (FIXNUM_P(end) || RB_BIGNUM_TYPE_P(end))) {
            return int_range_sum(beg, end, excl, memo.v);
        }
    }

    if (RB_TYPE_P(obj, T_HASH) &&
            rb_method_basic_definition_p(CLASS_OF(obj), id_each))
        hash_sum(obj, &memo);
    else
        rb_block_call(obj, id_each, 0, 0, enum_sum_i, (VALUE)&memo);

    if (memo.float_value) {
        return DBL2NUM(memo.f + memo.c);
    }
    else {
        if (memo.n != 0)
            memo.v = rb_fix_plus(LONG2FIX(memo.n), memo.v);
        if (memo.r != Qundef) {
            memo.v = rb_rational_plus(memo.r, memo.v);
        }
        return memo.v;
    }
}

Без заданного блока возвращает сумму initial_value и элементов:

(1..100).sum          # => 5050
(1..100).sum(1)       # => 5051
('a'..'d').sum('foo') # => "fooabcd"

В целом, сумма вычисляется с помощью методов + и each; для повышения производительности эти методы могут не использоваться, и поэтому любое переопределение этих методов здесь может не повлиять.

Один из таких оптимизаций: когда возможно, используется формула суммирования Гаусса n(n+1)/2:

100 * (100 + 1) / 2 # => 5050

При заданном блоке вызывается блок для каждого элемента; возвращает сумму initial_value и значений, возвращаемых блоком:

(1..4).sum {|i| i*i }                        # => 30
(1..4).sum(100) {|i| i*i }                   # => 130
h = {a: 0, b: 1, c: 2, d: 3, e: 4, f: 5}
h.sum {|key, value| value.odd? ? value : 0 } # => 9
('a'..'f').sum('x') {|c| c < 'd' ? c : '' }  # => "xabc"
take(n) → массив Показать исходный код
static VALUE
enum_take(VALUE obj, VALUE n)
{
    struct MEMO *memo;
    VALUE result;
    long len = NUM2LONG(n);

    if (len < 0) {
        rb_raise(rb_eArgError, "attempt to take negative size");
    }

    if (len == 0) return rb_ary_new2(0);
    result = rb_ary_new2(len);
    memo = MEMO_NEW(result, 0, len);
    rb_block_call(obj, id_each, 0, 0, take_i, (VALUE)memo);
    return result;
}

Для неотрицательного целого n, возвращает первые n элементов:

r = (1..4)
r.take(2) # => [1, 2]
r.take(0) # => []

h = {foo: 0, bar: 1, baz: 2, bat: 3}
h.take(2) # => [[:foo, 0], [:bar, 1]]
take_while {|элемент| ... } → массив Показать исходный код
take_while → перечислитель
static VALUE
enum_take_while(VALUE obj)
{
    VALUE ary;

    RETURN_ENUMERATOR(obj, 0, 0);
    ary = rb_ary_new();
    rb_block_call(obj, id_each, 0, 0, take_while_i, ary);
    return ary;
}

Вызывает блок для последовательных элементов, пока блок возвращает истинное значение; возвращает массив всех элементов до этого момента:

(1..4).take_while{|i| i < 3 } # => [1, 2]
h = {foo: 0, bar: 1, baz: 2}
h.take_while{|element| key, value = *element; value < 2 }
# => [[:foo, 0], [:bar, 1]]

Без заданного блока возвращает Enumerator.

tally → новый_хеш Показать исходный код
tally(хеш) → хеш
static VALUE
enum_tally(int argc, VALUE *argv, VALUE obj)
{
    VALUE hash;
    if (rb_check_arity(argc, 0, 1)) {
        hash = rb_convert_type(argv[0], T_HASH, "Hash", "to_hash");
        rb_check_frozen(hash);
    }
    else {
        hash = rb_hash_new();
    }

    return enum_hashify_into(obj, 0, 0, tally_i, hash);
}

Возвращает хеш, содержащий счётчики равных элементов:

  • Каждый ключ — элемент self.

  • Каждое значение — количество элементов, равных этому ключу.

Без аргументов:

%w[a b c b c a c b].tally # => {"a"=>2, "b"=>3, "c"=>3}

С аргументом-хешем, этот хеш используется для подсчёта (вместо нового хеша) и возвращается; это может быть полезно для накопления подсчётов по нескольким перечисляемым объектам:

hash = {}
hash = %w[a c d b c a].tally(hash)
hash # => {"a"=>2, "c"=>2, "d"=>1, "b"=>1}
hash = %w[b a z].tally(hash)
hash # => {"a"=>3, "c"=>2, "d"=>1, "b"=>2, "z"=>1}
hash = %w[b a m].tally(hash)
hash # => {"a"=>4, "c"=>2, "d"=>1, "b"=>3, "z"=>1, "m"=> 1}
to_a → массив Показать исходный код
static VALUE
enum_to_a(int argc, VALUE *argv, VALUE obj)
{
    VALUE ary = rb_ary_new();

    rb_block_call_kw(obj, id_each, argc, argv, collect_all, ary, RB_PASS_CALLED_KEYWORDS);

    return ary;
}

Возвращает массив, содержащий элементы в self:

(0..4).to_a # => [0, 1, 2, 3, 4]

Enumerable#entries — псевдоним для Enumerable#to_a.

Также алиас: entries
to_h → хеш Показать исходный код
to_h {|элемент| ... } → хеш
static VALUE
enum_to_h(int argc, VALUE *argv, VALUE obj)
{
    rb_block_call_func *iter = rb_block_given_p() ? enum_to_h_ii : enum_to_h_i;
    return enum_hashify(obj, argc, argv, iter);
}

Когда self состоит из массивов из 2 элементов, возвращает хеш, каждая запись которого — пара ключ-значение, сформированная из одного из этих массивов:

[[:foo, 0], [:bar, 1], [:baz, 2]].to_h # => {:foo=>0, :bar=>1, :baz=>2}

Когда задан блок, блок вызывается с каждым элементом self; блок должен возвращать массив из 2 элементов, который становится парой ключ-значение в возвращаемом хеше:

(0..3).to_h {|i| [i, i ** 2]} # => {0=>0, 1=>1, 2=>4, 3=>9}

Вызывает исключение, если элемент self не является массивом из 2 элементов и блок не передан.

to_set(klass = Set, *args, &block) Показать исходный код
# File lib/set.rb, line 855
def to_set(klass = Set, *args, &block)
  klass.new(self, *args, &block)
end

Создаёт множество из перечисляемого объекта с заданными аргументами. Для использования этого метода нужно require "set".

uniq → массив Показать исходный код
uniq {|элемент| ... } → массив
static VALUE
enum_uniq(VALUE obj)
{
    VALUE hash, ret;
    rb_block_call_func *const func =
        rb_block_given_p() ? uniq_iter : uniq_func;

    hash = rb_obj_hide(rb_hash_new());
    rb_block_call(obj, id_each, 0, 0, func, hash);
    ret = rb_hash_values(hash);
    rb_hash_clear(hash);
    return ret;
}

Без блока возвращает новый массив, содержащий только уникальные элементы; в массиве нет двух элементов e0 и e1 таких, что e0.eql?(e1):

%w[a b c c b a a b c].uniq       # => ["a", "b", "c"]
[0, 1, 2, 2, 1, 0, 0, 1, 2].uniq # => [0, 1, 2]

С блоком возвращает новый массив, содержащий только те элементы, для которых блок возвращает уникальное значение:

a = [0, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1]
a.uniq {|i| i.even? ? i : 0 } # => [0, 2, 4]
a = %w[a b c d e e d c b a a b c d e]
a.uniq {|c| c < 'c' }         # => ["a", "c"]
zip(*other_enums) → массив Показать исходный код
zip(*other_enums) {|массив| ... } → null
static VALUE
enum_zip(int argc, VALUE *argv, VALUE obj)
{
    int i;
    ID conv;
    struct MEMO *memo;
    VALUE result = Qnil;
    VALUE args = rb_ary_new4(argc, argv);
    int allary = TRUE;

    argv = RARRAY_PTR(args);
    for (i=0; i<argc; i++) {
        VALUE ary = rb_check_array_type(argv[i]);
        if (NIL_P(ary)) {
            allary = FALSE;
            break;
        }
        argv[i] = ary;
    }
    if (!allary) {
        static const VALUE sym_each = STATIC_ID2SYM(id_each);
        CONST_ID(conv, "to_enum");
        for (i=0; i<argc; i++) {
            if (!rb_respond_to(argv[i], id_each)) {
                rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (must respond to :each)",
                         rb_obj_class(argv[i]));
            }
            argv[i] = rb_funcallv(argv[i], conv, 1, &sym_each);
        }
    }
    if (!rb_block_given_p()) {
        result = rb_ary_new();
    }

    /* TODO: use NODE_DOT2 as memo(v, v, -) */
    memo = MEMO_NEW(result, args, 0);
    rb_block_call(obj, id_each, 0, 0, allary ? zip_ary : zip_i, (VALUE)memo);

    return result;
}

Без блока возвращает новый массив new_array размера self.size, элементы которого — массивы. Каждый вложенный массив new_array[n] имеет размер other_enums.size+1, и содержит:

  • n-й элемент self.

  • n-й элемент каждого из other_enums.

Если все перечисляемые объекты other_enums и self имеют одинаковый размер, все элементы включаются в результат, и нет заполнения до размера:

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

f = {foo: 0, bar: 1, baz: 2}
g = {goo: 3, gar: 4, gaz: 5}
h = {hoo: 6, har: 7, haz: 8}
d = f.zip(g, h)
d # => [
  #      [[:foo, 0], [:goo, 3], [:hoo, 6]],
  #      [[:bar, 1], [:gar, 4], [:har, 7]],
  #      [[:baz, 2], [:gaz, 5], [:haz, 8]]
  #    ]

Если какой-либо перечисляемый объект в other_enums меньше, чем self, заполняет до self.size значением 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]]

Если какой-либо перечисляемый объект в other_enums больше, чем self, его хвостовые элементы игнорируются:

a = [:a0, :a1, :a2, :a3]
b = [:b0, :b1, :b2, :b3, :b4]
c = [:c0, :c1, :c2, :c3, :c4, :c5]
d = a.zip(b, c)
d # => [[:a0, :b0, :c0], [:a1, :b1, :c1], [:a2, :b2, :c2], [:a3, :b3, :c3]]

Когда задан блок, вызывается блок для каждого из подмассивов (сформированных как указано выше); возвращает null:

a = [:a0, :a1, :a2, :a3]
b = [:b0, :b1, :b2, :b3]
c = [:c0, :c1, :c2, :c3]
a.zip(b, c) {|sub_array| p sub_array} # => nil

Вывод:

[:a0, :b0, :c0]
[:a1, :b1, :c1]
[:a2, :b2, :c2]
[:a3, :b3, :c3]

Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API