Программирование на языке Ruby.

8.1.6. Выборка из массива по заданному критерию.

Иногда нужно найти в массиве один или несколько элементов так, как будто мы опрашиваем таблицу в базе данных. Для этого есть несколько способов; рассмотренные ниже реализованы в подмешанном модуле Enumerable.

Метод detect находит не больше одного элемента. Он принимает блок (которому элементы передаются последовательно) и возвращает первый элемент, для которого значение блока оказывается равным true.

X = [5, 8, 12, 9, 4, 30].

# Найти первый элемент, кратный 6.

X.detect {|e| e % 6 == 0 } #12.

# Найти первый элемент, кратный 7.

C.detect {|e| e % 7 == 0 } # nil.

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

Метод find — синоним detect. Метод find_all возвращает несколько элементов, а не один-единственный; select — синоним find_all.

# Продолжение предыдущего примера...

X.find {|e| e % 2 == 0}     # 8.

X.find_all {|e| e % 2 == 0} # [8, 12, 4, 30].

X.select {|e| e % 2 == 0}   # [8, 12, 4, 30].

Метод grep вызывает оператор сравнения (то есть оператор ветвящегося равенства) для сопоставления каждого элемента с заданным образцом. В простейшей форме он возвращает массив, состоящий из элементов, соответствующих образцу. Так как используется оператор ===, то образец не обязан быть регулярным выражением. (Имя grep пришло из UNIX и связано с командой старого редактора g/re/p.)

А = %w[January February March April May].

A.grep(/ary/}  # ["January, "February"].

B = [1, 20, 5, 7, 13, 33, 15, 28].

B.grep(12..24) # [20, 13, 15].

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

# продолжение предыдущего примера...

# Будем сохранять длины строк.

A.grep(/ary/) {|m| m.length} # [7, 8].

# Будем сохранять квадраты исходных элементов.

B.grep(12..24) { |n| n*n}    # {400, 169, 225}.

Метод reject — полная противоположность select. Он исключает из массива элементы, для которых блок возвращает значение true. Имеется также вариант reject! для модификации массива «на месте»:

С = [5, 8, 12, 9, 4, 30].

D = с.reject {|e| е % 2 == 0} # [5, 9].

B.reject! {|e| е % 3 == 0}.

# с равно [5, 8, 4].

Методы min и max ищут минимальное и максимальное значение в массиве. У каждого метода есть две формы. В первой используется сравнение «по умолчанию», что бы это ни означало в конкретной ситуации (на базе оператора <=>). Во второй форме применяется блок для выполнения нестандартного сравнения.

А = %w[Elrond Galadriel Aragorn Saruman Legolas].

B = a.min # "Aragorn".

С = a.max # "Saruman".

D = a.min {|x,y| x.reverse <=> y.reverse} # "Elrond".

E = a.max {|x,y| x.reverse <=> y.reverse} # "Legolas".

Чтобы найти индекс минимального или максимального элемента (в предположении, что такой элемент один), применяется метод index:

# Продолжение предыдущего примера...

I = a.index a.min # 2.

J = a.index a.max # 3.

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