H.org
  • Questions

Dart: Iterables

  1. dart
  2. fundamentals

An iterable is a collection of items that you are able to "step" or iterate through sequentially. In Dart, Iterable is an abstract class which exposes an API for retrieving items.

Both List and Set collection classes are predictable in the way they return items. This is because they store each item in a specific position. For Map collections, both the keys and values are iterable. The default internal implementation of Map uses LinkedHashMap, which iterates the keys in the order they were inserted.

Getting an iterator

You can directly create an Iterable using the default growable list shorthand:

Iterable<String> characters = <String>[
'Cloud Strife',
'Aerith Gainsborough',
'Barrett Wallace',
'Red XIII',
'President Shinra',
'Sephiroth'
];

This will give you a plain Iterable with no additional efficient item access methods.

💡 Tip
You should always use the most specific collection type that you require for your needs. If you only need to for...in loop over a collection, use Iterable. If you need to access items in the collection via an index, use List.

To get an iterator for a collection you access the iterator property:

Iterator<String> iterator = characters.iterator;

Test predicate

You'll see a few examples coming up that use a test predicate. This is a function that returns a boolean value, very much like the expression in an if () statement.

These predicates are used to:

  • Determine whether the iterable:
    • contains any item/s. (any)
    • contains specific item/s (contains)
    • has all items that match specific conditions (every)
  • Retrieve:
    • all items where they satisfy the test (where)
    • the first item that satisfies the test (firstWhere)
    • a single item that satisfies the test (singleWhere)

I won't go into too much detail about some of these methods, so just keep in mind that a predicate is a test that specifies conditions that pass or fail for items in the iterable.

Movement

The iterator is like a shuttle in a hand loom, it moves along the collection's items, replacing its current property with the value it finds at each step.

In order to "move" the iterator to the next item, you use the moveNext() method:

iterator.moveNext();

If this method returns true, it has successfully been able to move to the next element. If there are no more items moveNext() will return false, this indicates that it cannot move any further.

It is safe to repeatedly call moveNext() even when there are no items, it will just keep returning false.

Initial value

The initial value of an iterator's current property is null. This is because the iterator starts positioned before the first item in the collection.

Iterator<String> iterator = characters.iterator;
print(iterator.current); // null

Performing checks

There are various properties and methods on an Iterable you can use in order to determine its current state:

// Returns the total number of items in the iterable
characters.length; // 6

// Checks if the iterable is empty
characters.isEmpty; // false

// Convenience method for checking the opposite of isEmpty
characters.isNotEmpty; // true

// Checks if any items in the iterable satisfy the test predicate
characters.any((item) => item == 'Red XIII')); // true

// Returns true if the iterable contains the item provided
characters.contains('Barrett Wallace'); // true
characters.contains('Cait Sith'); // false

// Checks if the iterable has a single item in it and returns
// that item. If it contains more than one item a StateError
// will be thrown.
characters.single; // StateError

Getting items

You can easily get the first and last items in an Iterable using first or last:

print(characters.first);
print(characters.last);
Cloud Strife
Sephiroth

🚧 Warning
Using last can be very slow since it has to iterate over all items to find the last one. Some iterables, such as List, have efficient ways of finding the last item. Make sure you understand the performance implications for a particular collection type and choose appropriately.

Single items

If you try to access an item at a specific index using [] the compiler will complain that this operator is not defined for Iterables. This is because the Iterable cannot guarantee that accessing an item by an index is efficient.

Instead, you can use elementAt():

print(iterable.elementAt(1));
Aerith Gainsborough

This makes the Iterable move to the given index (1) and return the item it has. Index 0 represents the first item, so iterable.elementAt(0) is equivalent to iterable.first.

Loops

You may not realise it, but a for...in loop actually uses an iterator under the hood, the language just hides away this complexity:

for (String name in characters) {
print(name);
}

Iterables also define a forEach() method which loops over each item in the Iterable just like for...in, but is more "functional":

characters.forEach((String character) {
print(character);
})

Since moveNext() returns a boolean, this can be used as the control statement for a while loop:

Iterator<String> iterator = characters.iterator;

while(iterator.moveNext()) {
print(iterator.current);
}

This will continually execute until there are no more items left.

Filtering items

Finding items in an Iterable is simple and there are several methods in Dart that make life easy.

Where

Where iterates over the list and returns items that satisfy the test predicate:

Iterable<String> filteredCharacters =
characters.where((item) => item.contains('a'));
Aerith Gainsborough
Barrett Wallace
President Shinra

Skip

You can skip items using the skip() method. This returns an Iterable containing all but the first x number of items:

characters.skip(2);
Barrett Wallace
Red XIII
President Shinra
Sephiroth

Take

This takes x number of items from the Iterable and returns them:

Iterable<String> smallerCharacterList = characters.take(2);
Cloud Strife
Aerith Gainsborough

The take() method returns a lazy iterable and the function provided will not execute until characterNameLengths itself is iterated over.

Mapping items

The map() method allows you to iterate over each item in the Iterable, executing a function for each one:

Iterable<int> characterNameLengths =
characters.map((item) => item.length);

Just like take(), map() returns a lazy Iterable.

Notice we are mapping to a completely different data type (Iterable<int>), transformation is just one of the things you can do with it. You will see map() used later in examples when creating Widget instances for dynamic UIs.

Converting to different collection types

To List

To create a list from your Iterable, you can use the toList() method:

characters.toList();

You can also pass a boolean value to toList() in order to create a growable list:

List<String> listOfCharacters = characters.toList(true);

To Set

To create a Set from your Iterable, you can use the toSet() method:

Set<String> setOfCharacters = characters.toSet();

The set may contain fewer items because duplicate items will be discarded. The order of the items in the set is not guaranteed to be the same as the Iterable.


There are a lot of other methods I've not gone into detail about here, these are just the essentials to get you working with iterables.

Ko-Fi button Support Me on Ko-fi

Created with lots of green tea and 11ty

Opinions are solely my own and not those of my employer.

Privacy Policy - Licenses