Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
96 changes: 96 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,102 @@ 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.)

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 did write it. Let me try putting it in an see how it feels. The iterator itself is simple. The complications come from me trying to optimize Iterable methods too.)

const emitBefore = 1;
const emitAfter = 2;
var state = before ? emitBefore : 0;
final afterState = emitBefore | (after ? emitAfter : 0);
for (var element in this) {
if (state & emitBefore != 0) yield separator;
state = afterState;
yield element;
}
if (state & emitAfter != 0) 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 hasElement = false;
return [
for (var element in this) ...[
if (hasElement || (hasElement = true) && before) separator,
element,
],
if (hasElement && after) separator
];
}

/// The elements that do not satisfy [test].
Iterable<T> whereNot(bool Function(T element) test) =>
where((element) => !test(element));
Expand Down
108 changes: 108 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,114 @@ 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]..insertSeparator(-1)); // [1, -1, 2, -1, 3]
/// print([1]..insertSeparator(-1)); // [1]
/// print([]..insertSeparator(-1)); // []
///
/// print([1, 2, 3]..insertSeparator(
/// -1
/// before: true,
/// )); // [-1, 1, -1, 2, -1, 3]
///
/// print([1]..insertSeparator(
/// -1
/// before: true,
/// after: true,
/// )); // [-1, 1, -1]
///
/// print([]..insertSeparator(
/// -1
/// before: true,
/// after: true,
/// )); // []
/// ```
void separate(E separator, {bool before = false, bool after = false}) {
var length = this.length;
if (length == 0) return;
var newLength = length * 2 - 1;
var offset = 0;
if (before) {
newLength++;
offset = 1;
}
if (after) newLength++;
E newElementAt(int index) {
index -= offset;
if (index.isOdd) return separator;
return this[index >> 1];
}

for (var i = length; i < newLength; i++) {
add(newElementAt(i));
}
for (var i = length, firstChanged = offset ^ 1; i > firstChanged;) {
--i;
this[i] = newElementAt(i);
}
}
}

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