Spec-Zone.ru › Ruby 2.7

модуль Enumerable

Mixin Enumerable предоставляет классам коллекций несколько методов обхода и поиска, а также возможность сортировки. Класс должен предоставить метод each, который возвращает последовательные члены коллекции. Если используется Enumerable#max, min или sort, объекты в коллекции также должны реализовывать осмысленный оператор <=>, так как эти методы полагаются на упорядочение между членами коллекции.

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

all? [{ |obj| block } ] → true or false Show source
all?(pattern) → true or 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;
}

Передает каждый элемент коллекции в заданный блок. Метод возвращает true если блок никогда не возвращает false или nil. Если блок не задан, Ruby добавляет неявный блок { |obj| obj }, который заставит all? возвращать true когда ни один из элементов коллекции не является false или nil.

Если вместо этого задан шаблон, метод возвращает, является ли pattern === element для каждого элемента коллекции.

%w[ant bear cat].all? { |word| word.length >= 3 } #=> true
%w[ant bear cat].all? { |word| word.length >= 4 } #=> false
%w[ant bear cat].all?(/t/)                        #=> false
[1, 2i, 3.14].all?(Numeric)                       #=> true
[nil, true, 99].all?                              #=> false
[].all?                                           #=> true
any? [{ |obj| block }] → true or false Show source
any?(pattern) → true or 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;
}

Передает каждый элемент коллекции в заданный блок. Метод возвращает true если блок когда-либо возвращает значение, отличное от false или nil. Если блок не задан, Ruby добавляет неявный блок { |obj| obj }, который заставит any? возвращать true если хотя бы один из элементов коллекции не является false или nil.

Если вместо этого задан шаблон, метод возвращает, является ли pattern === element для любого элемента коллекции.

%w[ant bear cat].any? { |word| word.length >= 3 } #=> true
%w[ant bear cat].any? { |word| word.length >= 4 } #=> true
%w[ant bear cat].any?(/d/)                        #=> false
[nil, true, 99].any?(Integer)                     #=> true
[nil, true, 99].any?                              #=> true
[].any?                                           #=> false
chain(*enums) → enumerator Show source
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 enum_chain_initialize(enum_chain_allocate(rb_cEnumChain), enums);
}

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

e = (1..3).chain([4, 5])
e.to_a #=> [1, 2, 3, 4, 5]
chunk { |elt| ... } → an_enumerator Show source
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, rb_intern("chunk_enumerable"), enumerable);
    rb_ivar_set(enumerator, rb_intern("chunk_categorize"), rb_block_proc());
    rb_block_call(enumerator, idInitialize, 0, 0, chunk_i, enumerator);
    return enumerator;
}

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

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

Например, последовательные четные и нечетные числа могут быть сгруппированы следующим образом.

[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5].chunk { |n|
  n.even?
}.each { |even, ary|
  p [even, ary]
}
#=> [false, [3, 1]]
#   [true, [4]]
#   [false, [1, 5, 9]]
#   [true, [2, 6]]
#   [false, [5, 3, 5]]

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

open("/usr/share/dict/words", "r:iso-8859-1") { |f|
  f.chunk { |line| line.ord }.each { |ch, lines| p [ch.chr, lines.length] }
}
#=> ["\n", 1]
#   ["A", 1327]
#   ["B", 1372]
#   ["C", 1507]
#   ["D", 791]
#   ...

Следующие значения ключей имеют особое значение:

  • nil и :_separator указывают, что элементы должны быть пропущены.

  • :_alone указывает, что элемент должен быть сгруппирован сам по себе.

Любые другие символы, начинающиеся с подчеркивания, вызовут ошибку:

items.chunk { |item| :_underscore }
#=> RuntimeError: symbols beginning with an underscore are reserved

nil и :_separator могут использоваться для игнорирования некоторых элементов.

Например, последовательность дефисов в журнале 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
}

:_alone может использоваться для принудительной группировки элементов. Например, вы можете разместить строки, содержащие URL-адрес, сами по себе и сгруппировать остальные строки вместе, например так:

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

Если блок не задан, вместо этого возвращается перечислитель для `chunk`.

chunk_while {|elt_before, elt_after| bool } → an_enumerator Show source
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, rb_intern("slicewhen_enum"), enumerable);
    rb_ivar_set(enumerator, rb_intern("slicewhen_pred"), pred);
    rb_ivar_set(enumerator, rb_intern("slicewhen_inverted"), Qtrue);

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

Создает перечислитель для каждого сгруппированного элемента. Начала групп определяются блоком.

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

