Iterable collections Dart 문서 읽기


Collection과 Iterable

  • Collection : element의 집합을 표현하는 객체 (e.g. List, Set, Map)

  • Iterable : element에 순차적으로 접근할 수 있는 collection의 한 종류

    • Iterable abstract class를 상속받은 List, Set 등을 통해 Iterable 객체 생성

    • Map은 key를 사용해서 value를 얻는 방식으로 Iterable이 아님. 단, entriesvalues 속성을 통해 key 또는 value group을 Iterable 객체로 읽을 수 있음

    • IterableList는 element에 접근하는 방법에 차이가 있음

      • List[index] operator를 사용하지만, IterableelementAt(index) method를 사용해서 특정 index의 element에 접근

        Iterable<int> iterable = [1, 2, 3];
        print(iterable.elementAt(1)); // 2
        
        List<int> list = [1, 2, 3];
        print(list[1]); // 2
        
      • elementAt()index까지 다른 element들을 무시함 (step through)

Reading elements

  1. for-in loop : Iterable을 순회하며 element 읽기
    const iterable = ["Salad", "popcorn"];
    for (final element in iterable) {
        print(element);
    }
    
  2. first and last : Iterable의 첫 번째 또는 마지막 element 읽기
    Iterable<String> iterable = const ["Salad", "Popcorn", "Toast"];
    iterable.first; // Salad
    iterable.last; // Toast
    
    • Empty Iterable에서 사용하면 StateError 발생
  3. firstWhere(predicate) : 특정 조건을 만족하는 첫 번째 element 읽기
    const items = ["Salad", "Popcorn", "Toast", "Lasagne"];
    final element = iterable.firstWhere(
        (element) => element.length > 5,
        orElse: () => "None!",
    );
    print(element); // Popcorn
    
    • predicate 함수에서 조건을 검사하여 true를 반환하는 첫 번째 element 반환
    • 조건에 만족하는 element를 찾지 못하면,
      • StateError 발생
      • orElse 함수를 전달하면 이 함수가 반환하는 값을 반환
  4. singleWhere(predicate) : 특정 조건을 단 한번만 만족하는 element 읽기
    const items = ["Salad", "Popcorn", "Milk", "Toast", "Lasagne"];
    final element = items.singleWhere(
        (element) => element.startsWith("M") && element.contains("a"),
    );
    print(element); // Milk
    
    • firstWhere()는 조건에 맞는 element를 찾으면 종료
    • singleWhere()Iterable의 모든 element를 순회하므로 infinite 또는 element가 아주 많은 Iterable에서 사용하면 StateError가 발생할 수 있음

Checking conditions

  1. every(predicate) : 모든 element가 predicate 조건을 만족할 때 true 반환
    final isValid = items.every((item) => item.length >= 5);
    
  2. any() : predicate 조건을 만족하는 element가 최소 1개 이상일 때 true 반환
    const items = ["Salad", "Popcorn", "Toast"];
    final any = items.any((item) => item.contains("a")); // true (Salad or Toast)
    final every = items.every((item) => item.length >= 5); //
    
    • any()는 조건을 만족하는 element가 1개라도 있으면 true를 반환
    • any()조건을 만족하는 element가 없다는 것을 검증할 때도 사용 가능 (false를 반환하는 경우)

Filtering elements

  1. where() : 특정 조건을 만족하는 모든 element를 찾아서 새 Iterable(WhereIterable)로 만들어 반환
    final evenNumbers = numbers.where((number) => number.isEven);
    
    • firstWhere()singleWhere()는 조건을 만족하는 element 1개를 찾는다는 차이가 있다.
    • 조건에 맞는 element가 없다면 empty Iterable 반환 (firstWhere()와 달리 StateError를 발생시키지 않음)
  2. takeWhile(predicate) : predicate에서 false를 반환하기 이전 element들을 모아서 새 Iterable로 반환
    const numbers = [1, 3, -2, 0, 4, 5];
    final numbersUntilZero = numbers.takeWhile((number) => number != 0);
    print(numbersUntilZero); // [1, 3, -2]
    
  3. skipWhile() : predicate에서 false를 반환한 이후 element들을 모아서 새 Iterable로 반환
    const numbers = [1, 3, -2, 0, 4, 5];
    final numbersUntilZero = numbers.skipWhile((number) => number != 0);
    print(numbersUntilZero); // [0, 4, 5]
    
    • predicate에서 false를 반환하는 element를 찾으면 이후 element에 대해서는 조건을 검사하지 않음

Mapping elements

  1. map() : Iterable의 각 element들을 새로운 값으로 교체
    Iterable<int> output = numbers.map((number) => number * 10);
    

Other APIs

문서에서 소개한 함수 외에 Iterable 객체에서 사용할 수 있는 유용한 함수들

fold()

const items = ["Salad", "Popcorn", "Milk", "Toast", "Lasagne"];
final count = items.fold(0, (previous, element) => previous + element.length);
print(count); // 28 = 5 + 7 + 4 + 5 + 7
  • collection의 element들을 initialValue와 연산하여 하나의 값으로 합침
  • 다른 언어의 reduce 함수

reduce()

const items = [1, 2, 3];
final sum = items.reduce((value, element) => value + element);
print(sum); // 6
  • fold와 같은 역할이지만, initialValue 없이 collection의 element들을 가지고 연산
  • 다른 언어의 reduce와 달리 결과 type이 원본 collection type으로 고정되어 있음
  • Collection이 비어있다면 IterableElementError 발생

forEach()

const items = ["Salad", "Popcorn", "Toast"];
items.forEach(print); // Salad, Popcorn, Toast
  • 각 element들에 대해 함수 실행

join()

const numbers = [1, 2, 3];
final result = numbers.join("-");
print(result); // 1-2-3
  • Element를 String type으로 변환한 뒤 separator로 연결(concatenate)

contains()

const items = ["Salad", "Popcorn", "Toast"];
items.contains("Toast"); // true
items.contains("Milk"); // false
  • Iterableelement가 들어 있는지 판단
  • Dart의 모든 값은 객체이므로 Object type parameter로 전달 가능

take(count)

const items = ["Salad", "Popcorn", "Milk", "Toast", "Lasagne"];
final result = items.take(3);
print(result); // [Salad, Popcorn, Milk]
  • Iterable의 element들 중 처음 count개의 element를 모아서 lazy iterable 생성
  • 내부적으로 iterator가 count개의 element를 읽은 후에야 iterable을 생성

skip(count)

const items = ["Salad", "Popcorn", "Milk", "Toast", "Lasagne"];
final result = items.skip(3);
print(result); // [Toast, Lasagne]
  • Iterable의 element들 중 처음 count개의 element를 제외한 나머지 element들을 모아서 iterable 생성