Перейти к основному содержимому

Фильтрация коллекции

Фильтрация — это выбор из коллекции тех элементов, которые подходят под условие.

Условие записывается как функция, которая принимает один элемент и возвращает true (оставить его) или false (пропустить его). Такую функцию называют предикатом.

Фильтрация создаёт новый слайс. Исходный слайс не изменяется.

В Go нет встроенного метода filter у слайсов. Фильтровать можно циклом for с if или один раз написать небольшой обобщённый хелпер и переиспользовать его. Хелпер ниже работает со слайсом любого типа.

В стандартной библиотеке есть slices.DeleteFunc(), которая удаляет элементы, подходящие под условие. Будьте внимательны: она изменяет исходный слайс, а не создаёт новый.

Синтаксис обобщённого хелпера Filter

T обозначает тип элемента. keep — это предикат: элементы, для которых он возвращает true, копируются в result.

main.go
func Filter[T any](items []T, keep func(T) bool) []T {
  var result []T
  for _, v := range items {
    if keep(v) {
      result = append(result, v)
    }
  }
  return result
}

Фильтрация по предикату

Самый простой способ отфильтровать — передать предикат хелперу Filter. Хелпер проходит по слайсу и оставляет каждый элемент, который проходит проверку.

У map своего хелпера нет. Чтобы отфильтровать map, переберите его с помощью range и скопируйте подходящие пары ключ:значение в новый map.

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

Чтобы оставить элементы, которые не подходят под условие, поставьте перед условием !. Отдельной функции filterNot в Go нет.

Слайс типа []any может хранить значения разных типов. Приведение типа вида v.(string) проверяет, является ли значение строкой, и возвращает его с этим типом. Указатель может быть nil, что означает «нет значения»; сравнивайте с nil, чтобы пропустить такие записи.

Фильтрация слайса

Оставляем только слова длиннее 3 букв. Условие len(s) > 3 передаётся в Filter как функция.

Prompt
Напишите обобщённый хелпер Filter, который принимает слайс и функцию-условие и возвращает новый слайс только с теми элементами, которые проходят проверку. Примените его к словам one, two, three, four, чтобы оставить слова длиннее 3 букв. Напечатайте результат.
Вывод: программа на Go и захваченный вывод
Filter a slice

Фильтрация map

Перебираем map и оставляем пары, ключ которых заканчивается на "1", а значение больше 10. strings.HasSuffix() проверяет окончание строки.

Prompt
Создайте map, который сопоставляет ключам ключ1, ключ2, ключ3, ключ11 значения 1, 2, 3, 11. Переберите его и скопируйте в новый map только те пары, ключ которых заканчивается на «1», а значение больше 10. Напечатайте новый map.
Вывод: программа на Go и захваченный вывод
Filter a map

Фильтрация по индексу

range даёт индекс и значение. Оставляем слово только тогда, когда его индекс не равен 0 и в нём меньше 5 букв.

Prompt
Возьмите слова one, two, three, four. Пройдите по ним циклом с range, чтобы получить и индекс, и слово. Оставьте слово только в том случае, если его индекс не равен 0 и в нём меньше 5 букв. Напечатайте оставленные слова.
Вывод: программа на Go и захваченный вывод
Filter using the index

Фильтрация с отрицанием условия

Отбрасываем слова из 3 букв или короче. Условие len(s) = 3 инвертируется с помощью !, поэтому остаются только более длинные слова.

Prompt
Возьмите слова one, two, three, four. Используйте обобщённый хелпер Filter, но инвертируйте условие с помощью !, чтобы слова из 3 букв или короче отбрасывались. Напечатайте результат.
Вывод: программа на Go и захваченный вывод
Filter with a negated condition

Фильтрация по типу

Слайс содержит nil, целое число, две строки и число с плавающей точкой. Приведение типа v.(string) вместе со значением ok находит строки. Каждая из них печатается заглавными буквами.

Prompt
Создайте слайс типа []any, который содержит nil, 1, «two», 3.0 и «four». Пройдите по нему циклом и с помощью приведения типа найдите значения, которые являются строками. Напечатайте каждую строку заглавными буквами, по одной в строке.
Вывод: программа на Go и захваченный вывод
Filter by type

Отсеивание значений nil

Слайс содержит указатели на строки, и некоторые из них равны nil. Оставляем указатели, которые не равны nil, и собираем значения, на которые они указывают, с помощью *p.

Prompt
Создайте слайс указателей на строки, который содержит nil, указатель на «one», nil и указатель на «two». Оставьте только указатели, не равные nil, и соберите строки, на которые они указывают. Напечатайте собранный слайс.
Вывод: программа на Go и захваченный вывод
Filter out nil values

Разделение на две части

Иногда нужны обе группы: и элементы, которые подходят под условие, и те, которые не подходят.

В Go нет встроенного partition. Это делает небольшой обобщённый хелпер: он проходит по слайсу один раз и добавляет каждый элемент либо в слайс match, либо в слайс rest. Хелпер возвращает оба слайса, потому что функция в Go может возвращать больше одного значения.

Разделение слайса надвое

Слова длиннее 3 букв попадают в match, все остальные — в rest. Печатаются оба слайса.

Prompt
Напишите обобщённый хелпер Partition, который разделяет слайс надвое: элементы, проходящие условие, и все остальные. Примените его к словам one, two, three, four с условием «длиннее 3 букв». Напечатайте слайс с подходящими элементами, а затем остальные.
Вывод: программа на Go и захваченный вывод
Split a slice in two

Проверка предикатов

Иногда нужны не сами подходящие элементы, а только ответ «да» или «нет».

  • any: проходит ли проверку хотя бы один элемент? На это отвечает slices.ContainsFunc().
  • none: не проходит ли проверку ни один элемент? Поставьте ! перед slices.ContainsFunc().
  • all: проходит ли проверку каждый элемент? В Go нет функции для этого, поэтому используйте небольшой обобщённый хелпер All.

Без условия вопрос «есть ли хоть один элемент?» — это len(s) > 0, а «нет ни одного элемента?» — это len(s) == 0.

На пустом слайсе All возвращает true. Нет ни одного элемента, который не прошёл бы проверку, поэтому условие ни разу не нарушено. Это называется тривиальной истинностью.

any

Проверяем, заканчивается ли хотя бы одно слово на "e", а затем проверяем, есть ли в слайсе хоть один элемент.

Prompt
Возьмите слова one, two, three, four. С помощью slices.ContainsFunc проверьте, заканчивается ли хотя бы одно слово на «e», и напечатайте ответ. Затем напечатайте, есть ли в слайсе хоть один элемент, проверив, что его длина больше 0.
Вывод: программа на Go и захваченный вывод
any

none

Проверяем, что ни одно слово не заканчивается на "a", а затем проверяем, пуст ли слайс.

Prompt
Возьмите слова one, two, three, four. С помощью ! и slices.ContainsFunc проверьте, что ни одно слово не заканчивается на «a», и напечатайте ответ. Затем напечатайте, пуст ли слайс, проверив, что его длина равна 0.
Вывод: программа на Go и захваченный вывод
none

all

Проверяем, заканчивается ли каждое слово на "e". Затем вызываем All на пустом слайсе, что даёт true.

Prompt
Напишите обобщённый хелпер All, который возвращает true только тогда, когда каждый элемент слайса проходит условие. Проверьте, все ли слова one, two, three, four заканчиваются на «e», и напечатайте ответ. Затем вызовите All на пустом слайсе с тем же условием и напечатайте ответ.
Вывод: программа на Go и захваченный вывод
all