Блок вызывается длиной перечислителя получателя минус один.

Результирующий перечислитель выдает сгруппированные элементы в виде массива. Таким образом, метод each может быть вызван следующим образом:

enum.chunk_while { |elt_before, elt_after| bool }.each { |ary| ... }

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

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

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 = [0, 9, 2, 2, 3, 2, 7, 5, 9, 5]
p a.chunk_while {|i, j| i <= j }.to_a
#=> [[0, 9], [2, 2, 3], [2, 7], [5, 9], [5]]

Смежные четные и нечетные числа могут быть сгруппированы следующим образом: (Enumerable#chunk — это другой способ сделать это).

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 вместо false.

collect { |obj| block } → array Show source
collect → an_enumerator
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;
}

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

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

(1..4).map { |i| i*i }      #=> [1, 4, 9, 16]
(1..4).collect { "cat"  }   #=> ["cat", "cat", "cat", "cat"]
collect_concat { |obj| block } → array Show source
collect_concat → an_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;
}

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

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

[1, 2, 3, 4].flat_map { |e| [e, -e] } #=> [1, -1, 2, -2, 3, -3, 4, -4]
[[1, 2], [3, 4]].flat_map { |e| e + [100] } #=> [1, 2, 100, 3, 4, 100]
count → int Show source
count(item) → int
count { |obj| block } → int
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);
}

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

ary = [1, 2, 4, 2]
ary.count               #=> 4
ary.count(2)            #=> 2
ary.count{ |x| x%2==0 } #=> 3
cycle(n=nil) { |obj| block } → nil Show source
cycle(n=nil) → an_enumerator
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;
}

Вызывает блок для каждого элемента enum многократно n раз или бесконечно, если не задано значение или задано nil. Если задано неположительное число или коллекция пуста, ничего не делает. Возвращает nil, если цикл завершился без прерывания.

Enumerable#cycle сохраняет элементы во внутреннем массиве, поэтому изменения в enum после первого прохода не влияют.

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

a = ["a", "b", "c"]
a.cycle { |x| puts x }  # print, a, b, c, a, b, c,.. forever.
a.cycle(2) { |x| puts x }  # print, a, b, c, a, b, c.
detect(ifnone = nil) { |obj| block } → obj or nil Show source
detect(ifnone = nil) → an_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;
}

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

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

(1..100).detect  #=> #<Enumerator: 1..100:detect>
(1..100).find    #=> #<Enumerator: 1..100:find>

(1..10).detect         { |i| i % 5 == 0 && i % 7 == 0 }   #=> nil
(1..10).find           { |i| i % 5 == 0 && i % 7 == 0 }   #=> nil
(1..10).detect(-> {0}) { |i| i % 5 == 0 && i % 7 == 0 }   #=> 0
(1..10).find(-> {0})   { |i| i % 5 == 0 && i % 7 == 0 }   #=> 0
(1..100).detect        { |i| i % 5 == 0 && i % 7 == 0 }   #=> 35
(1..100).find          { |i| i % 5 == 0 && i % 7 == 0 }   #=> 35
drop(n) → array Show source
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 элементов из enum и возвращает оставшиеся элементы в массиве.

a = [1, 2, 3, 4, 5, 0]
a.drop(3)             #=> [4, 5, 0]
drop_while { |obj| block } → array Show source
drop_while → an_enumerator
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;
}

Удаляет элементы вплоть до, но не включая, первого элемента, для которого блок возвращает nil или false, и возвращает массив, содержащий оставшиеся элементы.

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

a = [1, 2, 3, 4, 5, 0]
a.drop_while { |i| i < 3 }   #=> [3, 4, 5, 0]
each_cons(n) { ... } → nil Показать исходный код
each_cons(n) → итерируемый объект
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 Qnil;
    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 Qnil;
}

Итерирует заданный блок для каждого массива последовательных элементов <n>. Если блок не задан, возвращает итерируемый объект.

Например:

(1..10).each_cons(3) { |a| p a }
# outputs below
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 10]
each_entry { |obj| блок } → перечисление Показать исходный код
each_entry → итерируемый объект
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;
}

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

Если блок не задан, вместо него возвращается итерируемый объект.

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

Результат:

1
[1, 2]
nil
each_slice(n) { ... } → nil Показать исходный код
each_slice(n) → итерируемый объект
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 Qnil;
}

Итерирует заданный блок для каждого набора из <n> элементов. Если блок не задан, возвращает итерируемый объект.

