【问题标题】:How do I split or chunk a list into equal parts, with Dart?如何使用 Dart 将列表拆分或分块?
【发布时间】:2014-04-12 00:19:36
【问题描述】:

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

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

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

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

使用 Dart 有什么好的方法?

【问题讨论】:

    标签: list flutter dart split chunks


    【解决方案1】:

    这是另一种方式:

      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上运行它

    【讨论】:

    • 当列表不包含一对元素时,此答案不起作用。在调用 sublist 之前,你应该添加一个是否到达末尾的检查,否则你会得到类似RangeError (end): Invalid value: Not in range 14..15, inclusive: 16 的错误
    • chunks.add(letters.sublist(i, i+2 > letters.length ? letters.length : i + 2));
    • 使用 ListComprehension 怎么样? [for (int i = 0; i
    • @Pom12 我已经编辑了答案以支持不同大小的块并添加了一个 dartpad 链接。享受吧!
    【解决方案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;
      }
    }
    

    【讨论】:

      【解决方案3】:

      子列表

      您还可以使用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 从给定范围返回一个新列表。

      【讨论】:

      • 糟糕,我错过了 Seth Ladd 的回答已经使用了 sublist。不过,我将把它作为一个更普遍的例子。
      【解决方案4】:

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

      var subList = mylist.take(3); // take 3 items first
      var subList = mylist.skip(2).take(3); // take [2..5] items
      

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          【解决方案6】:

          这是使用索引 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]]
          

          【讨论】:

            【解决方案7】:

            在大小为 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);
            

            【讨论】:

              【解决方案8】:

              另一种方式:

              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])
              

              【讨论】:

                【解决方案9】:

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

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

                【讨论】:

                  【解决方案10】:

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

                  【讨论】:

                    【解决方案11】:

                    另一种解决方案;

                    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]]
                    

                    【讨论】:

                      【解决方案12】:

                      受上述@Alan 的回答和extending 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));
                      }
                      

                      实现可见here

                      【讨论】:

                        【解决方案13】:

                        Quiver (version >= 0.18) 提供 partition() 作为其可迭代库的一部分 (import 'package:quiver/iterables.dart')。该实现返回延迟计算的Iterable,使其非常高效。用作:

                        var letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
                        var pairs = partition(letters, 2);
                        

                        返回的pairs 将是一个Iterable&lt;List&gt;,如下所示:

                        [['a', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h']]
                        

                        【讨论】:

                          【解决方案14】:

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

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

                          【讨论】:

                          • 我喜欢 python 的一点是它的 zip 函数,它允许你去 pairs_iter = zip(it, it)
                          • dart 可能会使用 zip 功能。 columns = zip(rows) 或 rows = zip(columns) 相当常见。
                          • 一般情况下也是 zip(repeat(it, n)) 获得长度为 n 的块。
                          • 你可以在Quiver的iterables库中找到一个zip函数。实现是here
                          【解决方案15】:

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

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

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

                          【讨论】:

                          【解决方案16】:
                            pairs(list) => list.isEmpty ? list : ([list.take(2)]..addAll(pairs(list.skip(2))));
                          

                          【讨论】:

                          • 为了提高效率,我怀疑带有索引的无聊 for 循环是最好的,但尝试简洁也很有趣。
                          • 这个解决方案在 O(n^2) 中。我喜欢简洁的代码,但我不确定它是否值得性能提升。
                          【解决方案17】:

                          这是一种方法:

                          letters.fold([[]], (list, x) {    
                            return list.last.length == 2 ? (list..add([x])) : (list..last.add(x));
                          });
                          

                          【讨论】:

                          • 有没有更好的办法?