Iterables in Dart

What are Iterables?

Iterables are basic building blocks for all sorts of Dart applications.

An Iterable is a collection of elements that can be accessed sequentially.

The elements of the iterable are accessed by getting an [Iterator] using the [iterator] getter, and using it to step through the values. Stepping with the iterator is done by calling [Iterator.moveNext], and if the call returns true, the iterator has now moved to the next element, which is then available as [Iterator.current]. If the call returns false, there are no more elements, and iterator.current returns null.

Create a new Iterable

In Dart, an Iterable is an abstract class, meaning that you can’t instantiate it directly. However, you can create a new Iterable by creating a new List or Set.

The List and Set classes are both Iterable.

// List
Iterable<String> listIterable = ['Product 1', 'Product 2', 'Product 3', 'Product 4'];

  // sets
Iterable<String> setIterable = {'Product 1', 'Product 2', 'Product 3', 'Product 4'};

elementAt method

As like List you cannot use operator '[]' index to access the elements in iterable.

The elementAt() method returns the [index]th element.

How it works?

  1. The elementAt() which steps through the elements of the iterable until it reaches that position.
  2. Returns the value from that position.

Note: The [index] must be non-negative and less than [length] of the iterable.

Index zero represents the first element so iterable.elementAt(0) is equivalent to iterable.first.

main() {
  Iterable<String> listIterable = [
    'Product 0',
    'Product 1',
    'Product 2',
    'Product 3',
  ];

  var value = listIterable.elementAt(1);

  print(value); // Product 1
}

for-in loop

Using the for-in loop construct to iterate over the elements of an Iterable.

The following example, which reads the each element of an iterable sequentially.

main() {
  Iterable<String> listIterable = [
    'Product 0',
    'Product 1',
    'Product 2',
    'Product 3',
  ];

  // iterate over the elements
  for (var element in listIterable) {
    // each element print to console
    print(element);
  }
}

first property

As like List you cannot use operator '[]' index to access the elements in iterable, so you can’t call iterable[0] to access the first element. Instead, you can use first, which gets the first element.

last property

The Iterable class's read-only property which returns the first element.

The following example, shows access the first element of an Iterable

main() {
  Iterable<String> listIterable = [
    'Product 0',
    'Product 1',
    'Product 2',
    'Product 3',
  ];

  listIterable.first; // Product 0
}

Note: Using first on an empty Iterable results in a StateError.

main() {
  Iterable<String> emptyIterable = [];

  print(emptyIterable.first); // Unhandled exception: No element
}

The Iterable class's read-only property which returns the last element.

With the Iterable class, you can’t use the operator [] to access the last element, Instead, you can use the last property

How it works?

  1. Steps through the all elements of the iterable until it reaches the last element.
  2. Returns the last element.

The following example, show access the last element of an Iterable.

main() {
  Iterable<String> listIterable = [
    'Product 0',
    'Product 1',
    'Product 2',
    'Product 3',
  ];

  // Returns the last element
  listIterable.last; // Product 3
}

Note: Because accessing the last element of an Iterable requires stepping through all the other elements, last can be slow.

Note: Using last on an empty Iterable results in a StateError.

main() {
  Iterable<String> emptyIterable = [];

  print(emptyIterable.last); // Unhandled exception: No element
}

firstWhere method

The Iterable class method which returns the first element that satisfies the given predicate.

Use firstWhere() to find the first element that satisfies certain conditions.

How it works?

  1. Iterates through elements returns true if the input satisfy a certain condition (predicate).
  2. If no element satisfies the predicate in firstWhere(), throw a StateError exception.
<!-- For example, if you want to find the first String that has more than 5 characters, you must pass a predicate that returns true when the element size is greater than 5. -->
main() {
  Iterable<String> listIterable = [
    'Product 0',
    'Product 1',
    'Product 2',
    'Product 2',
  ];

  // Returns the first element that satisfies the given predicate.
  var element = listIterable.firstWhere((element) => element == 'Product 2');

  print(element); // Product 2
}

where method

The Iterable class method which returns a new lazy [Iterable] with all elements that satisfy the certain condition (predicate).

Use the where() method find all the elements that satisfy a certain condition.

The matching elements have the same order in the returned iterable as they have in [iterator].

How it works?

  1. The filtering happens by stepping through elements until no element satisfies the predicate.
  2. If no element satisfies the predicate in where(), then the method returns an empty Iterable.
main() {
  Iterable<String> skills = ['Node', 'Angular', 'React', 'Dart', 'Flutter'];

  var result = skills.where((element) => element.contains('a'));

  print(result); // (Angular, React, Dart)

  for (var element in result) {
    print('$element Tutorial');
  }
}

takeWhile method

The Iterable class method which returns a lazy iterable of the leading elements satisfying certain condition (predicate).

Use takeWhile() returns an Iterable that contains all the elements leading to the element that satisfies the predicate.

How it works?

  1. The filtering happens lazily. Every new iterator of the returned iterable starts iterating over the elements of this
  2. The elements can be computed by stepping through [iterator] until an element is found where test(element) is false. At that point, the returned iterable stops (its moveNext() returns false).
Iterable<String> skills = ['Node', 'Angular', 'React', 'Dart', 'Flutter'];

   var skillsUntilDart = skills.takeWhile((element) => element != 'Dart');
   print('get skills until Dart: $skillsUntilDart');

skipWhile method

The Iterable class method which returns an Iterable that skips leading elements while certain condition (predicate) is satisfied.

Use skipWhile() returns an Iterable while skipping all the elements before the one that satisfies the predicate.

How it works?

  1. The filtering happens lazily. Every new [Iterator] of the returned iterable iterates over all elements of this.
  2. The returned iterable provides elements by iterating this iterable, but skipping over all initial elements where test(element) returns true. If all elements satisfy test the resulting iterable is empty, otherwise it iterates the remaining elements in their original order, starting with the first element for which test(element) returns false.
Iterable<String> skills = ['Node', 'Angular', 'React', 'Dart', 'Flutter'];

  var skillsAfterDart = skills.skipWhile((element) => element != 'Dart');

  print(
      'get skills after Dart: $skillsAfterDart'); // get skills after Dart: (Dart, Flutter)

map method

The Iterable class method which returns a new lazy [Iterable] with elements that are created by calling f on each element of this Iterable in iteration order.

Use map() to replacing an element with a new one:

Iterable<String> skills = ['Node', 'Angular', 'React', 'Dart', 'Flutter'];

  var result = skills
      .where((element) => element == 'Node')
      .map((element) => element = 'Nodejs');

  print(result); // Nodejs

  for (var element in result) {
    print('$element Tutorial');
  }

Use map() to transform an element into a different object:

main() {
  Iterable<int> numbers = [1, 2, 3, 4, 5];

  Iterable<String> output = numbers.map((number) => number.toString());

  print(output); // (10, 20, 30, 40, 50)

  for (var number in output) {
    print('$output is convert to string');
  }
}

Note: map() returns a lazy Iterable, meaning that the supplied function is called only when the elements are iterated.