Related
Assume I have a list like:
var letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
I would like a list of lists of 2 elements each:
var chunks = [['a', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h']];
What's a good way to do this with Dart?
Here is another way:
var chunks = [];
int chunkSize = 2;
for (var i = 0; i < letters.length; i += chunkSize) {
chunks.add(letters.sublist(i, i+chunkSize > letters.length ? letters.length : i + chunkSize));
}
return chunks;
Run it on dartpad
Quiver (version >= 0.18) supplies partition() as part of its iterables library (import 'package:quiver/iterables.dart'). The implementation returns lazily-computed Iterable, making it pretty efficient. Use as:
var letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
var pairs = partition(letters, 2);
The returned pairs will be an Iterable<List> that looks like:
[['a', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h']]
A slight improvement on Seth's answer to make it work with any list or chunk size:
var len = letters.length;
var size = 2;
var chunks = [];
for(var i = 0; i< len; i+= size)
{
var end = (i+size<len)?i+size:len;
chunks.add(letters.sublist(i,end));
}
pairs(list) => list.isEmpty ? list : ([list.take(2)]..addAll(pairs(list.skip(2))));
The official Dart's collection package has slices extension method, used like this:
final letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
final chunks = letters.slices(2); // [['a', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h']]
another solution;
List chunk(List list, int chunkSize) {
List chunks = [];
int len = list.length;
for (var i = 0; i < len; i += chunkSize) {
int size = i+chunkSize;
chunks.add(list.sublist(i, size > len ? len : size));
}
return chunks;
}
List nums = [1,2,3,4,5];
print(chunk(nums, 2));
// [[1,2], [3,4], [5]]
I found a simple solution:
var subList = mylist.take(3); // take 3 items first
var subList = mylist.skip(2).take(3); // take [2..5] items
Here is one way:
letters.fold([[]], (list, x) {
return list.last.length == 2 ? (list..add([x])) : (list..last.add(x));
});
another way:
extension IterableExtensions<E> on Iterable<E> {
Iterable<List<E>> chunked(int chunkSize) sync* {
if (length <= 0) {
yield [];
return;
}
int skip = 0;
while (skip < length) {
final chunk = this.skip(skip).take(chunkSize);
yield chunk.toList(growable: false);
skip += chunkSize;
if (chunk.length < chunkSize) return;
}
}
}
tests:
void main() {
test("list chunked", () {
final emptyList = [];
final letters = ['a', 'b', 'c', 'd', 'e', 'f'];
final digits = List.generate(32, (index) => index);
print(emptyList.chunked(2));
print(letters.chunked(2));
print(digits.chunked(2));
print(emptyList.chunked(3));
print(letters.chunked(3));
print(digits.chunked(3));
print(emptyList.chunked(5));
print(letters.chunked(5));
print(digits.chunked(5));
});
}
output:
([])
([a, b], [c, d], [e, f])
([0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11], ..., [28, 29], [30, 31])
([])
([a, b, c], [d, e, f])
([0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], ..., [27, 28, 29], [30, 31])
([])
([a, b, c, d, e], [f])
([0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], ..., [25, 26, 27, 28, 29], [30, 31])
This way works with odd length lists:
var nums = [1, 2, 3, 4, 5];
var pairs = new List.generate(nums.length~/2, (i) => [nums[2 * i], nums[2 * i + 1]]);
Perhaps you might want to throw an error or provide a filler value if the list length is not even.
I would suggest creating an iterable of the pairs, and using .toList if you really need it as a list. This solution can also be applied to any iterable, not just a list. First, a simple solution that only works on lists (with even length)(Like the solution provided from Robert King):
new Iterable.generate(letters.length ~/ 2,
(i) => [letters[2*i], letters[2*i + 1]])
The more general solution is complex:
class mappedIterable extends Object implements Iterable with IterableMixin {
Function generator;
mappedIterable(Iterable source, Iterator this.generator(Iterator in));
Iterator get iterator => generator(source.iterator);
}
class Pairs implements Iterator {
Iterator _source;
List _current = null;
Pairs(Iterator this._source);
List get current => _current;
bool moveNext() {
bool result = _source.moveNext();
_current = [_source.current, (_source..moveNext()).current];
return result;
}
}
Iterable makePairs(Iterable source) =>
new mappedIterable(source, (sourceIterator) => new Pairs(sourceIterator));
print(makePairs(letters))
It seems like it is actually easier to make a stream of pairs from a stream, than to make an iterable of pairs from an iterable.
Here's the old style solution using indexed for loops and generics:
List<List<T>> _generateChunks<T>(List<T> inList, int chunkSize) {
List<List<T>> outList = [];
List<T> tmpList = [];
int counter = 0;
for (int current = 0; current < inList.length; current++) {
if (counter != chunkSize) {
tmpList.add(inList[current]);
counter++;
}
if (counter == chunkSize || current == inList.length - 1) {
outList.add(tmpList.toList());
tmpList.clear();
counter = 0;
}
}
return outList;
}
Using the example
main() {
var letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
int chunkSize = 2;
List<List<String>> chunks = _generateChunks(letters, chunkSize);
print(chunks);
}
The output is:
[[a, b], [c, d], [e, f], [g, h]]
Sublist
You can also extract part of a list using sublist:
var list = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
final middle = list.length ~/ 2;
final part1 = list.sublist(0, middle);
final part2 = list.sublist(middle);
print(part1); // [a, b, c, d]
print(part2); // [e, f, g, h]
Notes:
sublist takes two parameters, start (inclusive) and end (exclusive).
end is optional. If you don't specify an end, then the default is the end of the list.
sublist returns a new list from the given range.
One more solution because some of these look a bit more complicated than necessary:
extension _IterableExtensions<T> on Iterable<T> {
Iterable<List<T>> chunks(int chunkSize) sync* {
final chunk = <T>[];
for (T item in this) {
chunk.add(item);
if (chunk.length == chunkSize) {
yield chunk;
chunk.clear();
}
}
if (chunk.isNotEmpty) yield chunk;
}
}
Influenced by #Alan's answer above and extending List, the equivalent of F# chunkedBySize and windowed and average could be:
import 'dart:collection';
class functionalList<E> extends ListBase<E> {
final List<E> l = [];
functionalList();
void set length(int newLength) { l.length = newLength; }
int get length => l.length;
E operator [](int index) => l[index];
void operator []=(int index, E value) { l[index] = value; }
chunkBySize(int size) => _chunkBySize(l, size);
windowed(int size) => _windowed(l, size);
get average => l.isEmpty
? 0
: l.fold(0, (t, e) => t + e) / l.length;
_chunkBySize(List list, int size) => list.isEmpty
? list
: ([list.take(size)]..addAll(_chunkBySize(list.skip(size), size)));
_windowed(List list, int size) => list.isEmpty
? list
: ([list.take(size)]..addAll(_windowed(list.skip(1), size)));
}
void main() {
var list = new functionalList();
list.addAll([1,2,3]);
print(list.chunkBySize(2));
}
The implementation can be seen here
Late to the party, but to whomever needing this: an extension-based solution:
extension Windowed<E> on Iterable<E> {
Iterable<List<E>> window(int size) sync* {
if (size <= 0) throw ArgumentError.value(size, 'size', "can't be negative");
final Iterator<E> iterator = this.iterator;
while (iterator.moveNext()) {
final List<E> slice = [iterator.current];
for (int i = 1; i < size; i++) {
if (!iterator.moveNext()) break;
slice.add(iterator.current);
}
yield slice;
}
}
}
Split list on equal chunks of size n (the last chunk is the remainder)
Iterable<List<T>> chunks<T>(List<T> lst, int n) sync* {
final gen = List.generate(lst.length ~/ n + 1, (e) => e * n);
for (int i in gen) {
if (i < lst.length)
yield lst.sublist(i, i + n < lst.length ? i + n : lst.length);
}
}
Usage example:
chunks([2, 3, 4, 5, 6, 7, 5, 20, 33], 4).forEach(print);
chunks(['a', 'b', 'c'], 2).forEach(print);
Now that Dart has for loops inside list literals, another possible approach is:
List<List<T>> chunk<T>(List<T> elements, int chunkSize) => [
for (var i = 0; i < elements.length; i+= chunkSize) [
for (var j = 0; j < chunkSize && i + j < elements.length; j++)
elements[i + j]
]
];
or, slightly shorter, but not as efficient:
List<List<T>> chunk<T>(List<T> elements, int chunkSize) => [
for (var i = 0; i < elements.length; i+= chunkSize) [
...elements.getRange(i, i + j)
]
];
Those can, as usual, also be made as extension methods instead, as:
extension ListChunk<T> on List<T> {
List<List<T>> chunk(int chunkSize) =>
... `this` instead of `elements` ...
}
Alternative to sublist using take and skip. Take N elements each time even if the original list is bigger.
List<String> names = [
"Link",
"Alloy",
"Mario",
"Hollow",
"Leon",
"Claire",
"Steve",
"Terry",
"Iori",
"King K. rool"
];
int length = names.length;
int chunkSize = 3;
int index = 0;
while (index < length) {
var chunk = names.skip(index).take(chunkSize);
print(chunk);
index += chunkSize;
}
Output:
(Link, Alloy, Mario)
(Hollow, Leon, Claire)
(Steve, Terry, Iori)
(King K. rool)
Adding my 2 cents on this question, I wish there was a solution that accepts negative numbers (to allow chunk in reverse order), so here we are:
import 'dart:math';
extension ChunkedList<T> on List<T> {
List<List<T>> chunked(int size, {bool incomplete = false}) {
if (size == 0) {
throw ArgumentError.value(
size,
'chunked',
'[size] must be a non-zero integer.',
);
}
final List<T> target = size.isNegative ? reversed.toList() : toList();
final int n = size.abs();
final int base = incomplete ? (length / n).ceil() : (length / n).floor();
return <List<T>>[
for (int i = 0; i < base; i++)
target.sublist(i * n, min((i + 1) * n, length)),
];
}
}
Usage:
print(<int>[1, 2, 3, 4, 5].chunked(2, incomplete: false)); // [[1, 2], [3, 4]]
print(<int>[1, 2, 3, 4, 5].chunked(2, incomplete: true)); // [[1, 2], [3, 4], [5]]
print(<int>[1, 2, 3, 4, 5].chunked(-2, incomplete: false)); // [[5, 4], [3, 2]]
print(<int>[1, 2, 3, 4, 5].chunked(-2, incomplete: true)); // [[5, 4], [3, 2], [1]]
Fully-typed.
Supports any type.
Support negative numbers.
Try it online.
This function returns the sublist from an original array given the chunk size.
chunkArray(List<dynamic> original, int size) {
var sublist = List.generate((original.length ~/ size) + 1, (e) => []);
for (var i = 0; i < sublist.length; i++) {
int remaining=(original.length - i * size);
sublist[i] = original.sublist(
i * size,
i * size +
( remaining> size
? size
: remaining));
}
return sublist;
}
Before adding a list I want to check that it does not exist so there are no repeated values. This works with a list of integers, but not with a list of other integer lists:
void main() {
var listasN = List<int>();
var n1 = 1;
var n2 = 2;
var n3 = 1;
void addN(int n) {
if (!listasN.contains(n)) {
listasN.add(n);
}
}
addN(n1);
addN(n2);
addN(n3);
print(listasN);
var listas = List<List<int>>();
var lista1 = [1, 2, 3, 4];
var lista2 = [5, 6, 7, 8];
var lista3 = [1, 2, 3, 4];
void addLista(List<int> ls) {
if (!listas.contains(ls)) {
listas.add(ls);
}
}
addLista(lista1);
addLista(lista2);
addLista(lista3);
print(listas);
}
Out:
[1, 2]
[[1, 2, 3, 4], [5, 6, 7, 8], [1, 2, 3, 4]]
The first function does not support repeated values, but the second does, why?
Dart lists do not have an inherent equality. That is, the == operator only considers lists equal if it's the same list, not if it's different lists with the same content.
That's generally how Dart handles equality of mutable types. The contains method uses equality for checking.
So, what you can do instead is to create a "list equals" method:
bool listEquals<T>(List<T> list1, List<T> list2) {
if (list1.length != list2.length) return false;
for (var i = 0; i < list1.length; i++) {
if (list1[i] != list2[i]) return false;
}
return true;
}
Then you can check if a similar list is contained in your lists:
void addLista(List<int> ls) {
if (!listas.any((ls2) => listEquals(ls, ls2))) {
listas.add(ls);
}
}
I am just creating a simple ToDo App in Flutter. I am managing all the todo tasks on the list. I want to add any new todo tasks at the beginning of the list. I am able to use this workaround kind of thing to achieve that. Is there any better way to do this?
void _addTodoInList(BuildContext context){
String val = _textFieldController.text;
final newTodo = {
"title": val,
"id": Uuid().v4(),
"done": false
};
final copiedTodos = List.from(_todos);
_todos.removeRange(0, _todos.length);
setState(() {
_todos.addAll([newTodo, ...copiedTodos]);
});
Navigator.pop(context);
}
Use insert() method of List to add the item, here the index would be 0 to add it in the beginning. Example:
List<String> list = ["B", "C", "D"];
list.insert(0, "A"); // at index 0 we are adding A
// list now becomes ["A", "B", "C", "D"]
Use
List.insert(index, value);
I would like to add another way to attach element at the start of a list like this
var list=[1,2,3];
var a=0;
list=[a,...list];
print(list);
//prints [0, 1, 2, 3]
Adding a new item to the beginnig and ending of the list:
List<String> myList = ["You", "Can", "Do", "It"];
myList.insert(0, "Sure"); // adding a new item to the beginning
// new list is: ["Sure", "You", "Can", "Do", "It"];
lastItemIndex = myList.length;
myList.insert(lastItemIndex, "Too"); // adding a new item to the ending
// new list is: ["You", "Can", "Do", "It", "Too"];
The other answers are good, but now that Dart has something very similar to Python's list comprehension I'd like to note it.
// Given
List<int> list = [2, 3, 4];
list = [
1,
for (int item in list) item,
];
or
list = [
1,
...list,
];
results in [1, 2, 3, 4]
Better yet and in case you want to add more items :-
List<int> myList = <int>[1, 2, 3, 4, 5];
myList = <int>[-5, -4, -3, -2, -1, ...myList];
For those who are looking for an easy method to add multiple items at the start position you can use this reference:
List<int> _listOne = [4,5,6,7];
List<int> _listTwo = [1,2,3];
_listOne.insertAll(0, _listTwo);
This is a Swift 3.0 version of a previously presented Swift 2.0 solution.
import Cocoa
func findAll<T: Equatable, C: Collection>(items: C, itemToFind: T) -> [C.Index]? where C.Iterator.Element == T {
guard items.contains(itemToFind) else {
return nil
}
var result = Array<C.Index>()
var index = items.startIndex
for item in items {
if item == itemToFind {
result.append(index)
}
index = items.index(after: index) // index = index.successor()
}
return result
}
print("\nList the Indicies within 'items' of where 'itemToFind' was found")
let Indexes = findAll([items: [5, 3, 7, 3, 9], itemToFind: 3) // result expected: [1, 3]
let Indexes = findAll([items: ["c", "a", "b", "c", "a"], itemToFind: "b") // result expected: [1, 4]
let stringChars = ["Hello, playground!".characters]
let stringIndexes = findAll(items: stringChars, itemToFind: Character("l")) // result expected: [2, 3, 8]
The compiler doesn't help me, the only errors reported complain about "expected ',' separator missing in the 4 statements after the only print statement.
Can anyone point out where I've either written the routine incorrectly or miss-understood how to input the parameters in the call to the 'findAll' function?
let Indexes = findAll([items: [5, 3, 7, 3, 9], itemToFind: 3)
^ 🤷
This line has a stray square bracket at the location I've marked. Remove it.
let Indexes = findAll([items: ["c", "a", "b", "c", "a"], itemToFind: "b")
Same problem, same place. Also, I'm not sure why you expect this to return [1, 4] -- if you'd passed itemToFind: "c" I'd agree, but "b" only appears once, at position [2].
(Also, you'll need to name the variable something other than Indexes -- that name is already in use from the previous line.)
let stringChars = ["Hello, playground!".characters]
Wouldn't you know it, this line doesn't need either of its square brackets! characters is already a CharacterView (which behaves enough like an array for findAll() to work); it doesn't need to be wrapped into another level of array.
This works in Swift 3.0
import Cocoa
func findAll<T: Equatable, C: Collection>(items: C, itemToFind: T) -> [C.Index]? where C.Iterator.Element == T {
guard items.contains(itemToFind) else {
return nil
}
var result = Array<C.Index>()
var index = items.startIndex
for item in items {
if item == itemToFind {
result.append(index)
}
index = items.index(after: index) // index = index.successor()
}
return result
// Another approach:
// return items.indices.filter { items[$0] == itemToFind }
}
print("\nList the Indicies within 'items' of where 'itemToFind' was found")
let Indexes1 = findAll(items: [5, 3, 7, 3, 9], itemToFind: 3) // result: [1, 3]
print(Indexes1!)
let Indexes2 = findAll(items: ["c", "a", "b", "c", "a"], itemToFind: "c") // result: [0, 3]
print(Indexes2!)
func offsetValue(input: String.CharacterView, position: String.CharacterView.Index) -> Int {
let offset = input.distance(from: input.startIndex, to: position)
return offset
}
var i = 0
let stringChars = "Hello, playground!".characters
let stringIndexes = findAll(items: stringChars, itemToFind: Character("l")) // result: [2, 3, 8]
print("[", terminator: "")
for stringIndex in stringIndexes! {
let stringIndexInt = offsetValue(input:stringChars, position: stringIndex)
print("\(stringIndexInt)", terminator: "")
i += 1
if i < (stringIndexes?.count)! {
print(", ", terminator: "")
}
}
print("]")
Assume I have a list like:
var letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
I would like a list of lists of 2 elements each:
var chunks = [['a', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h']];
What's a good way to do this with Dart?
Here is another way:
var chunks = [];
int chunkSize = 2;
for (var i = 0; i < letters.length; i += chunkSize) {
chunks.add(letters.sublist(i, i+chunkSize > letters.length ? letters.length : i + chunkSize));
}
return chunks;
Run it on dartpad
Quiver (version >= 0.18) supplies partition() as part of its iterables library (import 'package:quiver/iterables.dart'). The implementation returns lazily-computed Iterable, making it pretty efficient. Use as:
var letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
var pairs = partition(letters, 2);
The returned pairs will be an Iterable<List> that looks like:
[['a', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h']]
A slight improvement on Seth's answer to make it work with any list or chunk size:
var len = letters.length;
var size = 2;
var chunks = [];
for(var i = 0; i< len; i+= size)
{
var end = (i+size<len)?i+size:len;
chunks.add(letters.sublist(i,end));
}
pairs(list) => list.isEmpty ? list : ([list.take(2)]..addAll(pairs(list.skip(2))));
The official Dart's collection package has slices extension method, used like this:
final letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
final chunks = letters.slices(2); // [['a', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h']]
another solution;
List chunk(List list, int chunkSize) {
List chunks = [];
int len = list.length;
for (var i = 0; i < len; i += chunkSize) {
int size = i+chunkSize;
chunks.add(list.sublist(i, size > len ? len : size));
}
return chunks;
}
List nums = [1,2,3,4,5];
print(chunk(nums, 2));
// [[1,2], [3,4], [5]]
I found a simple solution:
var subList = mylist.take(3); // take 3 items first
var subList = mylist.skip(2).take(3); // take [2..5] items
Here is one way:
letters.fold([[]], (list, x) {
return list.last.length == 2 ? (list..add([x])) : (list..last.add(x));
});
another way:
extension IterableExtensions<E> on Iterable<E> {
Iterable<List<E>> chunked(int chunkSize) sync* {
if (length <= 0) {
yield [];
return;
}
int skip = 0;
while (skip < length) {
final chunk = this.skip(skip).take(chunkSize);
yield chunk.toList(growable: false);
skip += chunkSize;
if (chunk.length < chunkSize) return;
}
}
}
tests:
void main() {
test("list chunked", () {
final emptyList = [];
final letters = ['a', 'b', 'c', 'd', 'e', 'f'];
final digits = List.generate(32, (index) => index);
print(emptyList.chunked(2));
print(letters.chunked(2));
print(digits.chunked(2));
print(emptyList.chunked(3));
print(letters.chunked(3));
print(digits.chunked(3));
print(emptyList.chunked(5));
print(letters.chunked(5));
print(digits.chunked(5));
});
}
output:
([])
([a, b], [c, d], [e, f])
([0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11], ..., [28, 29], [30, 31])
([])
([a, b, c], [d, e, f])
([0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], ..., [27, 28, 29], [30, 31])
([])
([a, b, c, d, e], [f])
([0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], ..., [25, 26, 27, 28, 29], [30, 31])
This way works with odd length lists:
var nums = [1, 2, 3, 4, 5];
var pairs = new List.generate(nums.length~/2, (i) => [nums[2 * i], nums[2 * i + 1]]);
Perhaps you might want to throw an error or provide a filler value if the list length is not even.
I would suggest creating an iterable of the pairs, and using .toList if you really need it as a list. This solution can also be applied to any iterable, not just a list. First, a simple solution that only works on lists (with even length)(Like the solution provided from Robert King):
new Iterable.generate(letters.length ~/ 2,
(i) => [letters[2*i], letters[2*i + 1]])
The more general solution is complex:
class mappedIterable extends Object implements Iterable with IterableMixin {
Function generator;
mappedIterable(Iterable source, Iterator this.generator(Iterator in));
Iterator get iterator => generator(source.iterator);
}
class Pairs implements Iterator {
Iterator _source;
List _current = null;
Pairs(Iterator this._source);
List get current => _current;
bool moveNext() {
bool result = _source.moveNext();
_current = [_source.current, (_source..moveNext()).current];
return result;
}
}
Iterable makePairs(Iterable source) =>
new mappedIterable(source, (sourceIterator) => new Pairs(sourceIterator));
print(makePairs(letters))
It seems like it is actually easier to make a stream of pairs from a stream, than to make an iterable of pairs from an iterable.
Here's the old style solution using indexed for loops and generics:
List<List<T>> _generateChunks<T>(List<T> inList, int chunkSize) {
List<List<T>> outList = [];
List<T> tmpList = [];
int counter = 0;
for (int current = 0; current < inList.length; current++) {
if (counter != chunkSize) {
tmpList.add(inList[current]);
counter++;
}
if (counter == chunkSize || current == inList.length - 1) {
outList.add(tmpList.toList());
tmpList.clear();
counter = 0;
}
}
return outList;
}
Using the example
main() {
var letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
int chunkSize = 2;
List<List<String>> chunks = _generateChunks(letters, chunkSize);
print(chunks);
}
The output is:
[[a, b], [c, d], [e, f], [g, h]]
Sublist
You can also extract part of a list using sublist:
var list = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
final middle = list.length ~/ 2;
final part1 = list.sublist(0, middle);
final part2 = list.sublist(middle);
print(part1); // [a, b, c, d]
print(part2); // [e, f, g, h]
Notes:
sublist takes two parameters, start (inclusive) and end (exclusive).
end is optional. If you don't specify an end, then the default is the end of the list.
sublist returns a new list from the given range.
One more solution because some of these look a bit more complicated than necessary:
extension _IterableExtensions<T> on Iterable<T> {
Iterable<List<T>> chunks(int chunkSize) sync* {
final chunk = <T>[];
for (T item in this) {
chunk.add(item);
if (chunk.length == chunkSize) {
yield chunk;
chunk.clear();
}
}
if (chunk.isNotEmpty) yield chunk;
}
}
Influenced by #Alan's answer above and extending List, the equivalent of F# chunkedBySize and windowed and average could be:
import 'dart:collection';
class functionalList<E> extends ListBase<E> {
final List<E> l = [];
functionalList();
void set length(int newLength) { l.length = newLength; }
int get length => l.length;
E operator [](int index) => l[index];
void operator []=(int index, E value) { l[index] = value; }
chunkBySize(int size) => _chunkBySize(l, size);
windowed(int size) => _windowed(l, size);
get average => l.isEmpty
? 0
: l.fold(0, (t, e) => t + e) / l.length;
_chunkBySize(List list, int size) => list.isEmpty
? list
: ([list.take(size)]..addAll(_chunkBySize(list.skip(size), size)));
_windowed(List list, int size) => list.isEmpty
? list
: ([list.take(size)]..addAll(_windowed(list.skip(1), size)));
}
void main() {
var list = new functionalList();
list.addAll([1,2,3]);
print(list.chunkBySize(2));
}
The implementation can be seen here
Late to the party, but to whomever needing this: an extension-based solution:
extension Windowed<E> on Iterable<E> {
Iterable<List<E>> window(int size) sync* {
if (size <= 0) throw ArgumentError.value(size, 'size', "can't be negative");
final Iterator<E> iterator = this.iterator;
while (iterator.moveNext()) {
final List<E> slice = [iterator.current];
for (int i = 1; i < size; i++) {
if (!iterator.moveNext()) break;
slice.add(iterator.current);
}
yield slice;
}
}
}
Split list on equal chunks of size n (the last chunk is the remainder)
Iterable<List<T>> chunks<T>(List<T> lst, int n) sync* {
final gen = List.generate(lst.length ~/ n + 1, (e) => e * n);
for (int i in gen) {
if (i < lst.length)
yield lst.sublist(i, i + n < lst.length ? i + n : lst.length);
}
}
Usage example:
chunks([2, 3, 4, 5, 6, 7, 5, 20, 33], 4).forEach(print);
chunks(['a', 'b', 'c'], 2).forEach(print);
Now that Dart has for loops inside list literals, another possible approach is:
List<List<T>> chunk<T>(List<T> elements, int chunkSize) => [
for (var i = 0; i < elements.length; i+= chunkSize) [
for (var j = 0; j < chunkSize && i + j < elements.length; j++)
elements[i + j]
]
];
or, slightly shorter, but not as efficient:
List<List<T>> chunk<T>(List<T> elements, int chunkSize) => [
for (var i = 0; i < elements.length; i+= chunkSize) [
...elements.getRange(i, i + j)
]
];
Those can, as usual, also be made as extension methods instead, as:
extension ListChunk<T> on List<T> {
List<List<T>> chunk(int chunkSize) =>
... `this` instead of `elements` ...
}
Alternative to sublist using take and skip. Take N elements each time even if the original list is bigger.
List<String> names = [
"Link",
"Alloy",
"Mario",
"Hollow",
"Leon",
"Claire",
"Steve",
"Terry",
"Iori",
"King K. rool"
];
int length = names.length;
int chunkSize = 3;
int index = 0;
while (index < length) {
var chunk = names.skip(index).take(chunkSize);
print(chunk);
index += chunkSize;
}
Output:
(Link, Alloy, Mario)
(Hollow, Leon, Claire)
(Steve, Terry, Iori)
(King K. rool)
Adding my 2 cents on this question, I wish there was a solution that accepts negative numbers (to allow chunk in reverse order), so here we are:
import 'dart:math';
extension ChunkedList<T> on List<T> {
List<List<T>> chunked(int size, {bool incomplete = false}) {
if (size == 0) {
throw ArgumentError.value(
size,
'chunked',
'[size] must be a non-zero integer.',
);
}
final List<T> target = size.isNegative ? reversed.toList() : toList();
final int n = size.abs();
final int base = incomplete ? (length / n).ceil() : (length / n).floor();
return <List<T>>[
for (int i = 0; i < base; i++)
target.sublist(i * n, min((i + 1) * n, length)),
];
}
}
Usage:
print(<int>[1, 2, 3, 4, 5].chunked(2, incomplete: false)); // [[1, 2], [3, 4]]
print(<int>[1, 2, 3, 4, 5].chunked(2, incomplete: true)); // [[1, 2], [3, 4], [5]]
print(<int>[1, 2, 3, 4, 5].chunked(-2, incomplete: false)); // [[5, 4], [3, 2]]
print(<int>[1, 2, 3, 4, 5].chunked(-2, incomplete: true)); // [[5, 4], [3, 2], [1]]
Fully-typed.
Supports any type.
Support negative numbers.
Try it online.
This function returns the sublist from an original array given the chunk size.
chunkArray(List<dynamic> original, int size) {
var sublist = List.generate((original.length ~/ size) + 1, (e) => []);
for (var i = 0; i < sublist.length; i++) {
int remaining=(original.length - i * size);
sublist[i] = original.sublist(
i * size,
i * size +
( remaining> size
? size
: remaining));
}
return sublist;
}