假设我有一个类似的列表:
var letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
我想要一个包含 2 个元素的列表:
var chunks = [['a', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h']];
用 Dart 做这件事的好方法是什么?
这是另一种方式:
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;
Quiver(版本 >= 0.18)partition()
作为其可迭代库的一部分提供(import 'package:quiver/iterables.dart')。该实现返回 lazily-computed Iterable
,使其非常高效。用于:
var letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
var pairs = partition(letters, 2);
返回pairs
的将Iterable<List>
如下所示:
[['a', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h']]
对 Seth 的回答稍作改进,使其适用于任何列表或块大小:
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))));
这是一种方法:
letters.fold([[]], (list, x) {
return list.last.length == 2 ? (list..add([x])) : (list..last.add(x));
});
这种方式适用于奇数长度列表:
var nums = [1, 2, 3, 4, 5];
var pairs = new List.generate(nums.length~/2, (i) => [nums[2 * i], nums[2 * i + 1]]);
如果列表长度不均匀,您可能想要抛出错误或提供填充值。
另一种解决方案;
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]]
我找到了一个简单的解决方案:
var subList = mylist.take(3); // take 3 items first
var subList = mylist.skip(2).take(3); // take [2..5] items
我建议创建一个可迭代的对,如果您真的需要它作为列表,请使用 .toList 。该解决方案也可以应用于任何可迭代对象,而不仅仅是列表。首先,一个仅适用于列表(长度均匀)的简单解决方案(如 Robert King 提供的解决方案):
new Iterable.generate(letters.length ~/ 2,
(i) => [letters[2*i], letters[2*i + 1]])
更通用的解决方案很复杂:
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))
似乎从流中创建对流实际上比从可迭代中创建对的迭代更容易。
另一种方式:
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;
}
}
}
测试:
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));
});
}
输出:
([])
([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])
您还可以使用以下方法提取列表的一部分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]
备注:
sublist
接受两个参数,start
(包括)和end
(不包括)。end
是可选的。如果您不指定结束,则默认为列表的结尾。sublist
从给定范围返回一个新列表。受@Alan 上面的回答和扩展 List的影响,F# chunkedBySize和windowed和average的等价物可能是:
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));
}
可以在这里看到实现
另一种解决方案,因为其中一些看起来比必要的复杂一些:
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;
}
}
这是使用索引 for 循环和泛型的旧式解决方案:
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;
}
使用示例
main() {
var letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
int chunkSize = 2;
List<List<String>> chunks = _generateChunks(letters, chunkSize);
print(chunks);
}
输出是:
[[a, b], [c, d], [e, f], [g, h]]
迟到了,但对任何需要的人来说:基于扩展的解决方案:
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;
}
}
}
在大小为 n 的相等块上拆分列表(最后一个块是余数)
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);
}
}
使用示例:
chunks([2, 3, 4, 5, 6, 7, 5, 20, 33], 4).forEach(print);
chunks(['a', 'b', 'c'], 2).forEach(print);
此函数从给定块大小的原始数组中返回子列表。
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;
}