如何展开列表?

我怎样才能轻松地压平一个 List在 Dart?

例如:

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

30385 次浏览

I don't think there's a built-in method for that, but you can always reduce it to a single value:

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


var flatten = a.reduce([], (p, e) {
p.addAll(e);
return p;
});


print(flatten);

I wish addAll() would return the original list. Currently it returns nothing. If that were true, you could write a single liner: a.reduce([], (p, e) => p.addAll(e)).

Alternatively, you can just loop through the list and add:

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

The easiest way I know of is to use Iterable.expand() with an identity function. expand() takes each element of an Iterable, performs a function on it that returns an iterable (the "expand" part), and then concatenates the results. In other languages it may be known as flatMap.

So by using an identity function, expand will just concatenate the items. If you really want a List, then use toList().

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

the solution with expand method fits good to satisfy this case :

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

But not for theses ones

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

To satisfy theses test cases, you need something more recursive like the following function :

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

Best regards,

Sébastien

You can do this efficiently with a generator:

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


With Dart 2.3 or later, you instead can use collection-for and the spread operator to easily flatten a list. I personally find it to be more readable than using Iterable.expand:

List<T> flatten<T>(Iterable<Iterable<T>> list) =>
[for (var sublist in list) ...sublist];


void main() {
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]
}

If there are nested lists that you need to recursively flatten, you could use:

List<T> flattenDeep<T>(Iterable<dynamic> list) => [
for (var element in list)
if (element is! Iterable) element else ...flattenDeep(element),
];


void main() {
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]
}

package:collection additionally now provides a flattened extension method that can flatten one level of nesting:

import 'package:collection/collection.dart';


void main() {
var a = [[1, 2, 3], ['a', 'b', 'c'], [true, false, true]];
var b = a.flattened.toList();
print(b); // Prints: [1, 2, 3, a, b, c, true, false, true]
}

You can try this recursive solution that allows single elements too and flatten the deeply nested lists.

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