2013-01-18 14:08:41 +00:00
|
|
|
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
|
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
|
2014-01-16 12:06:38 +00:00
|
|
|
part of dart._internal;
|
2013-01-18 14:08:41 +00:00
|
|
|
|
2013-10-14 10:40:00 +00:00
|
|
|
/**
|
|
|
|
* Marker interface for [Iterable] subclasses that have an efficient
|
|
|
|
* [length] implementation.
|
|
|
|
*/
|
|
|
|
abstract class EfficientLength {
|
|
|
|
/**
|
|
|
|
* Returns the number of elements in the iterable.
|
|
|
|
*
|
|
|
|
* This is an efficient operation that doesn't require iterating through
|
|
|
|
* the elements.
|
|
|
|
*/
|
|
|
|
int get length;
|
|
|
|
}
|
2013-07-04 11:38:45 +00:00
|
|
|
|
2013-02-14 09:51:43 +00:00
|
|
|
/**
|
|
|
|
* An [Iterable] for classes that have efficient [length] and [elementAt].
|
|
|
|
*
|
|
|
|
* All other methods are implemented in terms of [length] and [elementAt],
|
|
|
|
* including [iterator].
|
|
|
|
*/
|
2013-10-14 10:40:00 +00:00
|
|
|
abstract class ListIterable<E> extends IterableBase<E>
|
|
|
|
implements EfficientLength {
|
2013-02-14 09:51:43 +00:00
|
|
|
int get length;
|
|
|
|
E elementAt(int i);
|
|
|
|
|
2013-02-25 10:48:09 +00:00
|
|
|
const ListIterable();
|
|
|
|
|
2013-02-18 10:35:51 +00:00
|
|
|
Iterator<E> get iterator => new ListIterator<E>(this);
|
2013-02-14 09:51:43 +00:00
|
|
|
|
|
|
|
void forEach(void action(E element)) {
|
|
|
|
int length = this.length;
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
action(elementAt(i));
|
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-28 14:14:26 +00:00
|
|
|
bool get isEmpty => length == 0;
|
2013-02-14 09:51:43 +00:00
|
|
|
|
|
|
|
E get first {
|
2014-05-05 09:26:56 +00:00
|
|
|
if (length == 0) throw IterableElementError.noElement();
|
2013-02-14 09:51:43 +00:00
|
|
|
return elementAt(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
E get last {
|
2014-05-05 09:26:56 +00:00
|
|
|
if (length == 0) throw IterableElementError.noElement();
|
2013-02-14 09:51:43 +00:00
|
|
|
return elementAt(length - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
E get single {
|
2014-05-05 09:26:56 +00:00
|
|
|
if (length == 0) throw IterableElementError.noElement();
|
|
|
|
if (length > 1) throw IterableElementError.tooMany();
|
2013-02-14 09:51:43 +00:00
|
|
|
return elementAt(0);
|
|
|
|
}
|
|
|
|
|
2013-06-24 12:45:10 +00:00
|
|
|
bool contains(Object element) {
|
2013-02-14 09:51:43 +00:00
|
|
|
int length = this.length;
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
if (elementAt(i) == element) return true;
|
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool every(bool test(E element)) {
|
|
|
|
int length = this.length;
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
if (!test(elementAt(i))) return false;
|
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool any(bool test(E element)) {
|
|
|
|
int length = this.length;
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
if (test(elementAt(i))) return true;
|
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-06-24 12:45:10 +00:00
|
|
|
dynamic firstWhere(bool test(E element), { Object orElse() }) {
|
2013-02-14 09:51:43 +00:00
|
|
|
int length = this.length;
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
E element = elementAt(i);
|
|
|
|
if (test(element)) return element;
|
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (orElse != null) return orElse();
|
2014-05-05 09:26:56 +00:00
|
|
|
throw IterableElementError.noElement();
|
2013-02-14 09:51:43 +00:00
|
|
|
}
|
|
|
|
|
2013-06-24 12:45:10 +00:00
|
|
|
dynamic lastWhere(bool test(E element), { Object orElse() }) {
|
2013-02-14 09:51:43 +00:00
|
|
|
int length = this.length;
|
2013-02-18 10:35:51 +00:00
|
|
|
for (int i = length - 1; i >= 0; i--) {
|
2013-02-14 09:51:43 +00:00
|
|
|
E element = elementAt(i);
|
|
|
|
if (test(element)) return element;
|
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (orElse != null) return orElse();
|
2014-05-05 09:26:56 +00:00
|
|
|
throw IterableElementError.noElement();
|
2013-02-14 09:51:43 +00:00
|
|
|
}
|
|
|
|
|
2013-03-12 17:34:49 +00:00
|
|
|
E singleWhere(bool test(E element)) {
|
2013-02-14 09:51:43 +00:00
|
|
|
int length = this.length;
|
|
|
|
E match = null;
|
|
|
|
bool matchFound = false;
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
E element = elementAt(i);
|
|
|
|
if (test(element)) {
|
|
|
|
if (matchFound) {
|
2014-05-05 09:26:56 +00:00
|
|
|
throw IterableElementError.tooMany();
|
2013-02-14 09:51:43 +00:00
|
|
|
}
|
|
|
|
matchFound = true;
|
|
|
|
match = element;
|
|
|
|
}
|
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (matchFound) return match;
|
2014-05-05 09:26:56 +00:00
|
|
|
throw IterableElementError.noElement();
|
2013-02-14 09:51:43 +00:00
|
|
|
}
|
|
|
|
|
2013-04-10 11:28:32 +00:00
|
|
|
String join([String separator = ""]) {
|
2013-02-14 09:51:43 +00:00
|
|
|
int length = this.length;
|
2013-04-10 11:28:32 +00:00
|
|
|
if (!separator.isEmpty) {
|
2013-02-14 09:51:43 +00:00
|
|
|
if (length == 0) return "";
|
|
|
|
String first = "${elementAt(0)}";
|
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
StringBuffer buffer = new StringBuffer(first);
|
|
|
|
for (int i = 1; i < length; i++) {
|
2013-03-08 13:07:18 +00:00
|
|
|
buffer.write(separator);
|
2013-04-10 11:28:32 +00:00
|
|
|
buffer.write(elementAt(i));
|
2013-02-14 09:51:43 +00:00
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return buffer.toString();
|
|
|
|
} else {
|
|
|
|
StringBuffer buffer = new StringBuffer();
|
|
|
|
for (int i = 0; i < length; i++) {
|
2013-04-10 11:28:32 +00:00
|
|
|
buffer.write(elementAt(i));
|
2013-02-14 09:51:43 +00:00
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return buffer.toString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Iterable<E> where(bool test(E element)) => super.where(test);
|
|
|
|
|
2013-02-18 10:35:51 +00:00
|
|
|
Iterable map(f(E element)) => new MappedListIterable(this, f);
|
2013-02-15 18:38:42 +00:00
|
|
|
|
2013-04-11 08:32:31 +00:00
|
|
|
E reduce(E combine(var value, E element)) {
|
2014-10-14 09:12:21 +00:00
|
|
|
int length = this.length;
|
2014-05-05 09:26:56 +00:00
|
|
|
if (length == 0) throw IterableElementError.noElement();
|
2013-04-11 08:32:31 +00:00
|
|
|
E value = elementAt(0);
|
|
|
|
for (int i = 1; i < length; i++) {
|
|
|
|
value = combine(value, elementAt(i));
|
2014-10-14 09:12:21 +00:00
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
|
2013-04-11 08:32:31 +00:00
|
|
|
}
|
|
|
|
return value;
|
2013-04-05 16:12:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fold(var initialValue, combine(var previousValue, E element)) {
|
2013-02-14 09:51:43 +00:00
|
|
|
var value = initialValue;
|
|
|
|
int length = this.length;
|
|
|
|
for (int i = 0; i < length; i++) {
|
2013-02-18 10:35:51 +00:00
|
|
|
value = combine(value, elementAt(i));
|
2013-02-14 09:51:43 +00:00
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2014-07-18 08:08:16 +00:00
|
|
|
Iterable<E> skip(int count) => new SubListIterable<E>(this, count, null);
|
2013-02-14 09:51:43 +00:00
|
|
|
|
|
|
|
Iterable<E> skipWhile(bool test(E element)) => super.skipWhile(test);
|
|
|
|
|
2014-07-18 08:08:16 +00:00
|
|
|
Iterable<E> take(int count) => new SubListIterable<E>(this, 0, count);
|
2013-02-14 09:51:43 +00:00
|
|
|
|
|
|
|
Iterable<E> takeWhile(bool test(E element)) => super.takeWhile(test);
|
|
|
|
|
2013-03-04 14:55:40 +00:00
|
|
|
List<E> toList({ bool growable: true }) {
|
2013-02-27 08:45:04 +00:00
|
|
|
List<E> result;
|
|
|
|
if (growable) {
|
|
|
|
result = new List<E>()..length = length;
|
|
|
|
} else {
|
|
|
|
result = new List<E>(length);
|
|
|
|
}
|
2013-02-14 09:51:43 +00:00
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
result[i] = elementAt(i);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
Set<E> toSet() {
|
|
|
|
Set<E> result = new Set<E>();
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
result.add(elementAt(i));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-18 10:35:51 +00:00
|
|
|
class SubListIterable<E> extends ListIterable<E> {
|
2014-09-25 10:45:24 +00:00
|
|
|
final Iterable<E> _iterable; // Has efficient length and elementAt.
|
2013-02-14 09:51:43 +00:00
|
|
|
final int _start;
|
|
|
|
/** If null, represents the length of the iterable. */
|
|
|
|
final int _endOrLength;
|
|
|
|
|
2013-06-20 15:45:38 +00:00
|
|
|
SubListIterable(this._iterable, this._start, this._endOrLength) {
|
2014-11-21 10:29:15 +00:00
|
|
|
RangeError.checkNotNegative(_start, "start");
|
2013-06-20 15:45:38 +00:00
|
|
|
if (_endOrLength != null) {
|
2014-11-21 10:29:15 +00:00
|
|
|
RangeError.checkNotNegative(_endOrLength, "end");
|
2013-06-20 15:45:38 +00:00
|
|
|
if (_start > _endOrLength) {
|
2014-11-21 10:29:15 +00:00
|
|
|
throw new RangeError.range(_start, 0, _endOrLength, "start");
|
2013-06-20 15:45:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-02-14 09:51:43 +00:00
|
|
|
|
|
|
|
int get _endIndex {
|
|
|
|
int length = _iterable.length;
|
|
|
|
if (_endOrLength == null || _endOrLength > length) return length;
|
|
|
|
return _endOrLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
int get _startIndex {
|
|
|
|
int length = _iterable.length;
|
|
|
|
if (_start > length) return length;
|
|
|
|
return _start;
|
|
|
|
}
|
|
|
|
|
|
|
|
int get length {
|
|
|
|
int length = _iterable.length;
|
|
|
|
if (_start >= length) return 0;
|
|
|
|
if (_endOrLength == null || _endOrLength >= length) {
|
|
|
|
return length - _start;
|
|
|
|
}
|
|
|
|
return _endOrLength - _start;
|
|
|
|
}
|
|
|
|
|
|
|
|
E elementAt(int index) {
|
|
|
|
int realIndex = _startIndex + index;
|
|
|
|
if (index < 0 || realIndex >= _endIndex) {
|
2014-11-21 10:29:15 +00:00
|
|
|
throw new RangeError.index(index, this, "index");
|
2013-02-14 09:51:43 +00:00
|
|
|
}
|
|
|
|
return _iterable.elementAt(realIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
Iterable<E> skip(int count) {
|
2014-11-21 10:29:15 +00:00
|
|
|
RangeError.checkNotNegative(count, "count");
|
2014-07-18 08:08:16 +00:00
|
|
|
int newStart = _start + count;
|
|
|
|
if (_endOrLength != null && newStart >= _endOrLength) {
|
|
|
|
return new EmptyIterable<E>();
|
|
|
|
}
|
|
|
|
return new SubListIterable<E>(_iterable, newStart, _endOrLength);
|
2013-02-14 09:51:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Iterable<E> take(int count) {
|
2014-11-21 10:29:15 +00:00
|
|
|
RangeError.checkNotNegative(count, "count");
|
2013-02-14 09:51:43 +00:00
|
|
|
if (_endOrLength == null) {
|
2014-07-18 08:08:16 +00:00
|
|
|
return new SubListIterable<E>(_iterable, _start, _start + count);
|
2013-02-14 09:51:43 +00:00
|
|
|
} else {
|
2013-02-18 10:35:51 +00:00
|
|
|
int newEnd = _start + count;
|
2013-02-14 09:51:43 +00:00
|
|
|
if (_endOrLength < newEnd) return this;
|
2014-07-18 08:08:16 +00:00
|
|
|
return new SubListIterable<E>(_iterable, _start, newEnd);
|
2013-02-14 09:51:43 +00:00
|
|
|
}
|
|
|
|
}
|
2014-09-25 10:45:24 +00:00
|
|
|
|
2014-09-25 12:06:26 +00:00
|
|
|
List<E> toList({bool growable: true}) {
|
2014-09-25 10:45:24 +00:00
|
|
|
int start = _start;
|
|
|
|
int end = _iterable.length;
|
|
|
|
if (_endOrLength != null && _endOrLength < end) end = _endOrLength;
|
|
|
|
int length = end - start;
|
|
|
|
if (length < 0) length = 0;
|
|
|
|
List result = growable ? (new List<E>()..length = length)
|
|
|
|
: new List<E>(length);
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
result[i] = _iterable.elementAt(start + i);
|
|
|
|
if (_iterable.length < end) throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2013-02-14 09:51:43 +00:00
|
|
|
}
|
|
|
|
|
2013-02-18 10:35:51 +00:00
|
|
|
/**
|
|
|
|
* An [Iterator] that iterates a list-like [Iterable].
|
|
|
|
*
|
|
|
|
* All iterations is done in terms of [Iterable.length] and
|
|
|
|
* [Iterable.elementAt]. These operations are fast for list-like
|
|
|
|
* iterables.
|
|
|
|
*/
|
|
|
|
class ListIterator<E> implements Iterator<E> {
|
2013-02-14 09:51:43 +00:00
|
|
|
final Iterable<E> _iterable;
|
|
|
|
final int _length;
|
|
|
|
int _index;
|
|
|
|
E _current;
|
2013-02-18 10:35:51 +00:00
|
|
|
|
|
|
|
ListIterator(Iterable<E> iterable)
|
2013-02-14 09:51:43 +00:00
|
|
|
: _iterable = iterable, _length = iterable.length, _index = 0;
|
|
|
|
|
|
|
|
E get current => _current;
|
|
|
|
|
|
|
|
bool moveNext() {
|
2013-05-24 11:54:18 +00:00
|
|
|
int length = _iterable.length;
|
|
|
|
if (_length != length) {
|
2013-02-14 09:51:43 +00:00
|
|
|
throw new ConcurrentModificationError(_iterable);
|
|
|
|
}
|
2013-05-24 11:54:18 +00:00
|
|
|
if (_index >= length) {
|
2013-02-14 09:51:43 +00:00
|
|
|
_current = null;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
_current = _iterable.elementAt(_index);
|
|
|
|
_index++;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-18 14:08:41 +00:00
|
|
|
typedef T _Transformation<S, T>(S value);
|
|
|
|
|
2013-04-15 14:32:16 +00:00
|
|
|
class MappedIterable<S, T> extends IterableBase<T> {
|
2013-01-18 14:08:41 +00:00
|
|
|
final Iterable<S> _iterable;
|
2013-07-11 10:33:34 +00:00
|
|
|
final _Transformation<S, T> _f;
|
2013-01-18 14:08:41 +00:00
|
|
|
|
2013-10-14 10:40:00 +00:00
|
|
|
factory MappedIterable(Iterable iterable, T function(S value)) {
|
|
|
|
if (iterable is EfficientLength) {
|
|
|
|
return new EfficientLengthMappedIterable<S, T>(iterable, function);
|
|
|
|
}
|
|
|
|
return new MappedIterable<S, T>._(iterable, function);
|
|
|
|
}
|
|
|
|
|
|
|
|
MappedIterable._(this._iterable, T this._f(S element));
|
2013-01-18 14:08:41 +00:00
|
|
|
|
|
|
|
Iterator<T> get iterator => new MappedIterator<S, T>(_iterable.iterator, _f);
|
|
|
|
|
|
|
|
// Length related functions are independent of the mapping.
|
|
|
|
int get length => _iterable.length;
|
|
|
|
bool get isEmpty => _iterable.isEmpty;
|
2013-02-04 09:30:36 +00:00
|
|
|
|
2013-02-18 10:35:51 +00:00
|
|
|
// Index based lookup can be done before transforming.
|
|
|
|
T get first => _f(_iterable.first);
|
|
|
|
T get last => _f(_iterable.last);
|
|
|
|
T get single => _f(_iterable.single);
|
|
|
|
T elementAt(int index) => _f(_iterable.elementAt(index));
|
|
|
|
}
|
2013-02-15 18:38:42 +00:00
|
|
|
|
2013-10-14 10:40:00 +00:00
|
|
|
class EfficientLengthMappedIterable<S, T> extends MappedIterable<S, T>
|
|
|
|
implements EfficientLength {
|
|
|
|
EfficientLengthMappedIterable(Iterable iterable, T function(S value))
|
|
|
|
: super._(iterable, function);
|
|
|
|
}
|
|
|
|
|
2013-01-18 14:08:41 +00:00
|
|
|
class MappedIterator<S, T> extends Iterator<T> {
|
|
|
|
T _current;
|
|
|
|
final Iterator<S> _iterator;
|
2013-07-11 10:33:34 +00:00
|
|
|
final _Transformation<S, T> _f;
|
2013-01-18 14:08:41 +00:00
|
|
|
|
|
|
|
MappedIterator(this._iterator, T this._f(S element));
|
|
|
|
|
|
|
|
bool moveNext() {
|
|
|
|
if (_iterator.moveNext()) {
|
|
|
|
_current = _f(_iterator.current);
|
|
|
|
return true;
|
|
|
|
}
|
2013-02-18 10:35:51 +00:00
|
|
|
_current = null;
|
|
|
|
return false;
|
2013-01-18 14:08:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
T get current => _current;
|
|
|
|
}
|
|
|
|
|
2013-10-14 10:40:00 +00:00
|
|
|
/**
|
|
|
|
* Specialized alternative to [MappedIterable] for mapped [List]s.
|
|
|
|
*
|
|
|
|
* Expects efficient `length` and `elementAt` on the source iterable.
|
|
|
|
*/
|
|
|
|
class MappedListIterable<S, T> extends ListIterable<T>
|
|
|
|
implements EfficientLength {
|
2013-02-18 10:35:51 +00:00
|
|
|
final Iterable<S> _source;
|
2013-07-11 10:33:34 +00:00
|
|
|
final _Transformation<S, T> _f;
|
2013-02-15 18:38:42 +00:00
|
|
|
|
2013-02-18 10:35:51 +00:00
|
|
|
MappedListIterable(this._source, T this._f(S value));
|
2013-02-15 18:38:42 +00:00
|
|
|
|
2013-02-18 10:35:51 +00:00
|
|
|
int get length => _source.length;
|
|
|
|
T elementAt(int index) => _f(_source.elementAt(index));
|
2013-02-15 18:38:42 +00:00
|
|
|
}
|
|
|
|
|
2013-02-04 09:30:36 +00:00
|
|
|
|
2013-01-18 14:08:41 +00:00
|
|
|
typedef bool _ElementPredicate<E>(E element);
|
|
|
|
|
2013-04-15 14:32:16 +00:00
|
|
|
class WhereIterable<E> extends IterableBase<E> {
|
2013-01-18 14:08:41 +00:00
|
|
|
final Iterable<E> _iterable;
|
2013-07-11 10:33:34 +00:00
|
|
|
final _ElementPredicate _f;
|
2013-01-18 14:08:41 +00:00
|
|
|
|
|
|
|
WhereIterable(this._iterable, bool this._f(E element));
|
|
|
|
|
|
|
|
Iterator<E> get iterator => new WhereIterator<E>(_iterable.iterator, _f);
|
|
|
|
}
|
|
|
|
|
|
|
|
class WhereIterator<E> extends Iterator<E> {
|
|
|
|
final Iterator<E> _iterator;
|
2013-07-11 10:33:34 +00:00
|
|
|
final _ElementPredicate _f;
|
2013-01-18 14:08:41 +00:00
|
|
|
|
|
|
|
WhereIterator(this._iterator, bool this._f(E element));
|
|
|
|
|
|
|
|
bool moveNext() {
|
|
|
|
while (_iterator.moveNext()) {
|
|
|
|
if (_f(_iterator.current)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
E get current => _iterator.current;
|
|
|
|
}
|
|
|
|
|
2013-02-05 09:48:05 +00:00
|
|
|
typedef Iterable<T> _ExpandFunction<S, T>(S sourceElement);
|
|
|
|
|
2013-04-15 14:32:16 +00:00
|
|
|
class ExpandIterable<S, T> extends IterableBase<T> {
|
2013-02-05 09:48:05 +00:00
|
|
|
final Iterable<S> _iterable;
|
2013-07-11 10:33:34 +00:00
|
|
|
final _ExpandFunction _f;
|
2013-02-05 09:48:05 +00:00
|
|
|
|
|
|
|
ExpandIterable(this._iterable, Iterable<T> this._f(S element));
|
|
|
|
|
|
|
|
Iterator<T> get iterator => new ExpandIterator<S, T>(_iterable.iterator, _f);
|
|
|
|
}
|
|
|
|
|
|
|
|
class ExpandIterator<S, T> implements Iterator<T> {
|
|
|
|
final Iterator<S> _iterator;
|
2013-07-11 10:33:34 +00:00
|
|
|
final _ExpandFunction _f;
|
2013-02-05 09:48:05 +00:00
|
|
|
// Initialize _currentExpansion to an empty iterable. A null value
|
|
|
|
// marks the end of iteration, and we don't want to call _f before
|
|
|
|
// the first moveNext call.
|
|
|
|
Iterator<T> _currentExpansion = const EmptyIterator();
|
|
|
|
T _current;
|
|
|
|
|
|
|
|
ExpandIterator(this._iterator, Iterable<T> this._f(S element));
|
|
|
|
|
|
|
|
void _nextExpansion() {
|
|
|
|
}
|
|
|
|
|
|
|
|
T get current => _current;
|
|
|
|
|
|
|
|
bool moveNext() {
|
|
|
|
if (_currentExpansion == null) return false;
|
|
|
|
while (!_currentExpansion.moveNext()) {
|
|
|
|
_current = null;
|
|
|
|
if (_iterator.moveNext()) {
|
|
|
|
// If _f throws, this ends iteration. Otherwise _currentExpansion and
|
|
|
|
// _current will be set again below.
|
|
|
|
_currentExpansion = null;
|
|
|
|
_currentExpansion = _f(_iterator.current).iterator;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_current = _currentExpansion.current;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-15 14:32:16 +00:00
|
|
|
class TakeIterable<E> extends IterableBase<E> {
|
2013-01-18 14:08:41 +00:00
|
|
|
final Iterable<E> _iterable;
|
|
|
|
final int _takeCount;
|
|
|
|
|
2013-10-14 10:40:00 +00:00
|
|
|
factory TakeIterable(Iterable<E> iterable, int takeCount) {
|
|
|
|
if (takeCount is! int || takeCount < 0) {
|
|
|
|
throw new ArgumentError(takeCount);
|
|
|
|
}
|
|
|
|
if (iterable is EfficientLength) {
|
|
|
|
return new EfficientLengthTakeIterable<E>(iterable, takeCount);
|
2013-01-18 14:08:41 +00:00
|
|
|
}
|
2013-10-14 10:40:00 +00:00
|
|
|
return new TakeIterable<E>._(iterable, takeCount);
|
2013-01-18 14:08:41 +00:00
|
|
|
}
|
|
|
|
|
2013-10-14 10:40:00 +00:00
|
|
|
TakeIterable._(this._iterable, this._takeCount);
|
|
|
|
|
2013-01-18 14:08:41 +00:00
|
|
|
Iterator<E> get iterator {
|
|
|
|
return new TakeIterator<E>(_iterable.iterator, _takeCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-14 10:40:00 +00:00
|
|
|
class EfficientLengthTakeIterable<E> extends TakeIterable<E>
|
|
|
|
implements EfficientLength {
|
|
|
|
EfficientLengthTakeIterable(Iterable<E> iterable, int takeCount)
|
|
|
|
: super._(iterable, takeCount);
|
|
|
|
|
|
|
|
int get length {
|
|
|
|
int iterableLength = _iterable.length;
|
|
|
|
if (iterableLength > _takeCount) return _takeCount;
|
|
|
|
return iterableLength;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-18 14:08:41 +00:00
|
|
|
class TakeIterator<E> extends Iterator<E> {
|
|
|
|
final Iterator<E> _iterator;
|
|
|
|
int _remaining;
|
|
|
|
|
|
|
|
TakeIterator(this._iterator, this._remaining) {
|
|
|
|
assert(_remaining is int && _remaining >= 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool moveNext() {
|
|
|
|
_remaining--;
|
|
|
|
if (_remaining >= 0) {
|
|
|
|
return _iterator.moveNext();
|
|
|
|
}
|
|
|
|
_remaining = -1;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
E get current {
|
|
|
|
if (_remaining < 0) return null;
|
|
|
|
return _iterator.current;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-15 14:32:16 +00:00
|
|
|
class TakeWhileIterable<E> extends IterableBase<E> {
|
2013-01-18 14:08:41 +00:00
|
|
|
final Iterable<E> _iterable;
|
2013-07-11 10:33:34 +00:00
|
|
|
final _ElementPredicate _f;
|
2013-01-18 14:08:41 +00:00
|
|
|
|
|
|
|
TakeWhileIterable(this._iterable, bool this._f(E element));
|
|
|
|
|
2013-04-15 12:45:35 +00:00
|
|
|
Iterator<E> get iterator {
|
2013-01-18 14:08:41 +00:00
|
|
|
return new TakeWhileIterator<E>(_iterable.iterator, _f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class TakeWhileIterator<E> extends Iterator<E> {
|
|
|
|
final Iterator<E> _iterator;
|
2013-07-11 10:33:34 +00:00
|
|
|
final _ElementPredicate _f;
|
2013-01-18 14:08:41 +00:00
|
|
|
bool _isFinished = false;
|
|
|
|
|
|
|
|
TakeWhileIterator(this._iterator, bool this._f(E element));
|
|
|
|
|
|
|
|
bool moveNext() {
|
|
|
|
if (_isFinished) return false;
|
|
|
|
if (!_iterator.moveNext() || !_f(_iterator.current)) {
|
|
|
|
_isFinished = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
E get current {
|
|
|
|
if (_isFinished) return null;
|
|
|
|
return _iterator.current;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-15 14:32:16 +00:00
|
|
|
class SkipIterable<E> extends IterableBase<E> {
|
2013-01-18 14:08:41 +00:00
|
|
|
final Iterable<E> _iterable;
|
|
|
|
final int _skipCount;
|
|
|
|
|
2014-11-21 10:29:15 +00:00
|
|
|
factory SkipIterable(Iterable<E> iterable, int count) {
|
2013-10-14 10:40:00 +00:00
|
|
|
if (iterable is EfficientLength) {
|
2014-11-21 10:29:15 +00:00
|
|
|
return new EfficientLengthSkipIterable<E>(iterable, count);
|
2013-10-14 10:40:00 +00:00
|
|
|
}
|
2014-11-21 10:29:15 +00:00
|
|
|
return new SkipIterable<E>._(iterable, count);
|
2013-10-14 10:40:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SkipIterable._(this._iterable, this._skipCount) {
|
2014-11-21 10:29:15 +00:00
|
|
|
if (_skipCount is! int) {
|
|
|
|
throw new ArgumentError.value(_skipCount, "count is not an integer");
|
2013-01-18 14:08:41 +00:00
|
|
|
}
|
2014-11-21 10:29:15 +00:00
|
|
|
RangeError.checkNotNegative(_skipCount, "count");
|
2013-01-18 14:08:41 +00:00
|
|
|
}
|
|
|
|
|
2014-11-21 10:29:15 +00:00
|
|
|
Iterable<E> skip(int count) {
|
|
|
|
if (_skipCount is! int) {
|
|
|
|
throw new ArgumentError.value(_skipCount, "count is not an integer");
|
2013-01-18 14:08:41 +00:00
|
|
|
}
|
2014-11-21 10:29:15 +00:00
|
|
|
RangeError.checkNotNegative(_skipCount, "count");
|
|
|
|
return new SkipIterable<E>._(_iterable, _skipCount + count);
|
2013-01-18 14:08:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Iterator<E> get iterator {
|
|
|
|
return new SkipIterator<E>(_iterable.iterator, _skipCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-14 10:40:00 +00:00
|
|
|
class EfficientLengthSkipIterable<E> extends SkipIterable<E>
|
|
|
|
implements EfficientLength {
|
|
|
|
EfficientLengthSkipIterable(Iterable<E> iterable, int skipCount)
|
|
|
|
: super._(iterable, skipCount);
|
|
|
|
|
|
|
|
int get length {
|
|
|
|
int length = _iterable.length - _skipCount;
|
|
|
|
if (length >= 0) return length;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-18 14:08:41 +00:00
|
|
|
class SkipIterator<E> extends Iterator<E> {
|
|
|
|
final Iterator<E> _iterator;
|
|
|
|
int _skipCount;
|
|
|
|
|
|
|
|
SkipIterator(this._iterator, this._skipCount) {
|
|
|
|
assert(_skipCount is int && _skipCount >= 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool moveNext() {
|
|
|
|
for (int i = 0; i < _skipCount; i++) _iterator.moveNext();
|
|
|
|
_skipCount = 0;
|
|
|
|
return _iterator.moveNext();
|
|
|
|
}
|
|
|
|
|
|
|
|
E get current => _iterator.current;
|
|
|
|
}
|
|
|
|
|
2013-04-15 14:32:16 +00:00
|
|
|
class SkipWhileIterable<E> extends IterableBase<E> {
|
2013-01-18 14:08:41 +00:00
|
|
|
final Iterable<E> _iterable;
|
2013-07-11 10:33:34 +00:00
|
|
|
final _ElementPredicate _f;
|
2013-01-18 14:08:41 +00:00
|
|
|
|
|
|
|
SkipWhileIterable(this._iterable, bool this._f(E element));
|
|
|
|
|
|
|
|
Iterator<E> get iterator {
|
|
|
|
return new SkipWhileIterator<E>(_iterable.iterator, _f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class SkipWhileIterator<E> extends Iterator<E> {
|
|
|
|
final Iterator<E> _iterator;
|
2013-07-11 10:33:34 +00:00
|
|
|
final _ElementPredicate _f;
|
2013-01-18 14:08:41 +00:00
|
|
|
bool _hasSkipped = false;
|
|
|
|
|
|
|
|
SkipWhileIterator(this._iterator, bool this._f(E element));
|
|
|
|
|
|
|
|
bool moveNext() {
|
|
|
|
if (!_hasSkipped) {
|
|
|
|
_hasSkipped = true;
|
|
|
|
while (_iterator.moveNext()) {
|
|
|
|
if (!_f(_iterator.current)) return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return _iterator.moveNext();
|
|
|
|
}
|
|
|
|
|
|
|
|
E get current => _iterator.current;
|
|
|
|
}
|
2013-02-04 09:30:36 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The always empty [Iterable].
|
|
|
|
*/
|
2013-10-14 10:40:00 +00:00
|
|
|
class EmptyIterable<E> extends IterableBase<E> implements EfficientLength {
|
2013-02-04 09:30:36 +00:00
|
|
|
const EmptyIterable();
|
|
|
|
|
|
|
|
Iterator<E> get iterator => const EmptyIterator();
|
|
|
|
|
|
|
|
void forEach(void action(E element)) {}
|
|
|
|
|
|
|
|
bool get isEmpty => true;
|
|
|
|
|
|
|
|
int get length => 0;
|
|
|
|
|
2014-05-05 09:26:56 +00:00
|
|
|
E get first { throw IterableElementError.noElement(); }
|
2013-02-04 09:30:36 +00:00
|
|
|
|
2014-05-05 09:26:56 +00:00
|
|
|
E get last { throw IterableElementError.noElement(); }
|
2013-02-04 09:30:36 +00:00
|
|
|
|
2014-05-05 09:26:56 +00:00
|
|
|
E get single { throw IterableElementError.noElement(); }
|
2013-02-04 09:30:36 +00:00
|
|
|
|
2014-11-21 10:29:15 +00:00
|
|
|
E elementAt(int index) { throw new RangeError.range(index, 0, 0, "index"); }
|
2013-02-04 09:30:36 +00:00
|
|
|
|
2013-06-24 12:45:10 +00:00
|
|
|
bool contains(Object element) => false;
|
2013-02-04 09:30:36 +00:00
|
|
|
|
|
|
|
bool every(bool test(E element)) => true;
|
|
|
|
|
|
|
|
bool any(bool test(E element)) => false;
|
|
|
|
|
2013-03-12 17:34:49 +00:00
|
|
|
E firstWhere(bool test(E element), { E orElse() }) {
|
2013-02-04 09:30:36 +00:00
|
|
|
if (orElse != null) return orElse();
|
2014-05-05 09:26:56 +00:00
|
|
|
throw IterableElementError.noElement();
|
2013-02-04 09:30:36 +00:00
|
|
|
}
|
|
|
|
|
2013-03-12 17:34:49 +00:00
|
|
|
E lastWhere(bool test(E element), { E orElse() }) {
|
2013-02-04 10:41:55 +00:00
|
|
|
if (orElse != null) return orElse();
|
2014-05-05 09:26:56 +00:00
|
|
|
throw IterableElementError.noElement();
|
2013-02-04 09:30:36 +00:00
|
|
|
}
|
|
|
|
|
2013-03-12 17:34:49 +00:00
|
|
|
E singleWhere(bool test(E element), { E orElse() }) {
|
2013-02-04 10:41:55 +00:00
|
|
|
if (orElse != null) return orElse();
|
2014-05-05 09:26:56 +00:00
|
|
|
throw IterableElementError.noElement();
|
2013-02-04 09:30:36 +00:00
|
|
|
}
|
|
|
|
|
2013-04-10 11:28:32 +00:00
|
|
|
String join([String separator = ""]) => "";
|
2013-02-04 09:30:36 +00:00
|
|
|
|
|
|
|
Iterable<E> where(bool test(E element)) => this;
|
|
|
|
|
2013-02-04 10:41:55 +00:00
|
|
|
Iterable map(f(E element)) => const EmptyIterable();
|
2013-02-04 09:30:36 +00:00
|
|
|
|
2013-04-11 08:32:31 +00:00
|
|
|
E reduce(E combine(E value, E element)) {
|
2014-05-05 09:26:56 +00:00
|
|
|
throw IterableElementError.noElement();
|
2013-04-05 16:12:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fold(var initialValue, combine(var previousValue, E element)) {
|
2013-02-04 09:30:36 +00:00
|
|
|
return initialValue;
|
|
|
|
}
|
|
|
|
|
2013-06-20 15:45:38 +00:00
|
|
|
Iterable<E> skip(int count) {
|
2014-11-21 10:29:15 +00:00
|
|
|
RangeError.checkNotNegative(count, "count");
|
2013-06-20 15:45:38 +00:00
|
|
|
return this;
|
|
|
|
}
|
2013-02-04 09:30:36 +00:00
|
|
|
|
|
|
|
Iterable<E> skipWhile(bool test(E element)) => this;
|
|
|
|
|
2013-06-20 15:45:38 +00:00
|
|
|
Iterable<E> take(int count) {
|
2014-11-21 10:29:15 +00:00
|
|
|
RangeError.checkNotNegative(count, "count");
|
2014-03-21 09:54:46 +00:00
|
|
|
return this;
|
2013-06-20 15:45:38 +00:00
|
|
|
}
|
2013-02-04 09:30:36 +00:00
|
|
|
|
|
|
|
Iterable<E> takeWhile(bool test(E element)) => this;
|
|
|
|
|
2013-03-04 14:55:40 +00:00
|
|
|
List toList({ bool growable: true }) => growable ? <E>[] : new List<E>(0);
|
2013-02-04 09:30:36 +00:00
|
|
|
|
|
|
|
Set toSet() => new Set<E>();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** The always empty iterator. */
|
|
|
|
class EmptyIterator<E> implements Iterator<E> {
|
|
|
|
const EmptyIterator();
|
|
|
|
bool moveNext() => false;
|
|
|
|
E get current => null;
|
|
|
|
}
|
2013-02-12 15:22:31 +00:00
|
|
|
|
|
|
|
/** An [Iterator] that can move in both directions. */
|
2013-02-25 10:10:01 +00:00
|
|
|
abstract class BidirectionalIterator<T> implements Iterator<T> {
|
2013-02-12 15:22:31 +00:00
|
|
|
bool movePrevious();
|
|
|
|
}
|
2013-04-15 12:34:44 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This class provides default implementations for Iterables (including Lists).
|
|
|
|
*
|
|
|
|
* The uses of this class will be replaced by mixins.
|
|
|
|
*/
|
2014-07-18 08:08:16 +00:00
|
|
|
class IterableMixinWorkaround<T> {
|
2013-04-15 12:34:44 +00:00
|
|
|
static bool contains(Iterable iterable, var element) {
|
|
|
|
for (final e in iterable) {
|
2013-06-26 09:14:24 +00:00
|
|
|
if (e == element) return true;
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void forEach(Iterable iterable, void f(o)) {
|
|
|
|
for (final e in iterable) {
|
|
|
|
f(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool any(Iterable iterable, bool f(o)) {
|
|
|
|
for (final e in iterable) {
|
|
|
|
if (f(e)) return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool every(Iterable iterable, bool f(o)) {
|
|
|
|
for (final e in iterable) {
|
|
|
|
if (!f(e)) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static dynamic reduce(Iterable iterable,
|
|
|
|
dynamic combine(previousValue, element)) {
|
|
|
|
Iterator iterator = iterable.iterator;
|
2014-05-05 09:26:56 +00:00
|
|
|
if (!iterator.moveNext()) throw IterableElementError.noElement();
|
2013-04-15 12:34:44 +00:00
|
|
|
var value = iterator.current;
|
|
|
|
while (iterator.moveNext()) {
|
|
|
|
value = combine(value, iterator.current);
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static dynamic fold(Iterable iterable,
|
|
|
|
dynamic initialValue,
|
|
|
|
dynamic combine(dynamic previousValue, element)) {
|
|
|
|
for (final element in iterable) {
|
|
|
|
initialValue = combine(initialValue, element);
|
|
|
|
}
|
|
|
|
return initialValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes elements matching [test] from [list].
|
|
|
|
*
|
|
|
|
* This is performed in two steps, to avoid exposing an inconsistent state
|
|
|
|
* to the [test] function. First the elements to retain are found, and then
|
|
|
|
* the original list is updated to contain those elements.
|
|
|
|
*/
|
|
|
|
static void removeWhereList(List list, bool test(var element)) {
|
|
|
|
List retained = [];
|
|
|
|
int length = list.length;
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
var element = list[i];
|
|
|
|
if (!test(element)) {
|
|
|
|
retained.add(element);
|
|
|
|
}
|
|
|
|
if (length != list.length) {
|
|
|
|
throw new ConcurrentModificationError(list);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (retained.length == length) return;
|
|
|
|
list.length = retained.length;
|
|
|
|
for (int i = 0; i < retained.length; i++) {
|
|
|
|
list[i] = retained[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isEmpty(Iterable iterable) {
|
|
|
|
return !iterable.iterator.moveNext();
|
|
|
|
}
|
|
|
|
|
|
|
|
static dynamic first(Iterable iterable) {
|
|
|
|
Iterator it = iterable.iterator;
|
|
|
|
if (!it.moveNext()) {
|
2014-05-05 09:26:56 +00:00
|
|
|
throw IterableElementError.noElement();
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
return it.current;
|
|
|
|
}
|
|
|
|
|
|
|
|
static dynamic last(Iterable iterable) {
|
|
|
|
Iterator it = iterable.iterator;
|
|
|
|
if (!it.moveNext()) {
|
2014-05-05 09:26:56 +00:00
|
|
|
throw IterableElementError.noElement();
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
dynamic result;
|
|
|
|
do {
|
|
|
|
result = it.current;
|
|
|
|
} while(it.moveNext());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static dynamic single(Iterable iterable) {
|
|
|
|
Iterator it = iterable.iterator;
|
2014-05-05 09:26:56 +00:00
|
|
|
if (!it.moveNext()) throw IterableElementError.noElement();
|
2013-04-15 12:34:44 +00:00
|
|
|
dynamic result = it.current;
|
2014-05-05 09:26:56 +00:00
|
|
|
if (it.moveNext()) throw IterableElementError.tooMany();
|
2013-04-15 12:34:44 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static dynamic firstWhere(Iterable iterable,
|
2013-06-24 12:45:10 +00:00
|
|
|
bool test(dynamic value),
|
|
|
|
dynamic orElse()) {
|
2013-04-15 12:34:44 +00:00
|
|
|
for (dynamic element in iterable) {
|
|
|
|
if (test(element)) return element;
|
|
|
|
}
|
|
|
|
if (orElse != null) return orElse();
|
2014-05-05 09:26:56 +00:00
|
|
|
throw IterableElementError.noElement();
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static dynamic lastWhere(Iterable iterable,
|
|
|
|
bool test(dynamic value),
|
|
|
|
dynamic orElse()) {
|
|
|
|
dynamic result = null;
|
|
|
|
bool foundMatching = false;
|
|
|
|
for (dynamic element in iterable) {
|
|
|
|
if (test(element)) {
|
|
|
|
result = element;
|
|
|
|
foundMatching = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (foundMatching) return result;
|
|
|
|
if (orElse != null) return orElse();
|
2014-05-05 09:26:56 +00:00
|
|
|
throw IterableElementError.noElement();
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static dynamic lastWhereList(List list,
|
|
|
|
bool test(dynamic value),
|
|
|
|
dynamic orElse()) {
|
|
|
|
// TODO(floitsch): check that arguments are of correct type?
|
|
|
|
for (int i = list.length - 1; i >= 0; i--) {
|
|
|
|
dynamic element = list[i];
|
|
|
|
if (test(element)) return element;
|
|
|
|
}
|
|
|
|
if (orElse != null) return orElse();
|
2014-05-05 09:26:56 +00:00
|
|
|
throw IterableElementError.noElement();
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static dynamic singleWhere(Iterable iterable, bool test(dynamic value)) {
|
|
|
|
dynamic result = null;
|
|
|
|
bool foundMatching = false;
|
|
|
|
for (dynamic element in iterable) {
|
|
|
|
if (test(element)) {
|
|
|
|
if (foundMatching) {
|
2014-05-05 09:26:56 +00:00
|
|
|
throw IterableElementError.tooMany();
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
result = element;
|
|
|
|
foundMatching = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (foundMatching) return result;
|
2014-05-05 09:26:56 +00:00
|
|
|
throw IterableElementError.noElement();
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
|
2014-11-21 10:29:15 +00:00
|
|
|
static elementAt(Iterable iterable, int index) {
|
|
|
|
if (index is! int) throw new ArgumentError.notNull("index");
|
|
|
|
RangeError.checkNotNegative(index, "index");
|
|
|
|
int elementIndex = 0;
|
2014-11-21 11:18:58 +00:00
|
|
|
for (var element in iterable) {
|
2014-11-21 10:29:15 +00:00
|
|
|
if (index == elementIndex) return element;
|
|
|
|
elementIndex++;
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
2014-11-21 10:29:15 +00:00
|
|
|
throw new RangeError.index(index, iterable, "index", null, elementIndex);
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static String join(Iterable iterable, [String separator]) {
|
|
|
|
StringBuffer buffer = new StringBuffer();
|
|
|
|
buffer.writeAll(iterable, separator);
|
|
|
|
return buffer.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
static String joinList(List list, [String separator]) {
|
|
|
|
if (list.isEmpty) return "";
|
|
|
|
if (list.length == 1) return "${list[0]}";
|
|
|
|
StringBuffer buffer = new StringBuffer();
|
|
|
|
if (separator.isEmpty) {
|
|
|
|
for (int i = 0; i < list.length; i++) {
|
|
|
|
buffer.write(list[i]);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
buffer.write(list[0]);
|
|
|
|
for (int i = 1; i < list.length; i++) {
|
|
|
|
buffer.write(separator);
|
|
|
|
buffer.write(list[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return buffer.toString();
|
2013-07-11 12:59:12 +00:00
|
|
|
}
|
|
|
|
|
2014-07-18 08:08:16 +00:00
|
|
|
Iterable<T> where(Iterable iterable, bool f(var element)) {
|
|
|
|
return new WhereIterable<T>(iterable, f);
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static Iterable map(Iterable iterable, f(var element)) {
|
|
|
|
return new MappedIterable(iterable, f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Iterable mapList(List list, f(var element)) {
|
|
|
|
return new MappedListIterable(list, f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Iterable expand(Iterable iterable, Iterable f(var element)) {
|
|
|
|
return new ExpandIterable(iterable, f);
|
|
|
|
}
|
|
|
|
|
2014-07-18 08:08:16 +00:00
|
|
|
Iterable<T> takeList(List list, int n) {
|
2013-04-15 12:34:44 +00:00
|
|
|
// The generic type is currently lost. It will be fixed with mixins.
|
2014-07-18 08:08:16 +00:00
|
|
|
return new SubListIterable<T>(list, 0, n);
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
|
2014-07-18 08:08:16 +00:00
|
|
|
Iterable<T> takeWhile(Iterable iterable, bool test(var value)) {
|
2013-04-15 12:34:44 +00:00
|
|
|
// The generic type is currently lost. It will be fixed with mixins.
|
2014-07-18 08:08:16 +00:00
|
|
|
return new TakeWhileIterable<T>(iterable, test);
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
|
2014-07-18 08:08:16 +00:00
|
|
|
Iterable<T> skipList(List list, int n) {
|
2013-04-15 12:34:44 +00:00
|
|
|
// The generic type is currently lost. It will be fixed with mixins.
|
2014-07-18 08:08:16 +00:00
|
|
|
return new SubListIterable<T>(list, n, null);
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
|
2014-07-18 08:08:16 +00:00
|
|
|
Iterable<T> skipWhile(Iterable iterable, bool test(var value)) {
|
2013-04-15 12:34:44 +00:00
|
|
|
// The generic type is currently lost. It will be fixed with mixins.
|
2014-07-18 08:08:16 +00:00
|
|
|
return new SkipWhileIterable<T>(iterable, test);
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
|
2014-07-18 08:08:16 +00:00
|
|
|
Iterable<T> reversedList(List list) {
|
|
|
|
return new ReversedListIterable<T>(list);
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void sortList(List list, int compare(a, b)) {
|
|
|
|
if (compare == null) compare = Comparable.compare;
|
|
|
|
Sort.sort(list, compare);
|
|
|
|
}
|
|
|
|
|
2013-10-07 12:07:30 +00:00
|
|
|
static void shuffleList(List list, Random random) {
|
|
|
|
if (random == null) random = new Random();
|
2013-10-03 11:09:01 +00:00
|
|
|
int length = list.length;
|
|
|
|
while (length > 1) {
|
|
|
|
int pos = random.nextInt(length);
|
|
|
|
length -= 1;
|
|
|
|
var tmp = list[length];
|
|
|
|
list[length] = list[pos];
|
|
|
|
list[pos] = tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-15 12:34:44 +00:00
|
|
|
static int indexOfList(List list, var element, int start) {
|
2013-12-19 10:40:53 +00:00
|
|
|
return Lists.indexOf(list, element, start, list.length);
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int lastIndexOfList(List list, var element, int start) {
|
|
|
|
if (start == null) start = list.length - 1;
|
2013-12-19 10:40:53 +00:00
|
|
|
return Lists.lastIndexOf(list, element, start);
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
|
2013-04-15 16:25:21 +00:00
|
|
|
static void _rangeCheck(List list, int start, int end) {
|
2014-11-21 10:29:15 +00:00
|
|
|
RangeError.checkValidRange(start, end, list.length);
|
2013-04-15 16:25:21 +00:00
|
|
|
}
|
|
|
|
|
2014-07-18 08:08:16 +00:00
|
|
|
Iterable<T> getRangeList(List list, int start, int end) {
|
2013-04-15 16:25:21 +00:00
|
|
|
_rangeCheck(list, start, end);
|
2013-04-15 12:34:44 +00:00
|
|
|
// The generic type is currently lost. It will be fixed with mixins.
|
2014-07-18 08:08:16 +00:00
|
|
|
return new SubListIterable<T>(list, start, end);
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
|
2013-04-15 13:52:29 +00:00
|
|
|
static void setRangeList(List list, int start, int end,
|
|
|
|
Iterable from, int skipCount) {
|
2013-04-15 16:25:21 +00:00
|
|
|
_rangeCheck(list, start, end);
|
2013-04-15 13:52:29 +00:00
|
|
|
int length = end - start;
|
2013-04-15 12:34:44 +00:00
|
|
|
if (length == 0) return;
|
|
|
|
|
2013-04-15 13:52:29 +00:00
|
|
|
if (skipCount < 0) throw new ArgumentError(skipCount);
|
2013-04-15 12:34:44 +00:00
|
|
|
|
2013-04-15 13:52:29 +00:00
|
|
|
// TODO(floitsch): Make this accept more.
|
|
|
|
List otherList;
|
|
|
|
int otherStart;
|
|
|
|
if (from is List) {
|
|
|
|
otherList = from;
|
|
|
|
otherStart = skipCount;
|
|
|
|
} else {
|
|
|
|
otherList = from.skip(skipCount).toList(growable: false);
|
|
|
|
otherStart = 0;
|
|
|
|
}
|
|
|
|
if (otherStart + length > otherList.length) {
|
2014-05-05 09:26:56 +00:00
|
|
|
throw IterableElementError.tooFew();
|
2013-04-15 13:52:29 +00:00
|
|
|
}
|
2013-12-19 10:40:53 +00:00
|
|
|
Lists.copy(otherList, otherStart, list, start, length);
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
|
2013-04-15 16:25:21 +00:00
|
|
|
static void replaceRangeList(List list, int start, int end,
|
|
|
|
Iterable iterable) {
|
|
|
|
_rangeCheck(list, start, end);
|
2013-10-14 10:40:00 +00:00
|
|
|
if (iterable is! EfficientLength) {
|
|
|
|
iterable = iterable.toList();
|
|
|
|
}
|
|
|
|
int removeLength = end - start;
|
|
|
|
int insertLength = iterable.length;
|
|
|
|
if (removeLength >= insertLength) {
|
|
|
|
int delta = removeLength - insertLength;
|
|
|
|
int insertEnd = start + insertLength;
|
|
|
|
int newEnd = list.length - delta;
|
|
|
|
list.setRange(start, insertEnd, iterable);
|
|
|
|
if (delta != 0) {
|
|
|
|
list.setRange(insertEnd, newEnd, list, end);
|
|
|
|
list.length = newEnd;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int delta = insertLength - removeLength;
|
|
|
|
int newLength = list.length + delta;
|
|
|
|
int insertEnd = start + insertLength; // aka. end + delta.
|
|
|
|
list.length = newLength;
|
|
|
|
list.setRange(insertEnd, newLength, list, end);
|
|
|
|
list.setRange(start, insertEnd, iterable);
|
|
|
|
}
|
2013-04-15 16:25:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void fillRangeList(List list, int start, int end, fillValue) {
|
|
|
|
_rangeCheck(list, start, end);
|
|
|
|
for (int i = start; i < end; i++) {
|
|
|
|
list[i] = fillValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void insertAllList(List list, int index, Iterable iterable) {
|
2014-11-21 12:00:11 +00:00
|
|
|
RangeError.checkValueInInterval(index, 0, list.length, "index");
|
2013-10-14 10:40:00 +00:00
|
|
|
if (iterable is! EfficientLength) {
|
2013-04-15 16:25:21 +00:00
|
|
|
iterable = iterable.toList(growable: false);
|
|
|
|
}
|
|
|
|
int insertionLength = iterable.length;
|
|
|
|
list.length += insertionLength;
|
|
|
|
list.setRange(index + insertionLength, list.length, list, index);
|
|
|
|
for (var element in iterable) {
|
|
|
|
list[index++] = element;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void setAllList(List list, int index, Iterable iterable) {
|
2014-11-21 10:29:15 +00:00
|
|
|
RangeError.checkValueInInterval(index, 0, list.length, "index");
|
2013-04-15 16:25:21 +00:00
|
|
|
for (var element in iterable) {
|
|
|
|
list[index++] = element;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-18 08:08:16 +00:00
|
|
|
Map<int, T> asMapList(List l) {
|
|
|
|
return new ListMapView<T>(l);
|
2013-04-15 12:34:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool setContainsAll(Set set, Iterable other) {
|
|
|
|
for (var element in other) {
|
|
|
|
if (!set.contains(element)) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Set setIntersection(Set set, Set other, Set result) {
|
|
|
|
Set smaller;
|
|
|
|
Set larger;
|
|
|
|
if (set.length < other.length) {
|
|
|
|
smaller = set;
|
|
|
|
larger = other;
|
|
|
|
} else {
|
|
|
|
smaller = other;
|
|
|
|
larger = set;
|
|
|
|
}
|
|
|
|
for (var element in smaller) {
|
|
|
|
if (larger.contains(element)) {
|
|
|
|
result.add(element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Set setUnion(Set set, Set other, Set result) {
|
|
|
|
result.addAll(set);
|
|
|
|
result.addAll(other);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Set setDifference(Set set, Set other, Set result) {
|
|
|
|
for (var element in set) {
|
|
|
|
if (!other.contains(element)) {
|
|
|
|
result.add(element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
2014-05-05 09:26:56 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates errors throw by [Iterable] when the element count is wrong.
|
|
|
|
*/
|
|
|
|
abstract class IterableElementError {
|
|
|
|
/** Error thrown thrown by, e.g., [Iterable.first] when there is no result. */
|
|
|
|
static StateError noElement() => new StateError("No element");
|
|
|
|
/** Error thrown by, e.g., [Iterable.single] if there are too many results. */
|
|
|
|
static StateError tooMany() => new StateError("Too many elements");
|
|
|
|
/** Error thrown by, e.g., [List.setRange] if there are too few elements. */
|
|
|
|
static StateError tooFew() => new StateError("Too few elements");
|
|
|
|
}
|