window method

  1. @Possible({RangeError})
  2. @lazy
  3. @useResult
Iterable<List<E>> window({
  1. required int size,
  2. int by = 1,
  3. bool partial = false,
})

Split this iterable using a sliding window.

The window's size is controlled by size while by controls the amount by which to increase the window's starting index. partial controls whether partial windows smaller than size at the end are emitted.

Contract

Throws a RangeError if size <= 0 or by <= 0.

Example

// Overlapping windows
[1, 2, 3, 4, 5].split.window(size: 3, by: 2); // [[1, 2, 3], [3, 4, 5]]

// Non-overlapping windows
[1, 2, 3, 4, 5].split.window(size: 2, by: 3); // [[1, 2], [4, 5]]


// No partial windows
[1, 2, 3, 4].split.window(size: 3, by: 2); // [[1, 2, 3]]

// Partial windows
[1, 2, 3, 4].split.window(size: 3, by: 2, partial: true); // [[1, 2, 3], [3, 4]]

Implementation

@Possible({RangeError})
@lazy @useResult Iterable<List<E>> window({required int size, int by = 1, bool partial = false}) sync* {
  RangeError.checkValidRange(1, null, size);
  RangeError.checkValidRange(1, null, by);

  final iterator = _iterable.iterator;
  final window = <E>[];
  final overlap = max(0, size - by);
  final skip = max(0, by - size);
  final unused = size - overlap;

  while (true) {
    for (var i = window.length; i < size; i++) {
      if (!iterator.moveNext()) {
        if (partial && overlap < window.length) {
          yield [...window];
        }

        return;
      }

      window.add(iterator.current);
    }

    yield [...window];

    window.removeRange(0, unused);

    for (var i = 0; i < skip; i++) {
      if (!iterator.moveNext()) {
        return;
      }
    }
  }
}