The Iterable interface

The Iterable interface can be defined as the common behavior of all classes in the collection framework that supports a mechanism to iterate through all the elements of a collection. It is an object that uses an Iterator interface to iterate over all the elements in a collection. There are two abstract classes, IterableBase and IterableMixin, that implement the Iterator interface. The IterableMixin class is perfectly suited to be extended in the mixin solutions. If you plan to create your own implementation of the Iterable interface, you need to extend one of them. There are many different methods in the Iterable interface to help you manipulate the elements in a collection.

Note

The Iterable interface doesn't support adding or removing elements from a collection.

Properties of the Iterable collection

Here is a list of the read-only properties that are common for all collections:

  • length: This property returns the number of elements in a collection
  • isEmpty: This property returns true if there are no elements in a collection
  • isNotEmpty: This property returns true if there is at least one element in a collection
  • first: This property returns the first element of a collection or throws StateError if the collection is empty
  • last: This property returns the last element of a collection or throws StateError if the collection is empty
  • single: This property returns a single element in a collection. It throws StateError if the collection is empty or has more than one element
  • iterator: This property returns a reference on instance of the Iterator class that iterates over the elements of the collection

Checking the items of a collection on a condition

Sometimes, you want to know if all or any of the objects in a collection comply with a specific condition. The following methods check whether the collection conforms to specific conditions:

  • every: This method returns true if every element in the collection satisfies the specified condition. This can be seen in the following code:
    List colors = ['red', 'green', 'blue'];
    print(colors.every((color) => color != null || color != ''));
    // => true
  • any: This method returns true if one element in the collection satisfies the specified condition. This is illustrated in the following code:
    List colors = ['red', 'green', 'blue'];
    print(colors.any((color) => color == 'red'));
    // => true

The iterate over collection

The following method helps you iterate through elements of the collection:

  • forEach: This method applies the specified function to each element of the collection. This can be seen in the following code:
    List colors = ['red', 'green', 'blue'];
    colors.forEach((color) => print(color));
    // => red
    // => green
    // => blue

The search over collection

The search over collection list includes the following methods that are used to search for an element in a collection:

  • contains: This method returns true if the collection contains an element that is equal to the requested one. This is shown in the following code:
    List colors = ['red', 'green', 'blue'];
    print(colors.contains('red'));
    // => true
  • elementAt: This method returns the indexth element. Exactly which object is returned depends on the sorting algorithm implemented by the specific collection class that you're using, as shown in the following code:
    List colors = ['red', 'green', 'blue'];
    print(colors.elementAt(0));
    // => red

    Note

    If the collection does not support ordering, then the result of calling elementAt may be any element.

  • firstWhere: This method returns the first element in the collection that satisfies the given predicate test or the result of the orElse function. It throws StateError if orElse was not specified, as illustrated in the following code:
    List colors = ['red', 'green', 'blue'];
    print(colors.firstWhere((color) => color == 'orange', 
    orElse:() => 'orange'));
    // => orange
  • lastWhere: This method returns the last element in the collection that satisfies the given predicate test or the result of the orElse function. It throws StateError if orElse was not specified, as shown in the following code:
    List colors = ['red', 'green', 'blue'];
    print(colors.lastWhere((color) => color != 'orange', 
        orElse:() => ''));
    // => blue
  • singleWhere: This method returns a single element of the collection that satisfies the test. If the collection is empty or more than one element matches, then it throws StateError. The code is as follows:
    List colors = ['red', 'green', 'blue'];
    print(colors.singleWhere((color) => color == 'red'));
    // => red

Creating a new collection

