116

我如何从列表中删除重复项而不用乱搞一套?有没有类似 list.distinct() 的东西?还是 list.unique()?

void main() {
  print("Hello, World!");

  List<String> list = ['abc',"abc",'def'];
  list.forEach((f) => print("this is list $f"));

  Set<String> set = new Set<String>.from(list);
  print("this is #0 ${list[0]}");
  set.forEach((f) => print("set: $f"));

  List<String> l2= new List<String>.from(set);
  l2.forEach((f) => print("This is new $f"));
}
Hello, World!
this is list abc
this is list abc
this is list def
this is #0 abc
set: abc
set: def
This is new abc
This is new def

设置似乎更快!但它失去了项目的顺序:/

4

18 回答 18

347

使用toSet然后toList

  var ids = [1, 4, 4, 4, 5, 6, 6];
  var distinctIds = ids.toSet().toList();

结果:[1、4、5、6]

或使用扩展运算符:

var distinctIds = [...{...ids}];
于 2018-07-20T16:32:01.140 回答
106

我没有发现任何提供的答案很有帮助。以下是我通常会做的事情:

final ids = Set();
myList.retainWhere((x) => ids.add(x.id));

当然,您可以使用任何唯一标识您的对象的属性。它不必是一个id字段。

与其他方法相比的好处:

  • 保留列表的原始顺序
  • 适用于丰富的对象,而不仅仅是基元/可散列类型
  • 不必将整个列表复制到集合并返回到列表

21 年 9 月 12 日更新
您还可以为列表声明一次扩展方法:

extension Unique<E, Id> on List<E> {
  List<E> unique([Id Function(E element)? id, bool inplace = true]) {
    final ids = Set();
    var list = inplace ? this : List<E>.from(this);
    list.retainWhere((x) => ids.add(id != null ? id(x) : x as Id));
    return list;
  }
}

此扩展方法与我的原始答案相同。用法:

// Use a lambda to map an object to its unique identifier.
myRichObjectList.unique((x) => x.id);
// Don't use a lambda for primitive/hashable types.
hashableValueList.unique();
于 2020-08-06T05:28:46.300 回答
42

Set工作正常,但它不保留订单。这是另一种使用方式LinkedHashSet

import "dart:collection";

void main() {
  List<String> arr = ["a", "a", "b", "c", "b", "d"];
  List<String> result = LinkedHashSet<String>.from(arr).toList();
  print(result); // => ["a", "b", "c", "d"]
}

https://api.dart.dev/stable/2.4.0/dart-collection/LinkedHashSet/LinkedHashSet.from.html

于 2019-07-29T18:07:25.370 回答
26

尝试以下操作:

List<String> duplicates = ["a", "c", "a"];

duplicates = duplicates.toSet().toList();

在 Dartpad 上检查此代码。

于 2020-05-26T19:28:00.127 回答
18

如果您想继续排序或处理比原始类型更复杂的对象,请将看到的 id 存储到Set并过滤掉那些已经在集合中的对象。

final list = ['a', 'a', 'b'];
final seen = Set<String>();
final unique = list.where((str) => seen.add(str)).toList();

print(unique); // => ['a', 'b']
于 2019-09-30T11:36:39.980 回答
14

我将此添加到atreeon 的答案中。对于任何想要使用它的人Object

class MyObject{
  int id;

  MyObject(this.id);


  @override
  bool operator ==(Object other) {
    return other != null && other is MyObject && hashCode == other.hashCode;
  }


  @override
  int get hashCode => id;
}

main(){
   List<MyObject> list = [MyObject(1),MyObject(2),MyObject(1)];

   // The new list will be [MyObject(1),MyObject(2)]
   List<MyObject> newList = list.toSet().toList();
}
于 2021-02-10T13:03:52.273 回答
11

使用 Dart 2.3+,您可以使用扩展运算符来执行此操作:

final ids = [1, 4, 4, 4, 5, 6, 6];
final distinctIds = [...{...ids}];

ids.toSet().toList()这是否比我让读者决定的可读性或多或少:)

于 2019-10-03T13:09:35.217 回答
7

对于不同的列表,objects您可以使用Equatable包。

例子:

// ignore: must_be_immutable
class User extends Equatable {
  int id;
  String name;

  User({this.id, this.name});

  @override
  List<Object> get props => [id];
}

List<User> items = [
  User(
    id: 1,
    name: "Omid",
  ),
  User(
    id: 2,
    name: "Raha",
  ),
  User(
    id: 1,
    name: "Omid",
  ),
  User(
    id: 2,
    name: "Raha",
  ),
];

print(items.toSet().toList());

输出:

[User(1), User(2)]
于 2021-04-29T17:56:54.033 回答
5

//这个简单的方法很好用

List<String> myArray = [];
myArray = ['x', 'w', 'x', 'y', 'o', 'x', 'y', 'y', 'r', 'a'];

myArray = myArray.toSet().toList();

print(myArray);

// 结果 => myArray =['x','w','y','o','r','a']

于 2021-11-28T19:56:29.890 回答
5

这是一个可行的解决方案:

var sampleList = ['1', '2', '3', '3', '4', '4'];
//print('original: $sampleList');
sampleList = Set.of(sampleList).toList();
//print('processed: $sampleList');

输出:

original: [1, 2, 3, 3, 4, 4]
processed: [1, 2, 3, 4]
于 2019-07-15T09:55:09.497 回答
4
void uniqifyList(List<Dynamic> list) {
  for (int i = 0; i < list.length; i++) {
    Dynamic o = list[i];
    int index;
    // Remove duplicates
    do {
      index = list.indexOf(o, i+1);
      if (index != -1) {
        list.removeRange(index, 1);
      }
    } while (index != -1);
  }
}

