Iterable

Последнее обновление: 07.04.2023

Класс Iterable<E> представляет базовый тип для других типов коллекций, в частности, для типов List и Set, и предоставляет ряд общей функциональности. Рассмотрим некоторые общие методы, которые предоставляет класс Iterable для других типов коллекций.

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

Метод where() позволяет выбрать элементы по условию. Результов метода является отфильтрованный набор Iterable:

where(bool test(E element)) -> Iterable<E>

Условие фильтрации представляет функцию, которая передается в метод where в качестве параметра. Например, выберем строки с длиной в 3 символа:

void main (){
	
    var list = ["Tom", "Alice", "Bob", "Sam", "Kate"];
    var new_list = list.where((item)=> item.length==3);
    print(new_list);   // (Tom, Bob, Sam)
} 

Преобразование

Метод map() позволяет преобразовать данные коллекции:

map<T>(T toElement(E e)) -> Iterable<T>

В качестве параметра метод принимает функцию преобразования. В эту функцию в качестве параметра передается каждый элемент коллекции и возвращается преобразованное значение. Результатом метода map() является новая коллекция из преобразованных данных. Стоит отметить, что данный метод также доступен и для класса Map. Например:

void main (){
	const numbers = [1, 2, 3, 4];
    final squares = numbers.map((n) => n * n);
    print(squares);     // (1, 4, 9, 16)
}

Здесь в метод map передается анонимная функция (n) => n * n. Параметр n представляет элемент списка, а в качестве результата возвращается квадрат числа. Таким образом, squares будет представлять коллекцию (1, 4, 9, 16)

reduce. Сведение элементов

Метод reduce() позволяет свести элементы коллекции к одному значению.

reduce(E combine(E value, E element)) -> E

В качестве параметра метод принимает функцию с двумя параметрами. Первый параметр функции представляет сведенное значение, а второй параметр - текущий элемент коллекции. Например, получим сумму всех элементов списка:

void main (){
	const numbers = [1, 2, 3, 4];
    final sum = numbers.reduce((total, element) => total += element);
    print(sum);     // 10
}

В данном случае в метод reduce передается анонимная функция

(total, element) => total += element

Параметр total представляет сведенное значение, которое будет возвращаться из метода reduce. А второй параметр - текущий перебираемый элемент. В самой функции прибавляем к total значение текущего переданного элемента. При вызове метода reduce каждый элемент списка будет передаваться в анонимную функцию. И таким образом, каждый элемент будет прибавлен к total, и мы получим сумму элементов.

fold

Метод fold() также позволяет свести все элементы коллекции к одному значению.

fold<T>(T initialValue, T combine(T previousValue, E element)) -> T

Но в отличие от метода reduce() метод fold() позволяет задать начальное значение. Это может быть полезно, если коллекция пуста.

Первый параметр метода fold() представляет начальное значение, а второй - функция, аналогичная функции из метода reduce(). Например:

void main (){
	const numbers = <int>[];
    final sum = numbers.fold(0, (total, element) => total += element);
    print(sum);     // 0
}

Здесь в метод fold передается та же функция, что и в примере для reduce, которая вычисляет сумму элементов. Однако теперь также задано начальное значение - 0. И поскольку коллекция numbers в данном случае пуста, то метод fold возвращает число 0.

Если бы мы в этом случае использовали метод reduce, а не fold:

final sum = numbers.reduce((total, element) => total += element);

то мы получили бы ошибку.

Проверка элементов

Методы every() и any() проверяют соответствие элементов некоторому условию, которое передается в виде функции в метод:

any(bool test(E element)) -> bool
every(bool test(E element)) -> bool

Метод any() возвращает true, если хотя бы один элемент соответствует условию. А метод every() возвращает true, если все элементы соответствуют условию.

void main (){
	var list = ["Tom", "Bob", "Sam", "Kate"];
    
    print(list.every((element)=>element.length > 2));    // true
    print(list.every((element)=>element.length > 3));   // false

    print(list.any((element)=>element.length > 3));    // true
    print(list.any((element)=>element.length > 4));   // false
}

Пропуск элементов

Метод skip() позволяет пропустить некоторое количество элементов.