(1..10).each_slice(3) { |a| p a }
# outputs below
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
[10]
each_with_index(*args) { |obj, i| блок } → перечисление Показать исходный код
each_with_index(*args) → итерируемый объект
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;
}

Вызывает блок с двумя аргументами — элементом и его индексом — для каждого элемента в перечислении. Переданные аргументы передаются в each().

Если блок не задан, возвращается итерируемый объект вместо него.

hash = Hash.new
%w(cat dog wombat).each_with_index { |item, index|
  hash[item] = index
}
hash   #=> {"cat"=>0, "dog"=>1, "wombat"=>2}
each_with_object(объект) { |(*args), объект_памяти| ... } → объект Показать исходный код
each_with_object(объект) → итерируемый объект
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;
}

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

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

evens = (1..10).each_with_object([]) { |i, a| a << i*2 }
#=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
entries(*args) → массив Показать исходный код
static VALUE
enum_to_a(int argc, VALUE *argv, VALUE obj)
{
    VALUE ary = rb_ary_new();

    rb_block_call(obj, id_each, argc, argv, collect_all, ary);

    return ary;
}

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

(1..7).to_a                       #=> [1, 2, 3, 4, 5, 6, 7]
{ 'a'=>1, 'b'=>2, 'c'=>3 }.to_a   #=> [["a", 1], ["b", 2], ["c", 3]]

require 'prime'
Prime.entries 10                  #=> [2, 3, 5, 7]
filter { |obj| блок } → массив Показать исходный код
filter → итерируемый объект
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;
}

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

Методы find_all и select являются псевдонимами. Разницы в производительности нет.

Если блок не задан, возвращается Enumerator вместо него.

(1..10).find_all { |i|  i % 3 == 0 }   #=> [3, 6, 9]

[1,2,3,4,5].select { |num|  num.even?  }   #=> [2, 4]

[:foo, :bar].filter { |x| x == :foo }   #=> [:foo]

См. также Enumerable#reject, Enumerable#grep.

filter_map { |obj| блок } → массив Показать исходный код
filter_map → итерируемый объект
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;
}

Возвращает новый массив, содержащий результаты (исключая false или nil) выполнения block для каждого элемента в enum.

Если блок не задан, возвращается Enumerator вместо него.

(1..10).filter_map { |i| i * 2 if i.even? } #=> [4, 8, 12, 16, 20]
find(если_нет = nil) { |obj| блок } → obj или nil Показать исходный код
find(если_нет = nil) → итерируемый объект
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;
}

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

Если блок не задан, возвращается итерируемый объект вместо него.

(1..100).detect  #=> #<Enumerator: 1..100:detect>
(1..100).find    #=> #<Enumerator: 1..100:find>

(1..10).detect         { |i| i % 5 == 0 && i % 7 == 0 }   #=> nil
(1..10).find           { |i| i % 5 == 0 && i % 7 == 0 }   #=> nil
(1..10).detect(-> {0}) { |i| i % 5 == 0 && i % 7 == 0 }   #=> 0
(1..10).find(-> {0})   { |i| i % 5 == 0 && i % 7 == 0 }   #=> 0
(1..100).detect        { |i| i % 5 == 0 && i % 7 == 0 }   #=> 35
(1..100).find          { |i| i % 5 == 0 && i % 7 == 0 }   #=> 35
find_all { |obj| блок } → массив Показать исходный код
find_all → итерируемый объект
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;
}

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

Методы find_all и select являются псевдонимами. Разницы в производительности нет.

Если блок не задан, возвращается Enumerator вместо него.

(1..10).find_all { |i|  i % 3 == 0 }   #=> [3, 6, 9]

[1,2,3,4,5].select { |num|  num.even?  }   #=> [2, 4]

[:foo, :bar].filter { |x| x == :foo }   #=> [:foo]

См. также Enumerable#reject, Enumerable#grep.

find_index(значение) → целое число или nil Показать исходный код
find_index { |obj| блок } → целое число или nil
find_index → итерируемый объект
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

Если ни блок, ни аргумент не заданы, возвращается итерируемый объект вместо него.

(1..10).find_index  { |i| i % 5 == 0 && i % 7 == 0 }  #=> nil
(1..100).find_index { |i| i % 5 == 0 && i % 7 == 0 }  #=> 34
(1..100).find_index(50)                               #=> 49
first → объект или nil Показать исходный код
first(n) → массив
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;
    }
}

Возвращает первый элемент или первые n элементы перечисляемого объекта. Если перечисляемый объект пуст, первая форма возвращает nil, а вторая — пустой массив.

