【问题标题】:How to flatten a List?如何展平列表?
【发布时间】:2021-07-23 12:31:36
【问题描述】:

如何在 Dart 中轻松地展平 List

例如:

var a = [[1, 2, 3], ['a', 'b', 'c'], [true, false, true]];
var b = [1, 2, 3, 'a', 'b', 'c', true, false, true];

如何将a 转换为b,即转换为包含所有这些值的单个List

【问题讨论】:

    标签: dart


    【解决方案1】:

    我知道的最简单的方法是将Iterable.expand() 与标识函数一起使用。 expand() 获取 Iterable 的每个元素,对其执行一个函数,返回一个 iterable(“扩展”部分),然后连接结果。在其他语言中,它可能被称为 flatMap。

    因此,通过使用标识函数,expand 只会连接项目。如果你真的想要一个列表,那么使用toList()

    var a = [[1, 2, 3], ['a', 'b', 'c'], [true, false, true]];
    var flat = a.expand((i) => i).toList();
    

    【讨论】:

    • 不错!我认为应该有人列出 Dart 中常见的伟大技巧。
    • @shailentuli 添加的很棒的食谱。
    • 为了处理更多层次的嵌套,我这样做: Iterable flatten(Iterable iterable) => iterable.expand((e) => e is List ? flatten(e) : [e]);
    • 仅供参考,Quiver.iterables 包含一个 concat() 便利方法,它像这样使用 expand
    【解决方案2】:

    我不认为有一个内置的方法,但你总是可以将它减少到一个值:

    var a = [[1, 2, 3], ['a', 'b', 'c'], [true, false, true]];
    
    var flatten = a.reduce([], (p, e) {
      p.addAll(e);
      return p;
    });
    
    print(flatten);
    

    我希望addAll() 会返回原始列表。目前它什么也不返回。如果这是真的,你可以写一个衬里:a.reduce([], (p, e) => p.addAll(e))

    或者,您可以遍历列表并添加:

    var flatten = [];
    a.forEach((e) => flatten.addAll(e));
    

    【讨论】:

    • 您可以使用级联编写单行:a.reduce([], (p, e) => p..addAll(e))
    • @kai-sellgren ,我检查了 Dart API 页面,但那里的语法和示例(api.dart.dev/dev/2.8.0-dev.6.0/dart-core/Iterable/reduce.html)与您在此处使用的不同(初始空列表 []更加具体)。这不是“折叠”的情况吗?
    • Reduce 方法不接受初始值设定项,改用fold
    【解决方案3】:

    使用 Dart 2.3 或更高版本,您可以改为使用 collection-for 和展开运算符来轻松展平列表。我个人觉得它比使用Iterable.expand更具可读性:

    List<T> flatten<T>(Iterable<Iterable<T>> list) =>
        [for (var sublist in list) ...sublist];
    
    var a = [[1, 2, 3], ['a', 'b', 'c'], [true, false, true]];
    var b = flatten(a);
    print(b); // Prints: [1, 2, 3, a, b, c, true, false, true] 
    

    如果您需要递归展平嵌套列表,您可以使用:

    List<T> flattenDeep<T>(Iterable<dynamic> list) => [
          for (var element in list)
            if (element is! Iterable) element else ...flattenDeep(element),
        ];
    
    var a = [[1, [[2], 3]], [[['a']], 'b', 'c'], [true, false, [true]]];
    var b = flattenDeep(a);
    print(b) // Prints: [1, 2, 3, a, b, c, true, false, true] 
    

    【讨论】:

      【解决方案4】:

      您可以使用生成器有效地做到这一点:

      Iterable<T> flatten<T>(Iterable<Iterable<T>> items) sync* {
        for (var i in items) {
          yield* i;
        }
      }
      
      Iterable<X> flatMap<T,X>(Iterable<Iterable<T>> items, X Function(T) f) =>
        flatten(items).map(f);
      
      

      【讨论】:

        【解决方案5】:

        expand 方法的解决方案非常适合这种情况:

        expect(ListTools.getFlatList([[1],["hello",2],["test"]]),orderedEquals([1,"hello",2,"test"]));
        

        但不适用于论文

        expect(ListTools.getFlatList([[1],["hello",2,["foo",5]],["test"]]),orderedEquals([1,"hello",2,"foo",5,"test"]));
        expect(ListTools.getFlatList([1,["hello",2],"test"]),orderedEquals([1,"hello",2,"test"]));
        

        为了满足这些测试用例,你需要一些更递归的东西,比如下面的函数:

        List getFlatList(List list) {
          List internalList = new List();
          list.forEach((e) {
            if (e is List) {
              internalList.addAll(getFlatList(e));
            } else {
              internalList.add(e);
            }
          });
          return internalList;
        }
        

        最好的问候,

        塞巴斯蒂安

        【讨论】:

          【解决方案6】:

          您可以尝试这种递归解决方案,它也允许单个元素并展平深度嵌套的列表。

          List flatten(List arr) => 
            arr.fold([], (value, element) => 
             [
               ...value, 
               ...(element is List ? flatten(element) : [element])
             ]);
          
          

          【讨论】:

            猜你喜欢
            • 2013-04-24
            • 1970-01-01
            • 2016-08-19
            • 2019-12-12
            • 2014-04-22
            • 2012-03-19
            • 2016-09-07
            • 1970-01-01
            相关资源
            最近更新 更多