How can I delete duplicates in a Dart List? list.distinct()?

ListDart

List Problem Overview


How do I delete duplicates from a list without fooling around with a set? Is there something like list.distinct()? or 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


Set seems to be way faster!! But it loses the order of the items :/

List Solutions


Solution 1 - List

Use toSet and then toList

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

Result: [1, 4, 5, 6]

Or with spread operators:

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

Solution 2 - List

I didn't find any of the provided answers very helpful. Here is what I generally do:

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

Of course you can use any attribute which uniquely identifies your objects. It doesn't have to be an id field.

Benefits over other approaches:

  • Preserves the original order of the list
  • Works for rich objects not just primitives/hashable types
  • Doesn't have to copy the entire list to a set and back to a list

Update 09/12/21
You can also declare an extension method once for lists:

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

This extension method does the same as my original answer. Usage:

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

Solution 3 - List

Set works okay, but it doesn't preserve the order. Here's another way using 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

Solution 4 - List

Try the following:

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

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

Check this code on Dartpad.

Solution 5 - List

If you want to keep ordering or are dealing with more complex objects than primitive types, store seen ids to the Set and filter away those ones that are already in the set.

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

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

Solution 6 - List

I am adding this to atreeon's answer. For anyone that want use this with 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();
}

Solution 7 - List

Using Dart 2.3+, you can use the spread operators to do this:

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

Whether this is more or less readable than ids.toSet().toList() I'll let the reader decide :)

Solution 8 - List

For distinct list of objects you can use Equatable package.

Example:

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

Output:

[User(1), User(2)]

Solution 9 - List

//This easy way works fine

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

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

print(myArray);

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

Solution 10 - List

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

Gives output:

[abc, abc, def]
[abc, def]

Solution 11 - List

Here it is, a working solution:

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

Output:

original: [1, 2, 3, 3, 4, 4]
processed: [1, 2, 3, 4]

Solution 12 - List

Using the fast_immutable_collections package:

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

Or

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

Note: While distinct() returns a new list, removeDuplicates() does it lazily by returning an Iterable. This means it is much more efficient when you are doing some extra processing. For example, suppose you have a list with a million items, and you want to remove duplicates and get the first five:

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

Both methods also accept a by parameter. For example:

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

If you don't want to include a package into your project but needs the lazy code, here it is a simplified 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);
  }
}

Note: I am one of the authors of the fast_immutable_collections package.

Solution 13 - List

Remove duplicates from a list of objects:

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

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

List of stock, from where we want to remove duplicate stocks

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

Remove duplicates

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

Solution 14 - List

As for me, one of the best practices is sort the array, and then deduplicate it. The idea is stolen from low-level languages. So, first make the sort by your own, and then deduplicate equal values that are going after each other.

// 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');
}

Output:

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

removeLast: false
[[1], [1], [2, 1], [2, 2]]
[[1], [2, 2]]

Solution 15 - List

This is another way...

final reducedList = [];

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

reducedList.add(list.last);

print(reducedList);

Solution 16 - List

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

Solution 17 - List

This is my solution

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

Use it like this:

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

or...

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

or...

Solution 18 - List

It works for me.

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

Output:

[{"id": 1, "name": "Joshua"}, {"id": 3, "name": "Shinta"}, {"id": 5, "name": "Zaidan"}]

Solution 19 - List

I have a library called Reactive-Dart that contains many composable operators for terminating and non-terminating sequences. For your scenario it would look something like this:

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

Yielding:

[abc, def]

I should add that there are other libraries out there with similar features. Check around on GitHub and I'm sure you'll find something suitable.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGeroView Question on Stackoverflow
Solution 1 - ListatreeonView Answer on Stackoverflow
Solution 2 - ListBasic CoderView Answer on Stackoverflow
Solution 3 - ListSteffanView Answer on Stackoverflow
Solution 4 - ListAnandu YDView Answer on Stackoverflow
Solution 5 - ListNiko RuotsalainenView Answer on Stackoverflow
Solution 6 - Listmahdi shahbaziView Answer on Stackoverflow
Solution 7 - ListjtlimView Answer on Stackoverflow
Solution 8 - ListOmid RahaView Answer on Stackoverflow
Solution 9 - ListConfianceView Answer on Stackoverflow
Solution 10 - ListCutchView Answer on Stackoverflow
Solution 11 - ListBambinoUAView Answer on Stackoverflow
Solution 12 - ListMarcGView Answer on Stackoverflow
Solution 13 - ListBaigView Answer on Stackoverflow
Solution 14 - ListStanislav SaganView Answer on Stackoverflow
Solution 15 - ListYohanny QuirogaView Answer on Stackoverflow
Solution 16 - ListMohtady BehiryView Answer on Stackoverflow
Solution 17 - Listminkey ioView Answer on Stackoverflow
Solution 18 - ListIhda MukhoyarView Answer on Stackoverflow
Solution 19 - ListJohn EvansView Answer on Stackoverflow