%w[foo bar baz].first     #=> "foo"
%w[foo bar baz].first(2)  #=> ["foo", "bar"]
%w[foo bar baz].first(10) #=> ["foo", "bar", "baz"]
[].first                  #=> nil
[].first(10)              #=> []
flat_map { |obj| блок } → массив Показать исходный код
flat_map → итерируемый объект
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;
}

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

Если блок не задан, возвращается итерируемый объект вместо него.

[1, 2, 3, 4].flat_map { |e| [e, -e] } #=> [1, -1, 2, -2, 3, -3, 4, -4]
[[1, 2], [3, 4]].flat_map { |e| e + [100] } #=> [1, 2, 100, 3, 4, 100]
grep(шаблон) → массив Показать исходный код
grep(шаблон) { |объект| блок } → массив
static VALUE
enum_grep(VALUE obj, VALUE pat)
{
    VALUE ary = rb_ary_new();
    struct MEMO *memo = MEMO_NEW(pat, ary, Qtrue);

    rb_block_call(obj, id_each, 0, 0, rb_block_given_p() ? grep_iter_i : grep_i, (VALUE)memo);

    return ary;
}

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

(1..100).grep 38..44   #=> [38, 39, 40, 41, 42, 43, 44]
c = IO.constants
c.grep(/SEEK/)         #=> [:SEEK_SET, :SEEK_CUR, :SEEK_END]
res = c.grep(/SEEK/) { |v| IO.const_get(v) }
res                    #=> [0, 1, 2]
grep_v(шаблон) → массив Показать исходный код
grep_v(шаблон) { |объект| блок } → массив
static VALUE
enum_grep_v(VALUE obj, VALUE pat)
{
    VALUE ary = rb_ary_new();
    struct MEMO *memo = MEMO_NEW(pat, ary, Qfalse);

    rb_block_call(obj, id_each, 0, 0, rb_block_given_p() ? grep_iter_i : grep_i, (VALUE)memo);

    return ary;
}

Инвертированная версия Enumerable#grep. Возвращает массив каждого элемента в перечислении, для которого не Pattern === element.

(1..10).grep_v 2..5   #=> [1, 6, 7, 8, 9, 10]
res =(1..10).grep_v(2..5) { |v| v * 2 }
res                    #=> [2, 12, 14, 16, 18, 20]
group_by { |obj| блок } → словарь Показать исходный код
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);
}

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

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

(1..6).group_by { |i| i%3 }   #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}
include?(obj) → true or false Show source
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 если любой член enum равен obj. Равенство проверяется с помощью ==.

(1..10).include? 5  #=> true
(1..10).include? 15 #=> false
(1..10).member? 5   #=> true
(1..10).member? 15  #=> false
inject(initial, sym) → obj Show source
inject(sym) → obj
inject(initial) { |memo, obj| block } → obj
inject { |memo, obj| block } → obj
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;
}

Объединяет все элементы enum, применяя бинарную операцию, заданную блоком или символом, который обозначает метод или оператор.

Методы inject и reduce являются псевдонимами. Разницы в производительности нет.

Если вы указываете блок, то для каждого элемента в enum блоку передается значение накопителя (memo) и элемент. Если вы указываете символ, то каждый элемент в коллекции будет передан в метод memo с указанным именем. В любом случае, результат становится новым значением для memo. В конце итерации, конечное значение memo является возвращаемым значением метода.

Если вы явно не указываете начальное значение для memo, то первый элемент коллекции используется в качестве начального значения memo.

# Sum some numbers
(5..10).reduce(:+)                             #=> 45
# Same using a block and inject
(5..10).inject { |sum, n| sum + n }            #=> 45
# Multiply some numbers
(5..10).reduce(1, :*)                          #=> 151200
# Same using a block
(5..10).inject(1) { |product, n| product * n } #=> 151200
# find the longest word
longest = %w{ cat sheep bear }.inject do |memo, word|
   memo.length > word.length ? memo : word