void main() {
  List<String> list = ['abc', "abc", 'def'];
  print('$list');
  uniqifyList(list);
  print('$list');
}

给出输出:

[abc, abc, def]
[abc, def]
于 2012-08-19T23:22:50.863 回答
4

使用fast_immutable_collections包:

[1, 2, 3, 2].distinct();

或者

[1, 2, 3, 2].removeDuplicates().toList();

注意:虽然distinct()返回一个新列表,removeDuplicates()但它通过返回一个Iterable. 这意味着当您进行一些额外的处理时,效率会更高。例如,假设您有一个包含一百万个项目的列表,并且您想要删除重复项并获取前五个:

// This will process five items:
List<String> newList = list.removeDuplicates().take(5).toList();

// This will process a million items:
List<String> newList = list.distinct().sublist(0, 5);

// This will also process a million items:
List<String> newList = [...{...list}].sublist(0, 5);

这两种方法也接受一个by参数。例如:

// Returns ["a", "yk", "xyz"]
["a", "yk", "xyz", "b", "xm"].removeDuplicates(by: (item) => item.length);

如果你不想在你的项目中包含一个包但需要惰性代码,这里是一个简化的removeDuplicates()

Iterable<T> removeDuplicates<T>(Iterable<T> iterable) sync* {
  Set<T> items = {};
  for (T item in iterable) {
    if (!items.contains(item)) yield item;
    items.add(item);
  }
}

注意:我是fast_immutable_collections包的作者之一。

于 2020-12-16T14:32:43.597 回答
1

对我来说,最佳实践之一是对数组进行排序,然后对其进行重复数据删除。这个想法是从低级语言中偷来的。因此,首先按您自己的方式进行排序,然后对彼此之后的相等值进行重复数据删除。

// Easy example
void dedup<T>(List<T> list, {removeLast: true}) {
  int shift = removeLast ? 1 : 0;
  T compareItem;
  for (int i = list.length - 1; i >= 0; i--) {
    if (compareItem == (compareItem = list[i])) {
      list.removeAt(i + shift);
    }
  }
}

// Harder example
void dedupBy<T, I>(List<T> list, I Function(T) compare, {removeLast: true}) {
  int shift = removeLast ? 1 : 0;
  I compareItem;
  for (int i = list.length - 1; i >= 0; i--) {
    if (compareItem == (compareItem = compare(list[i]))) {
      list.removeAt(i + shift);
    }
  }
}


void main() {
  List<List<int>> list = [[1], [1], [2, 1], [2, 2]];
  print('$list');
  dedupBy(list, (innerList) => innerList[0]);
  print('$list');

  print('\n removeLast: false');

  List<List<int>> list2 = [[1], [1], [2, 1], [2, 2]];
  print('$list2');
  dedupBy(list2, (innerList) => innerList[0], removeLast: false);
  print('$list2');
}

输出:

[[1], [1], [2, 1], [2, 2]]
[[1], [2, 1]]

removeLast: false
[[1], [1], [2, 1], [2, 2]]
[[1], [2, 2]]
于 2019-12-17T05:15:19.183 回答
1

这是另一种方式...

final reducedList = [];

list.reduce((value, element) {
    if (value != element) 
        reducedList.add(value);
    return element;
});

reducedList.add(list.last);

print(reducedList);
于 2020-07-27T03:08:09.607 回答
0
List<Model> bigList = [];
List<ModelNew> newList = [];  

for (var element in bigList) {
      var list = newList.where((i) => i.type == element.type).toList();
      if(list.isEmpty){
       newList.add(element);
      }
    }
于 2022-03-05T18:08:36.157 回答
0

这个对我有用。

var list = [
 {"id": 1, "name": "Joshua"},
 {"id": 2, "name": "Joshua"},
 {"id": 3, "name": "Shinta"},
 {"id": 4, "name": "Shinta"},
 {"id": 5, "name": "Zaidan"}
];
list.removeWhere((element) => element.name == element.name.codeUnitAt(1));
list.sort((a, b) => a.name.compareTo(b.name));

输出:

[{"id": 1, "name": "Joshua"}, 
{"id": 3, "name": "Shinta"}, 
{"id": 5, "name": "Zaidan"}]
于 2021-12-09T01:03:59.510 回答
-1

这是我的解决方案

    List<T> removeDuplicates<T>(List<T> list, IsEqual isEqual) {
      List<T> output = [];
      for(var i = 0; i < list.length; i++) {
        bool found = false;
        for(var j = 0; j < output.length; j++) {
          if (isEqual(list[i], output[j])) {
            found = true;
          }
        }
        if (found) {
          output.add(list[i]);
        }
      }

      return output;
    }

像这样使用它:

  var theList = removeDuplicates(myOriginalList, (item1, item2) => item1.documentID == item2.documentID);

或者...

  var theList = removeDuplicates(myOriginalList, (item1, item2) => item1.equals(item2));

或者...

于 2020-12-21T23:27:49.740 回答
-9

我有一个名为Reactive-Dart的库,其中包含许多用于终止和非终止序列的可组合运算符。对于您的场景,它看起来像这样:

final newList = [];
Observable
   .fromList(['abc', 'abc', 'def'])
   .distinct()
   .observe((next) => newList.add(next), () => print(newList));

产量:

[abc, def]

我应该补充一点,还有其他具有类似功能的库。在 GitHub 上查看一下,我相信你会找到合适的。

于 2012-08-20T00:53:11.620 回答