【问题标题】:Enumerate or map through a list with index and value in Dart在 Dart 中枚举或映射具有索引和值的列表
【发布时间】:2019-07-20 18:42:00
【问题描述】:

在 dart 中有任何等价于 common:

enumerate(List) -> Iterator((index, value) => f)
or 
List.enumerate()  -> Iterator((index, value) => f)
or 
List.map() -> Iterator((index, value) => f)

这似乎是最简单的方法,但这个功能不存在似乎仍然很奇怪。

Iterable<int>.generate(list.length).forEach( (index) => {
  newList.add(list[index], index)
});

【问题讨论】:

  • Map#forEach?是你想要的吗?
  • 它是通过列表而不是地图进行枚举
  • Map#forEach 正在通过List 进行枚举?你的意思是?文档说:“将 f 应用于映射的每个键/值对。调用 f 不得在映射中添加或删除键。”
  • 我也不明白您所说的“通过索引和值枚举或映射列表”是什么意思

标签: dart flutter


【解决方案1】:

使用 -> mapIndexed(index, Element) 函数

将每个元素及其索引映射到一个新值。

import 'package:collection/collection.dart';

并按如下方式使用地图索引

(List).mapIndexed<Widget>(
 (mapIndex, mapElement) => Positioned(
  left: mapIndex.toDouble() * 5,
  child: Card(
   color: Colors.blue,
    child: Image.network(
     '${mapElement.ImageURL}',
      height: 80,
      width: 80))))

请参考: https://pub.dev/documentation/collection/latest/collection/IterableExtension/mapIndexed.html