skip(int count) -> Iterable<E>

В качестве параметра в метод передается количество пропускаемых элементов. Результатом является все элементы коллекции за исключением пропущенных элементов:

void main (){
	var list = ["Tom", "Bob", "Sam", "Kate", "Alice"];
    var new_list = list.skip(2);    // пропускаем 2 первых элемента
    print(new_list);    // (Sam, Kate, Alice)
}

Метод skipWhile() позволяет пропустить элементы с начала коллекции, которые соответствуют условию.

skipWhile(bool test(E value)) -> Iterable<E>

В качестве параметра в метод передается функция-условие. Результатом является все элементы коллекции за исключением пропущенных элементов:

void main (){
	var list = ["Tom", "Bob", "Kate", "Alice", "Sam"];
    var new_list = list.skipWhile((elem) => elem.length==3);  // пропускаем первые строки с длиной в 3 символа
    print(new_list);    // (Kate, Alice, Sam)
}

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

Взятие элементов

Метод take() позволяет взять из коллекции некоторое количество элементов.

take(int count) -> Iterable<E>

В качестве параметра в метод передается количество пропускаемых элементов. Результатом является новая коллекция:

void main (){
	var list = ["Tom", "Bob", "Kate", "Alice", "Sam"];
    var new_list = list.take(3);    // берем 3 первых элемента
    print(new_list);    // (Tom, Bob, Kate)
}

Метод takeWhile() позволяет извлекать элементы с начала коллекции, пока они соответствуют условию.

takeWhile(bool test(E value)) -> Iterable<E>

В качестве параметра в метод передается функция-условие. Результатом являются извлеченные элементы в виде новой коллекции:

void main (){
	var list = ["Tom", "Bob", "Kate", "Alice", "Sam"];
    var new_list = list.takeWhile((elem) => elem.length==3);  // берем первые строки с длиной в 3 символа
    print(new_list);    // (Tom, Bob)
}

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

Цепочки методов

Стоит отметить, что методы, которые возвращают другой объект Iterable (например, map или where) характеризируются отложенным выполнением. Это значит, что они в реальности выполняются, когда мы непосредственно получаем эти элементы (например, обращаемся к конкретному элементу по индексу, перебираем в цикле). Благодаря этому можно создавать цепочки методов, которые произведут одновременно несколько различных операций с данными.

Например, выберем по условию данные и переобразуем их:

class Person{
    String name;
    Person(this.name);
}

void main (){
	var people = [Person("Tom"), Person("Bob"), Person("Kate"), Person("Alice"), Person("Sam")];
    var new_people = people
                        .where((person) => person.name.length == 3)  // выбираем, если длина имени - 3 символа
                        .map((person) => person.name);               // преобразуем объекты Person в строки
    print(new_people);    // (Tom, Bob, Sam)
}

В данном случае мы имеет список people из объектов Person. К этому списку применяем метод where(), который выбирает объекты Person с именем в 3 символа. Далее к результату этого метода применяется метод map(), который из объектов Person выбирает их имена и помещает их в результирующую коллекцию.

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

print(new_people);    // (Tom, Bob, Sam)

Мы это можем увидеть наглядно. Изменим программу следующим образом:

class Person{
    String name;
    Person(this.name);
}

void main (){
	var people = [Person("Tom"), Person("Bob"), Person("Kate"), Person("Alice"), Person("Sam")];
    var new_people = people
                        .where((person){
                                    print("where for ${person.name}");
                                    return person.name.length == 3;
                                })
                        .map((person){
                                print("map for ${person.name}");
                                return person.name;
                            });
    print("Test where and map");
    print(new_people);    // (Tom, Bob, Sam)
}

Консольный вывод:

Test where and map
where for Tom
map for Tom
where for Bob
map for Bob
where for Kate
where for Alice
where for Sam
map for Sam
(Tom, Bob, Sam)

Другой пример вызова цепочки методов - постраничная навигация по данным:

void main (){
	var people = ["Tom", "Bob", "Kate", "Alice", "Sam"];
    var page2 = people.skip(2).take(2); // пропускаем 2 элемента и берем 2 элемента (3 и 4-й)
    print(page2);    // (Kate, Alice)
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850