如何删除 Dart List 中的重复项?

如何从列表中删除副本,而不会在集合中胡闹?是否有类似 list.different ()的东西?还是 list.special () ?

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

Set 看起来要快得多! ! 但是它丢失了项目的顺序:/

123795 次浏览
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]

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

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

屈服:

[abc, def]

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

使用 toSet,然后使用 toList

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

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

或者利差操作者:

var distinctIds = [...{...ids}];

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

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]

Set可以正常工作,但是它不能保持顺序:

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

如果希望继续排序,或者处理比基元类型更复杂的对象,请将已看到的 id 存储到 预备中,并过滤掉已经在集合中的 ID。

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


print(unique); // => ['a', 'b']

使用 Dart 2.3 + ,您可以使用扩展运算符完成以下操作:

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

这是否比 ids.toSet().toList()更具可读性,我将让读者来决定:)

对我来说,最好的做法之一就是对数组进行排序,然后删除重复数组。这个想法是从低级语言中窃取的。所以,首先按照自己的方式进行排序,然后去除彼此之间相等的重复值。

// 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]]

试试以下方法:

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


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

检查 这是 Dartpad 上的代码

这是另一种方式..。

final reducedList = [];


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


reducedList.add(list.last);


print(reducedList);

我没有发现任何提供的答案很有帮助。 我通常是这样做的:

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

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

相对于其他方法的好处:

  • 保留列表的原始顺序
  • 不仅适用于原语/散列类型,还适用于富对象
  • 不需要将整个列表复制到集合并返回到列表

更新09/12/21
还可以为列表声明一次扩展方法:

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

使用 Fast _ immutable _ Collection软件包:

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

或者

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

注意: 当 distinct()返回一个新列表时,removeDuplicates()通过返回一个 Iterable来返回 懒洋洋的。这意味着当您执行一些额外的处理时,它的效率要高得多。例如,假设您有一个包含100万个项目的列表,并且您希望删除重复项目并获得前五个项目:

// 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 _ Collection软件包的作者之一。

这就是我的解决办法

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

或者..。

我把这个添加到 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();
}

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

例如:

// 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)]

这种简单的方法很管用

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']

对我有用。

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

从对象列表中删除重复项:

class Stock {
String? documentID; //key
Make? make;
Model? model;
String? year;


Stock({
this.documentID,
this.make,
this.model,
this.year,
});
}

库存清单,从我们要删除重复的库存

List<Stock> stockList = [stock1, stock2, stock3];

删除副本

final ids = stockList.map((e) => e.documentID).toSet();
stockList.retainWhere((x) => ids.remove(x.documentID));

创建从 Array 中删除重复项并返回唯一元素的 Array 的方法。

class Utilities {
static List<String> uniqueArray(List<String> arr) {
List<String> newArr = [];
for (var obj in arr) {
if (newArr.contains(obj)) {
continue;
}
newArr.add(obj);
}
return newArr;
}
}