The following list includes methods to create a new collection from the original one; all of them return Lazy Iterable results:

  • expand: This method returns new collections by expanding each element of the original one to zero or more elements. This can be seen in the following code:
    List colors = ['red', 'green', 'blue'];
    print(colors.expand((color) {
      return color == 'red' 
          ? ['orange', 'red', 'yellow']
          : [color];
    }));
    // => [orange, red, yellow, green, blue]
  • map: This method creates a new collection of elements based on the elements from the original collection that are transformed with specified function. The code is as follows:
    List colors = ['red', 'green', 'blue'];
    print(colors.map((color) {
      if (color == 'green') return 'orange';
      if (color == 'blue') return 'yellow';
      return color;
    }));
    // => ['red', 'orange', 'yellow']
  • take: This method returns an Iterable collection with a specified number of elements from the original collection. The value of these elements must not be negative. If the number of requested elements is more than the actual number of elements, then it returns all the elements from the collection:
    List nums = [1, 2, 3, 4, 5, 6];
    print(nums.take(7));
    // => [1, 2, 3, 4, 5, 6]
  • takeWhile: This method returns an Iterable collection that stops once the test is not satisfied anymore. This is illustrated in the following code:
    List nums = [1, 2, 3, 4, 5, 6];
    print(nums.takeWhile((element) => element < 5));
    // => [1, 2, 3, 4]
  • skip: This method returns an Iterable collection that skips the specified number of initial elements. If it has fewer elements than the specified number, then the resulting Iterable collection is empty. Also, the specified number must not be negative. The code is as follows:
    List nums = [1, 2, 3, 4, 5, 6];
    print(nums.skip(4));
    // => [5, 6]
  • skipWhile: This method returns an Iterable collection that skips the elements while the test is satisfied. This is shown in the following code:
    List nums = [1, 2, 3, 4, 5, 6];
    print(nums.skipWhile((element) => element <= 4));
    // => [5, 6]
  • where: This method returns a Lazy Iterable collection with all the elements that satisfy the predicate test. This is illustrated in the following code:
    List nums = [1, 2, 3, 4, 5, 6];
    print(nums.where((element) => element > 1 && element < 5));
    // => [2, 3, 4]
  • toList: This method creates a list that contains the elements of the original collection. It creates a fixed length List if the growable attribute is false:
    List nums = [1, 2, 3];
    print(nums.toList(growable:false));
    // => [1, 2, 3]
  • toSet: This method creates a set that contains the elements of the original collection. It ignores the duplicate elements. The code is as follows:
    List nums = [1, 2, 1];
    print(nums.toSet());
    // => {1, 2}

Reducing a collection

The following list includes methods to reduce the number of elements in a collection:

  • reduce: This method reduces the collection to a single value by iteratively combining the elements of the collection using the provided function. If the collection is empty, this results in StateError. In the following example, we will calculate the sum of all elements in the collection:
    List nums = [1, 2, 3];
    print(nums.reduce((sum, element) => sum + element));
    // => 6
  • fold: This method reduces the collection to a single value by iteratively combining each element of the collection with an existing value using the specified function. Here, we have to specify the initial value and aggregation function:
    List nums = [1, 2, 3];
    print(nums.fold(0, (acc, element) => acc + element));
    // => 6

Converting a collection

The following method is used to convert all the elements of a collection:

  • join: This method converts each element of the collection into a string and returns the concatenated result separated with an optional separator. If the collection is empty, it doesn't actually modify the type of the elements in the collection, but just returns an empty string:
    List nums = [1, 2, 3];
    print(nums.join(' - '));
    // => 1 - 2 - 3

Generating a collection

The Iterable interface has a factory method that helps to create a new Iterable interface and is filled with a specified number of values generated by a generator function, as shown in the following code:

Iterable generated = new Iterable.generate(4, 
      (count) => "Is $count");
print(generated);
// => [Is 0, Is 1, Is 2, Is 3] 

If the generator function is absent, this method generates a collection with only the integer values:

Iterable generated = new Iterable.generate(4);
print(generated);
// => [0, 1, 2, 3]

The Lazy Iterable

The Lazy Iterable term is used plenty of times in Iterable interfaces. It is an iteration strategy that delays the iteration of a collection until its value is needed and avoids repeated iterations. In the following example, our code iterates over the list of numbers.

The where method prints the information about the current fetched element. This function calls the object only when we actually fetch the element in the forEach method of the Iterable interface, as shown in the following code:

lazyIterable() {
  List nums = [1, 2, 3];
  print('Get Iterable for $nums'),
  Iterable iterable = nums.where((int i) {
    print('Fetched $i'),
    return i.isOdd;
  });
  print('Start fetching'),
  iterable.forEach((int i) {
    print("Received $i");
  });
}

Here is the output of the preceding function:

Get Iterable for [1, 2, 3]
Start fetching
Fetched 1
Received 1
Fetched 2
Fetched 3
Received 3

The following are the benefits of the Lazy Iterable:

  • The performance increases because unnecessary iterations are avoided
  • The memory usage footprint decreases because the values are iterated when needed
  • It helps to create infinite data structures

Bear in mind that iteration over the Lazy Iterable could be much slower than a normal iteration because the code incurs the cost of an invocation to fetch the next item from the Iterable source.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.253.210