end
longest                                        #=> "sheep"
lazy → lazy_enumerator Show source
static VALUE
enumerable_lazy(VALUE obj)
{
    VALUE result = lazy_to_enum_i(obj, sym_each, 0, 0, lazyenum_size, PASS_KW_SPLAT);
    /* 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 { |obj| block } → array Show source
map → an_enumerator
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;
}

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

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

(1..4).map { |i| i*i }      #=> [1, 4, 9, 16]
(1..4).collect { "cat"  }   #=> ["cat", "cat", "cat", "cat"]
max → obj Show source
max { |a, b| block } → obj
max(n) → array
max(n) { |a, b| block } → array
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;
}

Возвращает объект в enum с максимальным значением. Первая форма предполагает, что все объекты реализуют Comparable; вторая использует блок для возврата a <=> b.

a = %w(albatross dog horse)
a.max                                   #=> "horse"
a.max { |a, b| a.length <=> b.length }  #=> "albatross"

Если указан аргумент n, возвращается массив из максимальных n элементов, отсортированных по убыванию.

a = %w[albatross dog horse]
a.max(2)                                  #=> ["horse", "dog"]
a.max(2) {|a, b| a.length <=> b.length }  #=> ["albatross", "horse"]
[5, 1, 3, 4, 2].max(3)                    #=> [5, 4, 3]
max_by {|obj| block } → obj Show source
max_by → an_enumerator
max_by(n) {|obj| block } → obj
max_by(n) → an_enumerator
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;
}

Возвращает объект в enum, который дает максимальное значение из данного блока.

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

a = %w(albatross dog horse)
a.max_by { |x| x.length }   #=> "albatross"

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

a = %w[albatross dog horse]
a.max_by(2) {|x| x.length } #=> ["albatross", "horse"]

enum.max_by(n) может использоваться для реализации взвешенной случайной выборки. Следующий пример реализует и использует Enumerable#wsample.

module Enumerable
  # weighted random sampling.
  #
  # Pavlos S. Efraimidis, Paul G. Spirakis
  # Weighted random sampling with a reservoir
  # Information Processing Letters
  # Volume 97, Issue 5 (16 March 2006)
  def wsample(n)
    self.max_by(n) {|v| rand ** (1.0/yield(v)) }
  end
end
e = (-20..20).to_a*10000
a = e.wsample(20000) {|x|
  Math.exp(-(x/5.0)**2) # normal distribution
}
# a is 20000 samples from e.
p a.length #=> 20000
h = a.group_by {|x| x }
-10.upto(10) {|x| puts "*" * (h[x].length/30.0).to_i if h[x] }
#=> *
#   ***
#   ******
#   ***********
#   ******************
#   *****************************
#   *****************************************
#   ****************************************************
#   ***************************************************************
#   ********************************************************************
#   ***********************************************************************
#   ***********************************************************************
#   **************************************************************
#   ****************************************************
#   ***************************************
#   ***************************
#   ******************
#   ***********
#   *******
#   ***
#   *
member?(obj) → true or false Show source
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 если любой член enum равен obj. Равенство проверяется с помощью ==.

(1..10).include? 5  #=> true
(1..10).include? 15 #=> false
(1..10).member? 5   #=> true
(1..10).member? 15  #=> false
min → obj Show source
min { |a, b| block } → obj
min(n) → array
min(n) { |a, b| block } → array
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;
}

Возвращает объект в enum с минимальным значением. Первая форма предполагает, что все объекты реализуют Comparable; вторая использует блок для возврата a <=> b.

a = %w(albatross dog horse)
a.min                                   #=> "albatross"
a.min { |a, b| a.length <=> b.length }  #=> "dog"

Если указан аргумент n, возвращается отсортированный массив из минимальных n элементов.

a = %w[albatross dog horse]
a.min(2)                                  #=> ["albatross", "dog"]
a.min(2) {|a, b| a.length <=> b.length }  #=> ["dog", "horse"]
[5, 1, 3, 4, 2].min(3)                    #=> [1, 2, 3]
min_by {|obj| block } → obj Show source
min_by → an_enumerator
min_by(n) {|obj| block } → array
min_by(n) → an_enumerator
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;
}

Возвращает объект в enum, который дает минимальное значение из данного блока.

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

a = %w(albatross dog horse)
a.min_by { |x| x.length }   #=> "dog"

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

a = %w[albatross dog horse]
p a.min_by(2) {|x| x.length } #=> ["dog", "horse"]
minmax → [min, max] Show source
minmax { |a, b| block } → [min, max]
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);
}

Возвращает массив из двух элементов, содержащий минимальное и максимальное значение в перечислимом объекте. Первая форма предполагает, что все объекты реализуют Comparable; вторая использует блок для возврата a <=> b.

a = %w(albatross dog horse)
a.minmax                                  #=> ["albatross", "horse"]
a.minmax { |a, b| a.length <=> b.length } #=> ["dog", "albatross"]
minmax_by { |obj| block } → [min, max] Show source
minmax_by → an_enumerator
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);
}

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

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

a = %w(albatross dog horse)
a.minmax_by { |x| x.length }   #=> ["dog", "albatross"]
none? [{ |obj| block }] → true or false Show source
none?(pattern) → true or 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 для всех элементов. Если блок не задан, none? вернет true, только если ни один из элементов коллекции не является истинным.

Если вместо этого предоставлен шаблон, метод возвращает, соответствует ли pattern === element ни одному из элементов коллекции.

%w{ant bear cat}.none? { |word| word.length == 5 } #=> true
%w{ant bear cat}.none? { |word| word.length >= 4 } #=> false
%w{ant bear cat}.none?(/d/)                        #=> true
[1, 3.14, 42].none?(Float)                         #=> false
[].none?                                           #=> true
[nil].none?                                        #=> true
[nil, false].none?                                 #=> true
[nil, false, true].none?                           #=> false
one? [{ |obj| block }] → true or false Show source
one?(pattern) → true or 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 ровно один раз. Если блок не задан, one? вернет true, только если ровно один из элементов коллекции является истинным.

Если вместо этого предоставлен шаблон, метод возвращает, соответствует ли pattern === element ровно одному элементу коллекции.

%w{ant bear cat}.one? { |word| word.length == 4 }  #=> true
%w{ant bear cat}.one? { |word| word.length > 4 }   #=> false
%w{ant bear cat}.one? { |word| word.length < 4 }   #=> false
%w{ant bear cat}.one?(/t/)                         #=> false
[ nil, true, 99 ].one?                             #=> false
[ nil, true, false ].one?                          #=> true
[ nil, true, 99 ].one?(Integer)                    #=> true
[].one?                                            #=> false
partition { |obj| block } → [ true_array, false_array ] Показать исходный код
partition → an_enumerator
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);
}

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

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

(1..6).partition { |v| v.even? }  #=> [[2, 4, 6], [1, 3, 5]]
reduce(initial, sym) → obj Показать исходный код
reduce(sym) → obj
reduce(initial) { |memo, obj| block } → obj
reduce { |memo, obj| block } → obj
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;
}

Объединяет все элементы enum, применяя бинарную операцию, заданную блоком или символом, обозначающим метод или оператор.

Методы inject и reduce являются псевдонимами. Разницы в производительности нет.

Если вы указываете блок, то для каждого элемента в enum блоку передаётся значение аккумулятора (memo) и элемент. Если вы указываете символ, то каждый элемент в коллекции передаётся в метод memo с указанным именем. В любом случае результат становится новым значением для memo. В конце итерации конечное значение memo является возвращаемым значением метода.

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

# Sum some numbers
(5..10).reduce(:+)                             #=> 45
# Same using a block and inject
(5..10).inject { |sum, n| sum + n }            #=> 45
# Multiply some numbers
(5..10).reduce(1, :*)                          #=> 151200
# Same using a block
(5..10).inject(1) { |product, n| product * n } #=> 151200
# find the longest word
longest = %w{ cat sheep bear }.inject do |memo, word|
   memo.length > word.length ? memo : word
end
longest                                        #=> "sheep"
reject { |obj| block } → array Показать исходный код
reject → an_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;
}

Возвращает массив элементов из enum, для которых заданный block возвращает false.

Если блок не задан, вместо него возвращается Enumerator.

(1..10).reject { |i|  i % 3 == 0 }   #=> [1, 2, 4, 5, 7, 8, 10]

[1, 2, 3, 4, 5].reject { |num| num.even? } #=> [1, 3, 5]

См. также Enumerable#find_all.

reverse_each(*args) { |item| block } → enum Показать исходный код
reverse_each(*args) → an_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;
}

Создаёт временный массив и проходит по нему в обратном порядке.

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

(1..3).reverse_each { |v| p v }

вывод:

3
2
1
select { |obj| block } → array Показать исходный код
select → an_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;
}

Возвращает массив, содержащий все элементы enum для которых заданный block возвращает значение true.

Методы find_all и select являются псевдонимами. Разницы в производительности нет.

Если блок не задан, вместо него возвращается Enumerator.

(1..10).find_all { |i|  i % 3 == 0 }   #=> [3, 6, 9]

[1,2,3,4,5].select { |num|  num.even?  }   #=> [2, 4]

[:foo, :bar].filter { |x| x == :foo }   #=> [:foo]

См. также Enumerable#reject, Enumerable#grep.

slice_after(pattern) → an_enumerator Показать исходный код
slice_after { |elt| bool } → an_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, rb_intern("sliceafter_enum"), enumerable);
    rb_ivar_set(enumerator, rb_intern("sliceafter_pat"), pat);
    rb_ivar_set(enumerator, rb_intern("sliceafter_pred"), pred);

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

Создаёт перечислитель для каждого элемента блока. Концы блоков определяются значением pattern и блоком.

Если pattern === elt возвращает true или блок возвращает true для элемента, то элемент является концом блока.

=== и block вызывается от первого к последнему элементу enum.

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

enum.slice_after(pattern).each { |ary| ... }
enum.slice_after { |elt| bool }.each { |ary| ... }

Другие методы класса 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) → an_enumerator Показать исходный код
slice_before { |elt| bool } → an_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, rb_intern("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, rb_intern("slicebefore_sep_pat"), sep_pat);
    }
    rb_ivar_set(enumerator, rb_intern("slicebefore_enumerable"), enumerable);
    rb_block_call(enumerator, idInitialize, 0, 0, slicebefore_i, enumerator);
    return enumerator;
}

Создаёт перечислитель для каждого элемента блока. Начало блоков определяются значением pattern и блоком.

Если pattern === elt возвращает true или блок возвращает true для элемента, то элемент является началом блока.

=== и block вызывается от первого к последнему элементу enum. Результат для первого элемента игнорируется.

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

enum.slice_before(pattern).each { |ary| ... }
enum.slice_before { |elt| bool }.each { |ary| ... }

Другие методы класса 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. Поэтому каждое письмо может быть извлечено с помощью slice перед строкой 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 {|elt_before, elt_after| bool } → an_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, rb_intern("slicewhen_enum"), enumerable);
    rb_ivar_set(enumerator, rb_intern("slicewhen_pred"), pred);
    rb_ivar_set(enumerator, rb_intern("slicewhen_inverted"), Qfalse);

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

Создаёт перечислитель для каждого элемента блока. Начало блоков определяются блоком.

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

Блок вызывается для длины перечислителя получателя минус один.

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

enum.slice_when { |elt_before, elt_after| bool }.each { |ary| ... }

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

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

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

Близкие элементы (порог: 6) в отсортированном массиве могут быть разбиты на блоки следующим образом:

a = [3, 11, 14, 25, 28, 29, 29, 41, 55, 57]
p a.slice_when {|i, j| 6 < j - i }.to_a
#=> [[3], [11, 14], [25, 28, 29, 29], [41], [55, 57]]

Возрастающая (неубывающая) подпоследовательность можно разбить на блоки следующим образом:

a = [0, 9, 2, 2, 3, 2, 7, 5, 9, 5]
p a.slice_when {|i, j| i > j }.to_a
#=> [[0, 9], [2, 2, 3], [2, 7], [5, 9], [5]]

Последовательные чётные и нечётные числа могут быть разбиты на блоки следующим образом: (Enumerable#chunk — это другой способ сделать это.)

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

Абзацы (непустые строки с последующими пустыми строками) могут быть разбиты на блоки следующим образом: (см. Enumerable#chunk, чтобы игнорировать пустые строки.)

lines = ["foo\n", "bar\n", "\n", "baz\n", "qux\n"]
p lines.slice_when {|l1, l2| /\A\s*\z/ =~ l1 && /\S/ =~ l2 }.to_a
#=> [["foo\n", "bar\n", "\n"], ["baz\n", "qux\n"]]

Enumerable#chunk_while делает то же самое, за исключением разделения, когда блок возвращает false вместо true.

sort → массив Показать исходный код
sort { |a, b| блок } → массив
static VALUE
enum_sort(VALUE obj)
{
    return rb_ary_sort_bang(enum_to_a(0, 0, obj));
}

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

Сравнения для сортировки будут выполняться с использованием оператора <=> элементов или с использованием необязательного кодового блока.

Блок должен реализовывать сравнение между a и b и возвращать целое число меньше 0, когда b следует за a, целое число, равное 0, когда a и b эквивалентны, или целое число больше 0, когда a следует за b.

Результат не гарантируется как стабильный. Когда сравнение двух элементов возвращает 0, порядок элементов непредсказуем.

%w(rhea kea flea).sort           #=> ["flea", "kea", "rhea"]
(1..10).sort { |a, b| b <=> a }  #=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

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

sort_by { |obj| блок } → массив Показать исходный код
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;
}

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

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

Если блок не указан, возвращается перечислитель.

%w{apple pear fig}.sort_by { |word| word.length }
              #=> ["fig", "pear", "apple"]

Текущая реализация 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(init=0) → число Показать исходный код
sum(init=0) {|e| выражение } → число
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_TYPE_P(beg, T_BIGNUM)) &&
                (FIXNUM_P(end) || RB_TYPE_P(end, T_BIGNUM))) {
            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) {
            /* r can be an Integer when mathn is loaded */
            if (FIXNUM_P(memo.r))
                memo.v = rb_fix_plus(memo.r, memo.v);
            else if (RB_TYPE_P(memo.r, T_BIGNUM))
                memo.v = rb_big_plus(memo.r, memo.v);
            else
                memo.v = rb_rational_plus(memo.r, memo.v);
        }
        return memo.v;
    }
}

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

