在 Dart 中枚举或映射具有索引和值的列表
Posted
技术标签:
【中文标题】在 Dart 中枚举或映射具有索引和值的列表【英文标题】:Enumerate or map through a list with index and value in 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 不得在映射中添加或删除键。”
我也不明白您所说的“通过索引和值枚举或映射列表”是什么意思
@GünterZöchbauer github.com/dart-lang/sdk/issues/5245 和 github.com/dart-lang/sdk/issues/32467
【参考方案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
包中的mapIndexed
或forEachIndexed
扩展方法。请注意,与 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。 @IWantAnswersE
和T
分别表示原始数组/输出数组中项目的类型,因为您可以映射到不同类型的数组。例如,如果您编写一个从Widget
列表中获取所有名称的函数,它将是widgetList.mapIndex(w => getName(w))
,其中widgetList
是List<Widget>
,结果是List<String>
@IWantAnswers 每次评估回调 (e) => 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]
【讨论】:
重要的是,集合包在 内部 Flutter SDK 中提供,因此现在可以“开箱即用” (api.flutter.dev/flutter/package-collection_collection/…)。 虽然没有扩展功能。 是的:api.flutter.dev/flutter/package-collection_collection/… @Maks 他们的意思是你不能在不安装collection
包的情况下导入扩展方法。它不在sdk中。见this。
@Maks 感谢您的纠正。我以前从来不知道。现在这一切都说得通了,为什么 dartpad 有收集包和其他一些开箱即用的包。这也是文档中简短的mentioned。【参考方案4】:
使用 dart collection package 可以访问各种列表扩展
一个是mapIndexed
:
Iterable<R> mapIndexed<R>(R Function(int, E) convert)
list of all iterable extensions
【讨论】:
重要的是,集合包是在内部在 Flutter SDK 中提供的,因此现在可以“开箱即用”(api.flutter.dev/flutter/package-collection_collection/…)。【参考方案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()。 @user2233706asMap
与 map
不同。 map
遍历 Iterable
并将其映射到映射器函数的返回类型。 asMap
将 Iterable
转换为 Map<int,IterableTYpe>
。 forEach 应用于asMap
返回的 Map
我感觉这并不能保证订购。是吗?以上是关于在 Dart 中枚举或映射具有索引和值的列表的主要内容,如果未能解决你的问题,请参考以下文章
如何使用变量创建具有索引和值的 Javascript 对象?