Liệt kê hoặc lập bản đồ thông qua danh sách có chỉ mục và giá trị trong Dart


95

Trong phi tiêu có bất kỳ tương đương với thông thường:

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

Có vẻ như đây là cách dễ nhất nhưng vẫn có vẻ lạ khi chức năng này không tồn tại.

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

Biên tập:

Nhờ @ hemanth-raj, tôi đã có thể tìm thấy giải pháp mà tôi đang tìm kiếm. Tôi sẽ đặt điều này ở đây cho bất kỳ ai cần làm điều gì đó tương tự.

List<Widget> _buildWidgets(List<Object> list) {
    return list
        .asMap()
        .map((index, value) =>
            MapEntry(index, _buildWidget(index, value)))
        .values
        .toList();
}

Ngoài ra, bạn có thể tạo một hàm máy phát đồng bộ để trả về một

Iterable<MapEntry<int, T>> enumerate<T>(Iterable<T> items) sync* {
  int index = 0;
  for (T item in items) {
    yield MapEntry(index, item);
    index = index + 1;
  }
}

//and use it like this.
var list = enumerate([0,1,3]).map((entry) => Text("index: ${entry.key}, value: ${entry.value}"));

Map#forEach? nó là những gì bạn muốn?
pskink

Nó đang liệt kê thông qua một danh sách không phải một bản đồ
David Rees

Bản đồ # forEach đang liệt kê thông qua một List? Ý anh là gì? tài liệu nói: "Áp dụng f cho từng cặp khóa / giá trị của bản đồ. Gọi f không được thêm hoặc xóa khóa khỏi bản đồ."
pskink 27/02/19

Tôi cũng không hiểu ý bạn là gì với "liệt kê hoặc lập bản đồ thông qua danh sách có chỉ mục và giá trị"
Günter Zöchbauer 27/02/19

Câu trả lời:


146

Có một asMapphương pháp chuyển đổi danh sách thành một bản đồ trong đó các khóa là chỉ mục và các giá trị là phần tử tại chỉ mục. Vui lòng xem các tài liệu ở đây .

Thí dụ:

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

Hi vọng điêu nay co ich!


34

Không có chức năng cài sẵn nào để lấy chỉ số lặp.

Nếu giống như tôi, bạn không thích ý tưởng xây dựng một Map(cấu trúc dữ liệu) chỉ cho một chỉ mục đơn giản, những gì bạn có thể muốn là một map(hàm) cung cấp cho bạn chỉ mục. Hãy gọi nó mapIndexed(như trong Kotlin):

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

Việc thực hiện mapIndexedrất đơn giản:

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

1
Đây là một câu trả lời tốt nhưng có lẽ sẽ được viết tốt như một máy phát điện đồng bộ
David Rees

2
@DavidRees thx cho đề xuất! Tôi cũng đổi tên thành chức năngmapIndexed
Vivien

Thật tiếc là phi tiêu không có một cách dễ dàng tích hợp để làm điều này. Ngay cả con trăn 30 tuổi cũng có thể làm điều này một cách dễ dàng!
osamu

Nó chỉ ra rằng .asMap().forEach()về cơ bản giống như thế này - hãy xem câu trả lời của tôi.
Timmmm

1
@Timmmm Tôi nghĩ asMap()sẽ tốn thêm vòng lặp để lấy dữ liệu, do đó nó sẽ không hiệu quả như mapIndexed()trên.
anticafe

17

Dựa trên câu trả lời của @Hemanth Raj.

Để chuyển đổi nó trở lại bạn có thể làm

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

Hoặc nếu bạn cần chỉ mục cho một hàm ánh xạ, bạn có thể làm như sau:

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

14

Bắt đầu với Dart 2.7, bạn có thể sử dụng extensionđể mở rộng các chức năng Iterablethay vì phải viết các phương thức trợ giúp

extension ExtendedIterable<E> on Iterable<E> {
  /// Like Iterable<T>.map but callback have index as second argument
  Iterable<T> mapIndex<T>(T f(E e, int i)) {
    var i = 0;
    return this.map((e) => f(e, i++));
  }

  void forEachIndex(void f(E e, int i)) {
    var i = 0;
    this.forEach((e) => f(e, i++));
  }
}

Sử dụng:

final inputs = ['a', 'b', 'c', 'd', 'e', 'f'];
final results = inputs
  .mapIndex((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.forEachIndex((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

1
đây là câu trả lời tốt nhất, điều này thậm chí còn hoạt động với các mục Flutter nơi bạn có thể trả về các Widget không chỉ là Chuỗi.
STEEL

7

Gói khác của Lukas Renggli bao gồm nhiều công cụ hữu ích bao gồm 'được lập chỉ mục' thực hiện chính xác những gì bạn muốn. Từ các tài liệu:

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

(Bạn có thể bỏ qua đối số offset trừ khi bạn có nền tảng Smalltalk :-).


7

Ban đầu tôi nghĩ rằng ['one', 'two', 'three'].asMap().forEach((index, value) { ... });nó sẽ thực sự không hiệu quả vì có vẻ như nó đang chuyển đổi danh sách thành một bản đồ. Thực ra không phải vậy - tài liệu cho biết nó tạo ra một cái nhìn bất biến về danh sách. Tôi đã kiểm tra kỹ với dart2jsmã này:

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

Nó tạo ra rất nhiều mã! Nhưng ý chính là:

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

Vì vậy, nó là đủ thông minh để làm điều hiệu quả! Khá thông minh.

Tất nhiên, bạn cũng có thể chỉ sử dụng vòng lặp for bình thường:

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

4

Để thuận tiện, bạn có thể sử dụng phương pháp mở rộng này.

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

2

Sử dụng asMap để chuyển đổi Danh sách thành bản đồ trước. Chỉ mục của phần tử là khóa. Phần tử trở thành giá trị. Sử dụng các mục nhập để ánh xạ khóa và giá trị với bất kỳ thứ gì bạn muốn.

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

Đầu ra:

[0:a, 1:b, 2:c]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.