Если задан блок, блок применяется к каждому элементу перед добавлением.

Если перечисление пусто, возвращается init.

Например:

{ 1 => 10, 2 => 20 }.sum {|k, v| k * v }  #=> 50
(1..10).sum                               #=> 55
(1..10).sum {|v| v * 2 }                  #=> 110
('a'..'z').sum                            #=> TypeError

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

{ 1 => 10, 2 => 20 }.sum([])                   #=> [1, 10, 2, 20]
"a\nb\nc".each_line.lazy.map(&:chomp).sum("")  #=> "abc"

Если метод применяется к диапазону Integer без блока, сумма вычисляется не итерационно, а с использованием формулы суммирования Гаусса.

Enumerable#sum метод может не учитывать переопределение метода “+” таких методов, как Integer#+, или методов “each”, таких как Range#each.

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 элементов из перечисления.

a = [1, 2, 3, 4, 5, 0]
a.take(3)             #=> [1, 2, 3]
a.take(30)            #=> [1, 2, 3, 4, 5, 0]
take_while { |obj| блок } → массив Показать исходный код
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;
}

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

Если блок не указан, возвращается перечислитель.

a = [1, 2, 3, 4, 5, 0]
a.take_while { |i| i < 3 }   #=> [1, 2]
tally → словарь Показать исходный код
static VALUE
enum_tally(VALUE obj)
{
    return enum_hashify(obj, 0, 0, tally_i);
}

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

