Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkgs/collection/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## 1.20.0-wip

- Adds `separated` and `separatedList` extension methods to `Iterable`.
- Adds `separate` extension method to `List`
- Add `IterableMapEntryExtension` for working on `Map` as a list of pairs, using
`Map.entries`.
- Explicitly mark `BoolList` as `abstract interface`
Expand Down
106 changes: 106 additions & 0 deletions pkgs/collection/lib/src/iterable_extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,112 @@ extension IterableExtension<T> on Iterable<T> {
return chosen;
}

/// The elements of this iterable separated by [separator].
///
/// Emits the same elements as this iterable, and also emits
/// a [separator] between any two of those elements.
///
/// If [before] is set to `true`, a [separator] is also
/// emitted before the first element.
/// If [after] is set to `true`, a [separator] is also
/// emitted after the last element.
///
/// If this iterable is empty, [before] and [after] have no effect.
///
/// Example:
/// ```dart
/// print([1, 2, 3].separated(-1)); // (1, -1, 2, -1, 3)
/// print([1].separated(-1)); // (1)
/// print([].separated(-1)); // ()
///
/// print([1, 2, 3].separated(
/// -1,
/// before: true,
/// )); // (-1, 1, -1, 2, -1, 3)
///
/// print([1].separated(
/// -1,
/// before: true,
/// after: true,
/// )); // (-1, 1, -1)
///
/// print([].separated(
/// -1,
/// before: true,
/// after: true,
/// )); // ()
/// ```
Iterable<T> separated(T separator,
{bool before = false, bool after = false}) sync* {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I can write a non-sync* implementation if we worry about performance, but I expect that most uses will be of separatedList or separate instead.)

var iterator = this.iterator;
if (iterator.moveNext()) {
if (before) yield separator;
while (true) {
yield iterator.current;
if (iterator.moveNext()) {
yield separator;
} else {
break;
}
}
if (after) yield separator;
}
}

/// Creates new list with the elements of this list separated by [separator].
///
/// Returns a new list which contains the same elements as this list,
/// with a [separator] between any two of those elements.
///
/// If [before] is set to `true`, a [separator] is also
/// added before the first element.
/// If [after] is set to `true`, a [separator] is also
/// added after the last element.
///
/// If this list is empty, [before] and [after] have no effect.
///
/// Example:
/// ```dart
/// print([1, 2, 3].separatedList(-1)); // [1, -1, 2, -1, 3]
/// print([1].separatedList(-1)); // [1]
/// print([].separatedList(-1)); // []
///
/// print([1, 2, 3].separatedList(
/// -1,
/// before: true,
/// )); // [-1, 1, -1, 2, -1, 3]
///
/// print([1].separatedList(
/// -1,
/// before: true,
/// after: true,
/// )); // [-1, 1, -1]
///
/// print([].separatedList(
/// -1,
/// before: true,
/// after: true,
/// )); // []
/// ```
List<T> separatedList(T separator,
{bool before = false, bool after = false}) {
var result = <T>[];
var iterator = this.iterator;
if (iterator.moveNext()) {
if (before) result.add(separator);
while (true) {
result.add(iterator.current);
if (iterator.moveNext()) {
result.add(separator);
} else {
break;
}
}
if (after) result.add(separator);
}
return result;
}

/// The elements that do not satisfy [test].
Iterable<T> whereNot(bool Function(T element) test) =>
where((element) => !test(element));
Expand Down
116 changes: 116 additions & 0 deletions pkgs/collection/lib/src/list_extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,122 @@ extension ListExtensions<E> on List<E> {
yield slice(i, min(i + length, this.length));
}
}

/// Creates new list with the elements of this list separated by [separator].
///
/// Returns a new list which contains the same elements as this list,
/// with a [separator] between any two of those elements.
///
/// If [before] is set to `true`, a [separator] is also
/// added before the first element.
/// If [after] is set to `true`, a [separator] is also
/// added after the last element.
///
/// If this list is empty, [before] and [after] have no effect.
///
/// Example:
/// ```dart
/// print([1, 2, 3].separatedList(-1)); // [1, -1, 2, -1, 3]
/// print([1].separatedList(-1)); // [1]
/// print([].separatedList(-1)); // []
///
/// print([1, 2, 3].separatedList(
/// -1
/// before: true,
/// )); // [-1, 1, -1, 2, -1, 3]
///
/// print([1].separatedList(
/// -1
/// before: true,
/// after: true,
/// )); // [-1, 1, -1]
///
/// print([].separatedList(
/// -1
/// before: true,
/// after: true,
/// )); // []
/// ```
List<E> separatedList(E separator,
{bool before = false, bool after = false}) =>
isEmpty
? []
: [
if (!before) this[0],
for (var i = before ? 0 : 1; i < length; i++) ...[
separator,
this[i],
],
if (after) separator
];

/// Inserts [separator] between elements of this list.
///
/// Afterwards, the list will contains all the original elements,
/// with a [separator] between any two of those elements.
///
/// If [before] is set to `true`, a [separator] is also
/// inserted before the first element.
/// If [after] is set to `true`, a [separator] is also
/// added after the last element.
///
/// If this list is empty, [before] and [after] have no effect.
///
/// Example:
/// ```dart
/// print([1, 2, 3]..separate(-1)); // [1, -1, 2, -1, 3]
/// print([1]..separate(-1)); // [1]
/// print([]..separate(-1)); // []
///
/// print([1, 2, 3]..separate(
/// -1,
/// before: true,
/// )); // [-1, 1, -1, 2, -1, 3]
///
/// print([1]..separate(
/// -1,
/// before: true,
/// after: true,
/// )); // [-1, 1, -1]
///
/// print([]..separate(
/// -1,
/// before: true,
/// after: true,
/// )); // []
/// ```
void separate(E separator, {bool before = false, bool after = false}) {
var length = this.length;
if (length == 0) return;
// New position of first element.
var newFirst = before ? 1 : 0;
// New position of last element.
var newLast = length * 2 - (newFirst ^ 1);

var splitIndex = length - newFirst;
var cursor = splitIndex >> 1;
if (splitIndex.isEven) {
add(this[cursor]);
}
cursor++;
while (this.length < newLast) {
add(separator);
add(this[cursor++]);
}
assert(cursor == length);
if (after) add(separator);

cursor = splitIndex >> 1;
if (splitIndex.isOdd) {
this[--length] = this[cursor];
}
cursor--;
for (var i = length; i > 1;) {
this[--i] = separator;
this[--i] = this[cursor--];
}
if (newFirst != 0) this[0] = separator;
}
}

/// Various extensions on lists of comparable elements.
Expand Down
Loading