36

假设我有一个类似的列表:

var letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];

我想要一个包含 2 个元素的列表:

var chunks = [['a', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h']];

用 Dart 做这件事的好方法是什么?

4

17 回答 17

34

这是另一种方式:

  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;

dartpad上运行

于 2014-03-08T19:43:23.890 回答
25

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']]
于 2014-03-09T19:01:28.667 回答
19

对 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));
}
于 2018-11-07T16:44:13.883 回答
13
  pairs(list) => list.isEmpty ? list : ([list.take(2)]..addAll(pairs(list.skip(2))));
于 2014-03-09T04:49:03.163 回答
5

这是一种方法:

letters.fold([[]], (list, x) {    
  return list.last.length == 2 ? (list..add([x])) : (list..last.add(x));
});
于 2014-03-08T19:34:03.167 回答
4

这种方式适用于奇数长度列表:

  var nums = [1, 2, 3, 4, 5];
  var pairs = new List.generate(nums.length~/2, (i) => [nums[2 * i], nums[2 * i + 1]]);

如果列表长度不均匀,您可能想要抛出错误或提供填充值。

于 2014-03-09T06:04:49.957 回答
4

另一种解决方案;

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]]
于 2018-07-08T22:39:14.393 回答
4

我找到了一个简单的解决方案:

var subList = mylist.take(3); // take 3 items first
var subList = mylist.skip(2).take(3); // take [2..5] items
于 2021-07-17T14:12:42.543 回答
3

我建议创建一个可迭代的对,如果您真的需要它作为列表,请使用 .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))

似乎从流中创建对流实际上比从可迭代中创建对的迭代更容易。

于 2014-03-09T15:26:29.577 回答
2

另一种方式:

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])
于 2021-01-28T09:20:53.250 回答
1

子列表

您还可以使用以下方法提取列表的一部分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从给定范围返回一个新列表。
于 2021-11-15T01:00:50.640 回答
0

受@Alan 上面的回答和扩展 List的影响,F# chunkedBySizewindowedaverage的等价物可能是:

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

可以在这里看到实现

于 2018-03-03T08:50:09.077 回答
0

另一种解决方案,因为其中一些看起来比必要的复杂一些:

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;
  }
}
于 2021-12-06T14:03:06.277 回答
0

这是使用索引 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]]
于 2021-04-14T12:40:41.957 回答
0

迟到了,但对任何需要的人来说:基于扩展的解决方案:

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;
    }
  }
}
于 2021-01-04T17:53:37.663 回答
0

在大小为 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);
于 2021-02-25T22:43:00.133 回答
-1

此函数从给定块大小的原始数组中返回子列表。

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;
      }
于 2021-07-08T05:25:56.003 回答