["a", "b", "c", "b"].tally  #=> {"a"=>1, "b"=>2, "c"=>1}
to_a(*аргументы) → массив Показать исходный код
static VALUE
enum_to_a(int argc, VALUE *argv, VALUE obj)
{
    VALUE ary = rb_ary_new();

    rb_block_call(obj, id_each, argc, argv, collect_all, ary);

    return ary;
}

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

(1..7).to_a                       #=> [1, 2, 3, 4, 5, 6, 7]
{ 'a'=>1, 'b'=>2, 'c'=>3 }.to_a   #=> [["a", 1], ["b", 2], ["c", 3]]

require 'prime'
Prime.entries 10                  #=> [2, 3, 5, 7]
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);
}

Возвращает результат интерпретации перечисления как списка [key, value] пар.

%i[hello world].each_with_index.to_h
  # => {:hello => 0, :world => 1}

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

(1..5).to_h {|x| [x, x ** 2]}
  #=> {1=>1, 2=>4, 3=>9, 4=>16, 5=>25}
to_set(klass = Set, *аргументы, &блок) Показать исходный код
# File lib/set.rb, line 805
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;
}

Возвращает новый массив, удаляя дублирующие значения в self.

См. также Array#uniq.

zip(аргумент, ...) → массив_массивов Показать исходный код
zip(аргумент, ...) { |массив| блок } → nil
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;
}

Берет один элемент из перечисления и объединяет соответствующие элементы из каждого аргумента. Это генерирует последовательность n-элементных массивов, где n - на единицу больше количества аргументов. Длина результирующей последовательности будет enum#size. Если размер любого аргумента меньше enum#size, используются значения nil. Если задан блок, он вызывается для каждого выходного массива, в противном случае возвращается массив массивов.

a = [ 4, 5, 6 ]
b = [ 7, 8, 9 ]

a.zip(b)                 #=> [[4, 7], [5, 8], [6, 9]]
[1, 2, 3].zip(a, b)      #=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
[1, 2].zip(a, b)         #=> [[1, 4, 7], [2, 5, 8]]
a.zip([1, 2], [8])       #=> [[4, 1, 8], [5, 2, nil], [6, nil, nil]]

c = []
a.zip(b) { |x, y| c << x + y }  #=> nil
c                               #=> [11, 13, 15]

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

Spec-Zone.ru

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