Dart: Check items in List of lists - list

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);
}
}

Related

Generate a random list based on limited items with spock-genesis

I use spock-genesis and would like to have an infinite lists generator parametrized with a list of values. A generated item should be a list that contains at most the list of parametrized values in random order.
Some invariants are:
def 'list generator'() {
expect:
xs.size() <= 3
xs.findAll({x -> x == 1}).size() <= 1
xs.findAll({x -> x == 2}).size() <= 1
xs.findAll({x -> x == 3}).size() <= 1
where:
xs << listGen([1, 2, 3])
}
I'm about to write own Generator implementation but there is chance I overthink something and it's possible to compose such generator with already existing spock-genesis units.
Try this
List listGen(List list) {
list.subsequences()
.collect { it.permutations() }
.inject([[]]) { result, subseq -> result + subseq }
}
The result of listGen([1, 2, 3]) will be:
[[], [1], [1, 2, 3], [3, 2, 1], [2, 1, 3], [3, 1, 2], [1, 3, 2], [2, 3, 1], [2], [3, 2], [2, 3], [2, 1], [1, 2], [3], [1, 3], [3, 1]]
Your test passes with this implementation.
UPD:
As per the OP clarifications below in the comments, they expect the permutations to be random, so here is the line of code that will do that using spock-genesis any:
where:
xs << Gen.any(listGen([1, 2, 3])).take(42).collect() // I assume 42 here should be random as well then
I'm not quite sure what you want, is just a list of every possible permutation of the list [1,2,3]? If so then this should be enough.
[1, 2, 3].permutations()
With #Dmitry Khamitov help I've come up with spock-genesis generator
package spock.genesis.generators.composites
import groovy.transform.CompileStatic
import spock.genesis.generators.Generator
import spock.genesis.generators.UnmodifiableIterator
import spock.genesis.generators.values.RandomElementGenerator
/** A lazy infinite {#code Generator} that returns a random subset of elements from a source Collection
* #warning O(n!) time complexity. Starts being too expensive with lists 10+ elements
* #param < E > the generated type
*/
#CompileStatic
class ListSubsetGenerator<E> extends Generator<List<E>> {
final RandomElementGenerator<List<E>> valueSource
ListSubsetGenerator(Collection<E> source) {
this.valueSource = new RandomElementGenerator<>(getListsSource(source))
}
private List<List<E>> getListsSource(Collection<E> source) {
source.toList().subsequences()
.collect { it.permutations() }
.inject([[]]) { result, subseq ->
result.addAll(subseq)
result
} as List<List<E>>
}
#Override
UnmodifiableIterator<List<E>> iterator() {
new UnmodifiableIterator<List<E>>() {
private final Iterator<List<E>> source = valueSource.iterator()
#Override
boolean hasNext() {
source.hasNext()
}
#Override
List<E> next() {
source.next()
}
}
}
#Override
Generator<List<E>> seed(Long seed) {
super.seed(seed)
valueSource.seed(seed)
this
}
}
Here are some tests:
class ListSubsetGeneratorTest extends Specification {
#Iterations(100)
def 'test invariants'() {
expect:
xs.size() <= 3
xs.findAll({x -> x == 1}).size() <= 1
xs.findAll({x -> x == 2}).size() <= 1
xs.findAll({x -> x == 3}).size() <= 1
xs.every { [1, 2, 3].contains(it) }
where:
xs << new ListSubsetGenerator([1, 2, 3])
}
def 'setting seed produces the same sequences for different generators'() {
given:
def elements = ['a', 'b', 'c', 'd']
def xs = new ListSubsetGenerator(elements).seed(seed).take(100).realized
def ys = new ListSubsetGenerator(elements).seed(seed).take(100).realized
expect:
xs == ys
where:
seed << [Long.MIN_VALUE, 100, Long.MAX_VALUE]
}
}

Split list into some part in dart [duplicate]

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;
}

Remove duplicates pairwise in kotlin

I have multiple lists of integers.
val firstList: ArrayList<Int> = arrayListOf(1, 1, 1, 2, 3, 4)
val secondList: ArrayList<Int> = arrayListOf(1, 4, 5, 6, 6, 6, 7, 8)
val thirdList: ArrayList<Int> = arrayListOf(1, 6, 9)
...
I need to remove duplicates only among the other lists, and not within a list itself.
The result should be:
[1,1,1,2,3,4,5,6,6,6,7,8,9]
What is the best approach to do so in Kotlin?
We can filter the additional lists with a .filter and .contains, then add the result to the firstList. Not sure if this is the most efficient way, but it should work.
val firstList: ArrayList<Int> = arrayListOf(1, 1, 1, 2, 3, 4)
val secondList: ArrayList<Int> = arrayListOf(1, 4, 5, 6, 6, 6, 7, 8)
val thirdList: ArrayList<Int> = arrayListOf(1, 6, 9)
firstList += secondList.filterNot { firstList.contains(it) }
firstList += thirdList.filterNot { firstList.contains(it) }
firstList.sort() //not sure if you want the firstList to be sorted or not
You could also make it an extension function, so then it is easier to call.
fun <T> MutableCollection<T>.addOtherNoDuplicates(vararg others: Iterable<T>){
others.forEach {other ->
this += other.filterNot { this.contains(it) }
}
}
Then the call would just be:
firstList.addOtherNoDuplicates(secondList, thirdList) //add as many as you want
firstList.sort()
If you only want to use it for ArrayList, then you can replace the MutableCollection<T> with ArrayList<T> and use the sort() directly in the function itself.
If these are very long lists, you can use a MutableSet on the side to efficiently avoid adding the unwanted values.
val input = listOf(firstList, secondList, thirdList)
val allValues = mutableSetOf<Int>()
val result = mutableListOf<Int>()
for (list in input) {
val newValues = list.filterNot { it in allValues }
result.addAll(newValues)
allValues.addAll(newValues)
}
println(result)

Is there a Dart utility for converting a flat list into a nested one?

It's a common problem here, but I couldn't find any simplified methods for Dart in particular - how can I convert a list like this
[1, 2, 3, 4, 5, 6]
into a list like this
[[1, 2], [3,4], [5,6]]
assuming there are no extra elements after this?
Dart Quiver package is a set of utility libraries for Dart that makes using many Dart libraries easier and more convenient or adds additional functionality (https://github.com/google/quiver-dart).
You can use the partition function from quiver iterables library as follows.
import 'package:quiver/iterables.dart';
main() {
var list = [1, 2, 3, 4, 5, 6];
# Use partition function to segment lists into chunks of size 2
var newList = partition<int>(list, 2);
print (newList);
}
Result [[1, 2], [3,4], [5,6]]
var list = [1, 2, 3, 4];
List<int> temp = [];
List<List<int>> newList = list.fold<List<List<int>>>([], (newList, i) {
if (temp.length < 2) {
temp.add(i);
}
if (temp.length >= 2) {
List<int> newValue = new List<int>.from(temp);
newList.add(newValue);
temp.clear();
}
return newList;
});
print(newList);

How do I split or chunk a list into equal parts, with Dart?

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;
}