【讨论】:

    【解决方案2】:

    您可以使用collection 包中的mapIndexedforEachIndexed 扩展方法。请注意,与 javascript 的 array.map() 或 C# 的 IEnumerable.Select() 不同,索引是回调的第一个参数,而不是第二个参数:

    import 'package:collection/collection.dart';
    
    void main() {
      final inputs = ['a', 'b', 'c', 'd', 'e', 'f'];
      final indexes = inputs.mapIndexed((index, element) => index).toList();
      
      inputs.forEachIndexed((index, element) {
        print('index: $index, element: $element');
      });
    
      print(indexes);
    }
    

    Live Demo


    旧答案

    从 Dart 2.7 开始,您可以使用 extension methods 来扩展 Iterable 的功能,而不必编写辅助函数:

    extension ExtendedIterable<E> on Iterable<E> {
      /// Like Iterable<T>.map but the callback has index as second argument
      Iterable<T> mapIndexed<T>(T Function(E e, int i) f) {
        var i = 0;
        return map((e) => f(e, i++));
      }
    
      void forEachIndexed(void Function(E e, int i) f) {
        var i = 0;
        forEach((e) => f(e, i++));
      }
    }
    

    用法:

    final inputs = ['a', 'b', 'c', 'd', 'e', 'f'];
    final results = inputs
      .mapIndexed((e, i) => 'item: $e, index: $i')
      .toList()
      .join('\n');
    
    print(results);
    
    // item: a, index: 0
    // item: b, index: 1
    // item: c, index: 2
    // item: d, index: 3
    // item: e, index: 4
    // item: f, index: 5
    
    inputs.forEachIndexed((e, i) => print('item: $e, index: $i'));
    
    // item: a, index: 0
    // item: b, index: 1
    // item: c, index: 2
    // item: d, index: 3
    // item: e, index: 4
    // item: f, index: 5
    

    Live Demo

    【讨论】:

    • 这是最好的答案,这甚至适用于 Flutter 项目,您可以返回 Widgets 而不仅仅是 String。
    • @IWantAnswers ET 分别表示原始数组/输出数组中项目的类型,因为您可以映射到不同类型的数组。例如,如果您编写一个从Widget 列表中获取所有名称的函数,它将是widgetList.mapIndex(w =&gt; getName(w)),其中widgetListList&lt;Widget&gt;,结果是List&lt;String&gt;
    • @IWantAnswers 每次评估回调 (e) =&gt; f(e, i++) 后都会发生增量。如果i-1 开头,则必须是++i。例如增量发生在评估回调之前。
    • 这是最好的!
    • 仍然对这不是 dart 内置感到失望。
    【解决方案3】:

    您可以使用collections 包中的mapIndexed 扩展:

    import 'package:collection/collection.dart';
    
    void main() {
      final nums = [1, 2, 3];
      final strs = nums.mapIndexed((index, element) => index.toString() + '_' + element.toString()).toList();
    
      print(strs); //  [0_1, 1_2, 2_3]
    }
    

    【讨论】:

    【解决方案4】:

    使用 dart collection package 可以访问各种列表扩展

    一个是mapIndexed:

    Iterable<R> mapIndexed<R>(R Function(int, E) convert)
    

    list of all iterable extensions

    【讨论】:

    【解决方案5】:

    您可以使用Iterable.generate 工厂。以下代码将使用索引和值映射Iterable

    extension IterableMapIndex<T> on Iterable<T> {
      Iterable<E> mapIndexed<E>(E f(int index, T t)) {
        return Iterable.generate(this.length, (index)=>f(index, elementAt(index)));
      }
    }
    

    【讨论】:

      【解决方案6】:

      先使用asMap将List转为map。元素的索引是关键。元素成为价值。使用 entries 将键和值映射到您想要的任何内容。

      List rawList = ["a", "b", "c"];
      List<String> argList = rawList.asMap().entries.map((e) => '${e.key}:${e.value}').toList();
      print(argList);
      

      输出:

      [0:a, 1:b, 2:c]
      

      【讨论】:

      • 最佳答案!不需要扩展或任何乱七八糟的东西!
      【解决方案7】:

      为方便起见,您可以使用此扩展方法。

      extension CollectionUtil<T> on Iterable<T>  {
      
        Iterable<E> mapIndexed<E, T>(E Function(int index, T item) transform) sync* {
          var index = 0;
      
          for (final item in this) {
            yield transform(index, item as T);
            index++;
          }
        }
      }
      

      【讨论】:

        【解决方案8】:

        没有获取迭代索引的内置函数。

        如果您像我一样不喜欢为简单的索引构建Map(数据结构)的想法,那么您可能想要的是一个为您提供索引的map(函数)。我们称之为mapIndexed(就像在 Kotlin 中一样):

        children: mapIndexed(
          list,
          (index, item) => Text("event_$index")
        ).toList();
        

        mapIndexed的实现很简单:

        Iterable<E> mapIndexed<E, T>(
            Iterable<T> items, E Function(int index, T item) f) sync* {
          var index = 0;
        
          for (final item in items) {
            yield f(index, item);
            index = index + 1;
          }
        }
        

        【讨论】:

        • 这是一个很好的答案,但写成同步生成器可能会更好
        • @DavidRees 感谢您的建议!我还重命名了函数mapIndexed
        • 遗憾的是,dart 没有内置的简单方法来执行此操作。 30岁的蟒蛇也能轻松做到!
        • 原来.asMap().forEach()和这个基本一样——看我的回答。
        • @Timmmm 我认为asMap() 将花费额外的循环来检索数据,因此它不会像上面的mapIndexed() 那样高效。
        【解决方案9】:

        我最初认为['one', 'two', 'three'].asMap().forEach((index, value) { ... }); 会非常低效,因为它看起来像是将列表转换为地图。实际上不是 - 文档说它创建了列表的不可变 view。我仔细检查了这段代码的dart2js

        void main() {
          final foo = ['one', 'two', 'three'];
          foo.asMap().forEach((idx, val) {
            print('$idx: $val');
          });
        }
        

        它会生成 很多 代码!但要点是:

          main: function() {
            var foo = H.setRuntimeTypeInfo(["one", "two", "three"], ...);
            new H.ListMapView(foo, ...).forEach$1(0, new F.main_closure());
          },
        
          H.ListMapView.prototype = {
            forEach$1: function(_, f) {
              var t1, $length, t2, i;
              ...
              t1 = this._values;
              $length = t1.length;
              for (t2 = $length, i = 0; i < $length; ++i) {
                if (i >= t2)
                  return H.ioore(t1, i);
                f.call$2(i, t1[i]);
                t2 = t1.length;
                if ($length !== t2)
                  throw H.wrapException(P.ConcurrentModificationError$(t1));
              }
            },
            ...
          },
        
          F.main_closure.prototype = {
            call$2: function(idx, val) {
              ...
              H.printString("" + idx + ": " + H.S(val));
            },
            $signature: 1
          };
        

        所以它足够聪明,可以做有效的事情!很聪明。

        当然你也可以只使用普通的for循环:

        for (var index = 0; index < values.length; ++index) {
          final value = values[index];
        

        【讨论】:

        • 感谢您对此进行研究!我也有同样的疑惑,因为我懒得搜索,总是卡在正常的循环中。
        【解决方案10】:

        以@Hemanth Raj 的回答为基础。

        要将其转换回来,您可以这样做

        List<String> _sample = ['a', 'b', 'c'];
        _sample.asMap().values.toList(); 
        //returns ['a', 'b', 'c'];
        

        或者,如果您需要映射函数的索引,您可以这样做:

        _sample
        .asMap()
        .map((index, str) => MapEntry(index, str + index.toString()))
        .values
        .toList();
        // returns ['a0', 'b1', 'c2']
        

        【讨论】:

          【解决方案11】:

          Lukas Renggli 的more 包包括许多有用的工具,包括“索引”,它完全符合您的要求。来自文档:

          indexed(['a', 'b'], offset: 1)
            .map((each) => '${each.index}: ${each.value}')
            .join(', ');
          

          (除非您有 Smalltalk 背景,否则您可以忽略偏移量参数 :-)。

          【讨论】:

            【解决方案12】:

            有一个asMap 方法将列表转换为映射,其中键是索引,值是索引处的元素。请查看文档here

            例子:

            List _sample = ['a','b','c'];
            _sample.asMap().forEach((index, value) => f);
            

            希望这会有所帮助!

            【讨论】:

            • 对于一个 JS 开发者来说非常令人失望,这里需要“转换”来遍历一个带有索引的列表
            • map() 应该返回一个值,以便将一种类型的列表转换为新的列表类型。 forEach() 返回无效。你也可以只使用 forEach()。
            • @user2233706 asMapmap 不同。 map 遍历 Iterable 并将其映射到映射器函数的返回类型。 asMapIterable 转换为 Map&lt;int,IterableTYpe&gt;。 forEach 应用于asMap返回的 Map
            • 我感觉这并不能保证订购。是吗?
            猜你喜欢
            • 2020-10-15
            • 2010-10-28
            • 1970-01-01
            • 1970-01-01
            • 2015-06-27
            • 1970-01-01
            • 2020-04-03
            • 1970-01-01
            • 2017-10-21
            相关资源
            最近更新 更多