1468

How can I check if a list has any duplicates and return a new list without duplicates?

7
  • 1
  • 2
    Interestingly, none of the top answers here provides an answer to the actual question: create a new list with only items that are not duplicated in the original list. I read that as [1, 2, 3, 4, 5, 2, 4] -> [1, 3, 5], as 2 and 4 are duplicated.
    – 9769953
    Sep 10, 2022 at 10:46
  • @9769953 Given what you say, would it make sense to use Rev 11, but keep only the first sub-question (i.e. [1, 2, 3, 1] → [1, 2, 3]) that is answered by the top answers? The accepted answer hints at a possible way to accomplish the second sub-question (i.e. [1, 2, 3, 1] → [2, 3]). As it stands, the question and top answer are paradoxically not in exactly sync. Sep 12, 2022 at 5:07
  • @MateenUlhaq I prefer to keep the original question. Also, rev. 11 changes the question to fit the answers more, but not necessarily fit the original question. I guess then it depends how much of a forum/mailing list style you'd like SO to be, or how close to a tips'n'tricks website (with very pure questions and answers). I don't think either is achievable.
    – 9769953
    Sep 12, 2022 at 7:31
  • After going back and reading rev 1, I can't fathom how the question could be read as saying anything about whether [1, 2, 3, 4, 5, 2, 4] should transform to [1, 3, 5] or to [1, 2, 3, 4, 5], or if order matters, or anything else. In fact, despite the title "Python removing duplicates in lists", it doesn't seem like OP wanted to remove duplicates from within the same list at all. Rather, it looks like OP wanted to take two lists e.g. [1, 2, 3, 4] and [1, 3, 4], and remove from the first those which are present in the second, to get [2]. Jan 25, 2023 at 8:25

58 Answers 58

2262

The common approach to get a unique collection of items is to use a set. Sets are unordered collections of distinct objects. To create a set from any iterable, you can simply pass it to the built-in set() function. If you later need a real list again, you can similarly pass the set to the list() function.

The following example should cover whatever you are trying to do:

>>> t = [1, 2, 3, 1, 2, 3, 5, 6, 7, 8]
>>> list(set(t))
[1, 2, 3, 5, 6, 7, 8]
>>> s = [1, 2, 3]
>>> list(set(t) - set(s))
[8, 5, 6, 7]

As you can see from the example result, the original order is not maintained. As mentioned above, sets themselves are unordered collections, so the order is lost. When converting a set back to a list, an arbitrary order is created.

Maintaining order

If order is important to you, then you will have to use a different mechanism. A very common solution for this is to rely on OrderedDict to keep the order of keys during insertion:

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys(t))
[1, 2, 3, 5, 6, 7, 8]

Starting with Python 3.7, the built-in dictionary is guaranteed to maintain the insertion order as well, so you can also use that directly if you are on Python 3.7 or later (or CPython 3.6):

>>> list(dict.fromkeys(t))
[1, 2, 3, 5, 6, 7, 8]

Note that this may have some overhead of creating a dictionary first, and then creating a list from it. If you don’t actually need to preserve the order, you’re often better off using a set, especially because it gives you a lot more operations to work with. Check out this question for more details and alternative ways to preserve the order when removing duplicates.


Finally note that both the set as well as the OrderedDict/dict solutions require your items to be hashable. This usually means that they have to be immutable. If you have to deal with items that are not hashable (e.g. list objects), then you will have to use a slow approach in which you will basically have to compare every item with every other item in a nested loop.

9
  • add this to example, t = [3, 2, 1, 1, 2, 5, 6, 7, 8], shows the difference clearly! Oct 26, 2019 at 4:44
  • 2
    "...overhead of creating a dictionary first... If you don’t actually need to preserve the order, you’re better off using a set." — I profiled this because I was curious if it was actually true. My timings show that indeed the set is slightly faster: 1.12 µs per loop (set) vs 1.53 µs per loop (dict) over 1M loops with an absolute time difference of about 4s over 1M iterations. So if you're doing this in a tight inner loop you may care, otherwise probably not.
    – millerdev
    Dec 9, 2019 at 13:30
  • @millerdev I was going to say something like “overhead does not only mean timing” but then I checked and it appears that a keyed dictionary is actually smaller in memory than a set with the same elements. At least in current versions of Python. That’s really surprising – but yes, it’s a good point! Thanks!
    – poke
    Dec 9, 2019 at 15:05
  • 4
    This solves the issue with unhashable types (where t is a list of dicts): [dict(d) for d in set([frozenset(i.items()) for i in t])] Dec 11, 2019 at 7:52
  • 1
    @BigDreamz dict.fromkeys() creates a dictionary in linear time, and list() will create a list from it also in linear time.
    – poke
    Aug 25, 2020 at 6:16
485

In Python 2.7, the new way of removing duplicates from an iterable while keeping it in the original order is:

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

In Python 3.5, the OrderedDict has a C implementation. My timings show that this is now both the fastest and shortest of the various approaches for Python 3.5.

In Python 3.6, the regular dict became both ordered and compact. (This feature is holds for CPython and PyPy but may not present in other implementations). That gives us a new fastest way of deduping while retaining order:

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

In Python 3.7, the regular dict is guaranteed to both ordered across all implementations. So, the shortest and fastest solution is:

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']
7
216

It's a one-liner: list(set(source_list)) will do the trick.

A set is something that can't possibly have duplicates.

Update: an order-preserving approach is two lines:

from collections import OrderedDict
OrderedDict((x, True) for x in source_list).keys()

Here we use the fact that OrderedDict remembers the insertion order of keys, and does not change it when a value at a particular key is updated. We insert True as values, but we could insert anything, values are just not used. (set works a lot like a dict with ignored values, too.)

2
  • @AdrianKeister: This is true. There are objects that have reasonable equality semantics but are not hashable, e.g. lists. OTOH if we can't have a shortcut like a hastable, we end up with a quadratic algorithm of just comparing every element with all currently known unique elements. This can be totally OK for short inputs, especially with a lot of duplicates.
    – 9000
    Aug 22, 2019 at 15:40
  • 1
    Right, exactly. I think your answer would be higher quality if you took this very common use case into account. Aug 22, 2019 at 15:44
120
>>> t = [1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> t
[1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> s = []
>>> for i in t:
       if i not in s:
          s.append(i)
>>> s
[1, 2, 3, 5, 6, 7, 8]
1
  • 51
    Note that this method works in O(n^2) time and is thus very slow on large lists.
    – dotancohen
    Sep 3, 2013 at 14:02
107

If you don't care about the order, just do this:

def remove_duplicates(l):
    return list(set(l))

A set is guaranteed to not have duplicates.

0
51

To make a new list retaining the order of first elements of duplicates in L:

newlist = [ii for n,ii in enumerate(L) if ii not in L[:n]]

For example: if L = [1, 2, 2, 3, 4, 2, 4, 3, 5], then newlist will be [1, 2, 3, 4, 5]

This checks each new element has not appeared previously in the list before adding it. Also it does not need imports.

5
  • 5
    This has a time complexity of O(n ^ 2). The answers with set and OrderedDict may have lower amortized time complexity. Apr 13, 2017 at 4:09
  • I used in my code this solution and worked great but I think it is time consuming Apr 26, 2018 at 13:59
  • @blubberdiblub can you explain what more code efficient mechanism exists in set and OrderedDict that could make them less time consuming? (excluding the overhead of loading them) Jan 14, 2019 at 11:45
  • 2
    @iliasiliadis The usual implementations of set and dict use hashes or (some form of balanced) trees. You have to consider building the set or dict and searching in it (multiple times), but their amortized complexity usually is still lower than O(n ^ 2). "Amortized" in simple terms means on average (they can have worst cases with higher complexity than the average case). This is only relevant when you have a big number of items. Jan 14, 2019 at 13:16
  • Nice answer, it works if the elements are not hashable. However, if the elements are Numpy arrays, you may get surprises, because the in operator doesn't work as one might expect (at least as I was expecting).
    – Keta
    Jun 1, 2022 at 8:47
41

Super late answer:

If you don't care about the list order, you can use *arg expansion with set uniqueness to remove duplicates, i.e.:

l = [*{*l}]

Python3 Demo

1
  • 13
    Nice... the one problem is that it's so clever that you kind of have to add a comment to say what it does. Oct 17, 2021 at 18:39
39

There are also solutions using Pandas and Numpy. They both return numpy array so you have to use the function .tolist() if you want a list.

t=['a','a','b','b','b','c','c','c']
t2= ['c','c','b','b','b','a','a','a']

Pandas solution

Using Pandas function unique():

import pandas as pd
pd.unique(t).tolist()
>>>['a','b','c']
pd.unique(t2).tolist()
>>>['c','b','a']

Numpy solution

Using numpy function unique().

import numpy as np
np.unique(t).tolist()
>>>['a','b','c']
np.unique(t2).tolist()
>>>['a','b','c']

Note that numpy.unique() also sort the values. So the list t2 is returned sorted. If you want to have the order preserved use as in this answer:

_, idx = np.unique(t2, return_index=True)
t2[np.sort(idx)].tolist()
>>>['c','b','a']

The solution is not so elegant compared to the others, however, compared to pandas.unique(), numpy.unique() allows you also to check if nested arrays are unique along one selected axis.

5
  • This will convert the list to numpy array which is a mess and won't work for strings.
    – user227666
    Jul 3, 2014 at 12:48
  • 1
    @user227666 thanks for your review but that's not true it works even with string and you can add .tolist if you want to get a list...
    – G M
    Jul 3, 2014 at 16:45
  • 2
    I think this is kinda like trying to kill a bee with a sledgehammer. Works, sure! But, importing a library for just this purpose might be a little overkill, no? Oct 9, 2016 at 9:11
  • @DebosmitRay it could be useful if you work in Data Science where usually you work with numpy and many times you need to work with numpy array.
    – G M
    Oct 10, 2016 at 7:17
  • 1
    the best answer in 2020 @DebosmitRay i hope you change your mind and use numpy / pandas every time you can
    – Egos
    Feb 27, 2020 at 13:52
37

In this answer, there will be two sections: Two unique solutions, and a graph of speed for specific solutions.

Removing Duplicate Items

Most of these answers only remove duplicate items which are hashable, but this question doesn't imply it doesn't just need hashable items, meaning I'll offer some solutions which don't require hashable items.

collections.Counter is a powerful tool in the standard library which could be perfect for this. There's only one other solution which even has Counter in it. However, that solution is also limited to hashable keys.

To allow unhashable keys in Counter, I made a Container class, which will try to get the object's default hash function, but if it fails, it will try its identity function. It also defines an eq and a hash method. This should be enough to allow unhashable items in our solution. Unhashable objects will be treated as if they are hashable. However, this hash function uses identity for unhashable objects, meaning two equal objects that are both unhashable won't work. I suggest you override this, and changing it to use the hash of an equivalent mutable type (like using hash(tuple(my_list)) if my_list is a list).

I also made two solutions. Another solution which keeps the order of the items, using a subclass of both OrderedDict and Counter which is named 'OrderedCounter'. Now, here are the functions:

from collections import OrderedDict, Counter

class Container:
    def __init__(self, obj):
        self.obj = obj
    def __eq__(self, obj):
        return self.obj == obj
    def __hash__(self):
        try:
            return hash(self.obj)
        except:
            return id(self.obj)

class OrderedCounter(Counter, OrderedDict):
     'Counter that remembers the order elements are first encountered'

     def __repr__(self):
         return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

     def __reduce__(self):
         return self.__class__, (OrderedDict(self),)
    
def remd(sequence):
    cnt = Counter()
    for x in sequence:
        cnt[Container(x)] += 1
    return [item.obj for item in cnt]

def oremd(sequence):
    cnt = OrderedCounter()
    for x in sequence:
        cnt[Container(x)] += 1
    return [item.obj for item in cnt]

remd is non-ordered sorting, while oremd is ordered sorting. You can clearly tell which one is faster, but I'll explain anyways. The non-ordered sorting is slightly faster, since it doesn't store the order of the items.

Now, I also wanted to show the speed comparisons of each answer. So, I'll do that now.

Which Function is the Fastest?

For removing duplicates, I gathered 10 functions from a few answers. I calculated the speed of each function and put it into a graph using matplotlib.pyplot.

I divided this into three rounds of graphing. A hashable is any object which can be hashed, an unhashable is any object which cannot be hashed. An ordered sequence is a sequence which preserves order, an unordered sequence does not preserve order. Now, here are a few more terms:

Unordered Hashable was for any method which removed duplicates, which didn't necessarily have to keep the order. It didn't have to work for unhashables, but it could.

Ordered Hashable was for any method which kept the order of the items in the list, but it didn't have to work for unhashables, but it could.

Ordered Unhashable was any method which kept the order of the items in the list, and worked for unhashables.

On the y-axis is the amount of seconds it took.

On the x-axis is the number the function was applied to.

I generated sequences for unordered hashables and ordered hashables with the following comprehension: [list(range(x)) + list(range(x)) for x in range(0, 1000, 10)]

For ordered unhashables: [[list(range(y)) + list(range(y)) for y in range(x)] for x in range(0, 1000, 10)]

Note there is a step in the range because without it, this would've taken 10x as long. Also because in my personal opinion, I thought it might've looked a little easier to read.

Also note the keys on the legend are what I tried to guess as the most vital parts of the implementation of the function. As for what function does the worst or best? The graph speaks for itself.

With that settled, here are the graphs.

Unordered Hashables

Unordered Hashables (Zoomed in) Unordered Hashables Zoomed

Ordered Hashables

Ordered Hashables (Zoomed in) Ordered Hashables Zoomed

Ordered Unhashables

Ordered Unhashables (Zoomed in) Ordered Unhashables Zoomed

1
  • 1
    Hard to read. Better have a top list at the bottom with the results wrapped up. Thus, for unordered hashables: Do not use: #- ii for n,ii in enumerate(seq) if ii not in seq[:n] #- cnt = Counter(); cnt[Container(x)] += 1 #- cnt = OrderedCounter(); cnt[Container(x)) += 1 #- if i not in new for i in seq. Better use: #- list(set(seq)) #- dict.fromkeys(seq) #- added = set(); for in seq: if not val in added #- OrderedDict.fromkeys(seq) #- OrderedDict((x, True) for x in seq).keys() #- functools.reduce(lambda r, v: v in r[1] and r or ... or ..., ([], set[]))[0] Sep 13, 2021 at 23:12
31

A colleague have sent the accepted answer as part of his code to me for a codereview today. While I certainly admire the elegance of the answer in question, I am not happy with the performance. I have tried this solution (I use set to reduce lookup time)

def ordered_set(in_list):
    out_list = []
    added = set()
    for val in in_list:
        if not val in added:
            out_list.append(val)
            added.add(val)
    return out_list

To compare efficiency, I used a random sample of 100 integers - 62 were unique

from random import randint
x = [randint(0,100) for _ in xrange(100)]

In [131]: len(set(x))
Out[131]: 62

Here are the results of the measurements

In [129]: %timeit list(OrderedDict.fromkeys(x))
10000 loops, best of 3: 86.4 us per loop

In [130]: %timeit ordered_set(x)
100000 loops, best of 3: 15.1 us per loop

Well, what happens if set is removed from the solution?

def ordered_set(inlist):
    out_list = []
    for val in inlist:
        if not val in out_list:
            out_list.append(val)
    return out_list

The result is not as bad as with the OrderedDict, but still more than 3 times of the original solution

In [136]: %timeit ordered_set(x)
10000 loops, best of 3: 52.6 us per loop
3
  • Nice using set quick lookup to speed up the looped comparison. If order does not matter list(set(x)) is still 6x faster than this
    – Joop
    Sep 17, 2014 at 10:24
  • @Joop, that was my first question for my colleague - the order does matter; otherwise, it would have been trivial issue
    – volcano
    Sep 17, 2014 at 11:00
  • optimized version of ordered set, for anyone who is interested: def unique(iterable): ;seen = set(); seen_add = seen.add; return [item for item in iterable if not item in seen and not seen_add(item)]
    – DrD
    Feb 16, 2020 at 22:29
23

Another way of doing:

>>> seq = [1,2,3,'a', 'a', 1,2]
>> dict.fromkeys(seq).keys()
['a', 1, 2, 3]
1
  • 1
    Note that in modern Python versions (2.7+ I think, but I don't recall for sure), keys() returns a dictionary view object, not a list. Dec 22, 2017 at 15:24
22

Simple and easy:

myList = [1, 2, 3, 1, 2, 5, 6, 7, 8]
cleanlist = []
[cleanlist.append(x) for x in myList if x not in cleanlist]

Output:

>>> cleanlist 
[1, 2, 3, 5, 6, 7, 8]
2
  • 5
    quadratic complexity nonetheless - in is O(n) operation and your cleanlist will have at most n numbers => worst-case ~O(n^2)
    – jermenkoo
    Mar 23, 2016 at 23:02
  • 7
    list comprehensions shouldn't be used for side effects. Dec 7, 2018 at 22:09
16

I had a dict in my list, so I could not use the above approach. I got the error:

TypeError: unhashable type:

So if you care about order and/or some items are unhashable. Then you might find this useful:

def make_unique(original_list):
    unique_list = []
    [unique_list.append(obj) for obj in original_list if obj not in unique_list]
    return unique_list

Some may consider list comprehension with a side effect to not be a good solution. Here's an alternative:

def make_unique(original_list):
    unique_list = []
    map(lambda x: unique_list.append(x) if (x not in unique_list) else False, original_list)
    return unique_list
3
  • 6
    map with a side effect is even more misleading than a listcomp with a side effect. Also, lambda x: unique_list.append(x) is just a clunkier and slower way to pass unique_list.append.
    – abarnert
    Nov 8, 2014 at 1:48
  • Very useful way to append elements in just one line, thanks!
    – ZLNK
    May 24, 2017 at 21:50
  • 2
    @ZLNK please, don't ever use that. Apart from being conceptually ugly, it's also extremely inefficient, because you actually create a potentially large list and throw it away just to perform basic iteration. Mar 13, 2019 at 20:14
14

If you want to preserve the order, and not use any external modules here is an easy way to do this:

>>> t = [1, 9, 2, 3, 4, 5, 3, 6, 7, 5, 8, 9]
>>> list(dict.fromkeys(t))
[1, 9, 2, 3, 4, 5, 6, 7, 8]

Note: This method preserves the order of appearance, so, as seen above, nine will come after one because it was the first time it appeared. This however, is the same result as you would get with doing

from collections import OrderedDict
ulist=list(OrderedDict.fromkeys(l))

but it is much shorter, and runs faster.

This works because each time the fromkeys function tries to create a new key, if the value already exists it will simply overwrite it. This wont affect the dictionary at all however, as fromkeys creates a dictionary where all keys have the value None, so effectively it eliminates all duplicates this way.

1
13

All the order-preserving approaches I've seen here so far either use naive comparison (with O(n^2) time-complexity at best) or heavy-weight OrderedDicts/set+list combinations that are limited to hashable inputs. Here is a hash-independent O(nlogn) solution:

Update added the key argument, documentation and Python 3 compatibility.

# from functools import reduce <-- add this import on Python 3

def uniq(iterable, key=lambda x: x):
    """
    Remove duplicates from an iterable. Preserves order. 
    :type iterable: Iterable[Ord => A]
    :param iterable: an iterable of objects of any orderable type
    :type key: Callable[A] -> (Ord => B)
    :param key: optional argument; by default an item (A) is discarded 
    if another item (B), such that A == B, has already been encountered and taken. 
    If you provide a key, this condition changes to key(A) == key(B); the callable 
    must return orderable objects.
    """
    # Enumerate the list to restore order lately; reduce the sorted list; restore order
    def append_unique(acc, item):
        return acc if key(acc[-1][1]) == key(item[1]) else acc.append(item) or acc 
    srt_enum = sorted(enumerate(iterable), key=lambda item: key(item[1]))
    return [item[1] for item in sorted(reduce(append_unique, srt_enum, [srt_enum[0]]))] 
5
  • Yet, this solution requires orderable elements. I will use it uniquify my list of lists: it is a pain to tuple() lists and to hash them. | | | | - Generally speaking, the hash process takes a time proportional to the size of the whole data, while this solution takes a time O(nlog(n)), depending only on the length of the list.
    – loxaxs
    May 18, 2016 at 20:40
  • I think that the set-based approach is equally cheap (O(n log n)), or cheaper, than sorting + detection of uniques. (This approach would parallelize much better, though.) It also does not exactly preserve the initial order, but it gives a predictable order.
    – 9000
    Jun 5, 2017 at 16:29
  • @9000 That is true. I've never mentioned time-complexity of a hash-table-based approach, which is obviously O(n). Here you can find many answers incorporating hash-tables. They are not universal, though, because they require objects to be hashable. Moreover, they are a lot more memory-intensive. Jun 6, 2017 at 17:34
  • Takes time to read and understand this answer. Is there a point in enumerating when you are not using the indices? The reduce() is already working on a sorted collection srt_enum, why did you apply sorted again?
    – Brayoni
    May 1, 2020 at 11:09
  • @Brayoni the first sort is there to group equal values, the second sort is there to restore initial order. The enumeration is needed to keep track of original relative order. May 1, 2020 at 13:30
13

I've compared the various suggestions with perfplot. It turns out that, if the input array doesn't have duplicate elements, all methods are more or less equally fast, independently of whether the input data is a Python list or a NumPy array.

enter image description here

If the input array is large, but contains just one unique element, then the set, dict and np.unique methods are costant-time if the input data is a list. If it's a NumPy array, np.unique is about 10 times faster than the other alternatives.

enter image description here

It's somewhat surprising to me that those are not constant-time operations, too.


Code to reproduce the plots:

import perfplot
import numpy as np
import matplotlib.pyplot as plt


def setup_list(n):
    # return list(np.random.permutation(np.arange(n)))
    return [0] * n


def setup_np_array(n):
    # return np.random.permutation(np.arange(n))
    return np.zeros(n, dtype=int)


def list_set(data):
    return list(set(data))


def numpy_unique(data):
    return np.unique(data)


def list_dict(data):
    return list(dict.fromkeys(data))


b = perfplot.bench(
    setup=[
        setup_list,
        setup_list,
        setup_list,
        setup_np_array,
        setup_np_array,
        setup_np_array,
    ],
    kernels=[list_set, numpy_unique, list_dict, list_set, numpy_unique, list_dict],
    labels=[
        "list(set(lst))",
        "np.unique(lst)",
        "list(dict(lst))",
        "list(set(arr))",
        "np.unique(arr)",
        "list(dict(arr))",
    ],
    n_range=[2 ** k for k in range(23)],
    xlabel="len(array)",
    equality_check=None,
)
# plt.title("input array = [0, 1, 2,..., n]")
plt.title("input array = [0, 0,..., 0]")
b.save("out.png")
b.show()
10

You could also do this:

>>> t = [1, 2, 3, 3, 2, 4, 5, 6]
>>> s = [x for i, x in enumerate(t) if i == t.index(x)]
>>> s
[1, 2, 3, 4, 5, 6]

The reason that above works is that index method returns only the first index of an element. Duplicate elements have higher indices. Refer to here:

list.index(x[, start[, end]])
Return zero-based index in the list of the first item whose value is x. Raises a ValueError if there is no such item.

2
  • This is horribly inefficient. list.index is a linear-time operation, making your solution quadratic. Apr 13, 2018 at 20:42
  • You're right. But also I believe it's fairly obvious the solution is intended to be a one liner that preserves the order. Everything else is already in here.
    – Atonal
    Oct 13, 2018 at 0:08
10

Best approach of removing duplicates from a list is using set() function, available in python, again converting that set into list

In [2]: some_list = ['a','a','v','v','v','c','c','d']
In [3]: list(set(some_list))
Out[3]: ['a', 'c', 'd', 'v']
2
  • @MeetZaveri glad.! May 2, 2018 at 5:53
  • Instantiating new lists and sets is not free. What happens if we do this many times in quick succession (ie. in a very tight loop), and the lists are very small?
    – Z4-tier
    Dec 24, 2019 at 11:47
10

You can use set to remove duplicates:

mylist = list(set(mylist))

But note the results will be unordered. If that's an issue:

mylist.sort()
2
  • 1
    You can just do: mylist = sorted(list(set(mylist))) Jan 3, 2019 at 13:39
  • Or mylist = sorted(set(mylist)). Feb 29 at 23:15
8

Try using sets:

import sets
t = sets.Set(['a', 'b', 'c', 'd'])
t1 = sets.Set(['a', 'b', 'c'])

print t | t1
print t - t1
7

One more better approach could be,

import pandas as pd

myList = [1, 2, 3, 1, 2, 5, 6, 7, 8]
cleanList = pd.Series(myList).drop_duplicates().tolist()
print(cleanList)

#> [1, 2, 3, 5, 6, 7, 8]

and the order remains preserved.

1
  • 2
    Though this might work well, using a heavy library like pandas for this purpose seems like an overkill.
    – Glutexo
    Mar 20, 2019 at 12:29
7

This one cares about the order without too much hassle (OrderdDict & others). Probably not the most Pythonic way, nor shortest way, but does the trick:

def remove_duplicates(item_list):
    ''' Removes duplicate items from a list '''
    singles_list = []
    for element in item_list:
        if element not in singles_list:
            singles_list.append(element)
    return singles_list
2
  • 1. You should never shadow builtin names (at least, as important as list); 2. Your method scales extremely bad: it is quadratic in the number of elements in list. Jan 7, 2018 at 19:05
  • 1
    1. Correct, but this was an example; 2. Correct, and that's exactly the reason why I offered it. All solutions posted here have pros and cons. Some sacrifice simplicity or order, mine sacrifices scalability.
    – cgf
    Mar 20, 2018 at 11:45
6

Reduce variant with ordering preserve:

Assume that we have list:

l = [5, 6, 6, 1, 1, 2, 2, 3, 4]

Reduce variant (unefficient):

>>> reduce(lambda r, v: v in r and r or r + [v], l, [])
[5, 6, 1, 2, 3, 4]

5 x faster but more sophisticated

>>> reduce(lambda r, v: v in r[1] and r or (r[0].append(v) or r[1].add(v)) or r, l, ([], set()))[0]
[5, 6, 1, 2, 3, 4]

Explanation:

default = (list(), set())
# user list to keep order
# use set to make lookup faster

def reducer(result, item):
    if item not in result[1]:
        result[0].append(item)
        result[1].add(item)
    return result

reduce(reducer, l, default)[0]
6

There are many other answers suggesting different ways to do this, but they're all batch operations, and some of them throw away the original order. That might be okay depending on what you need, but if you want to iterate over the values in the order of the first instance of each value, and you want to remove the duplicates on-the-fly versus all at once, you could use this generator:

def uniqify(iterable):
    seen = set()
    for item in iterable:
        if item not in seen:
            seen.add(item)
            yield item

This returns a generator/iterator, so you can use it anywhere that you can use an iterator.

for unique_item in uniqify([1, 2, 3, 4, 3, 2, 4, 5, 6, 7, 6, 8, 8]):
    print(unique_item, end=' ')

print()

Output:

1 2 3 4 5 6 7 8

If you do want a list, you can do this:

unique_list = list(uniqify([1, 2, 3, 4, 3, 2, 4, 5, 6, 7, 6, 8, 8]))

print(unique_list)

Output:

[1, 2, 3, 4, 5, 6, 7, 8]
2
  • seen = set(iterable); for item in seen: yield item is almost certainly faster. (I haven't tried this specific case, but that would be my guess.)
    – dylnmc
    Sep 23, 2016 at 18:40
  • 3
    @dylnmc, that's a batch operation, and it also loses the ordering. My answer was specifically intended to be on-the-fly and in order of first occurrence. :)
    – Cyphase
    Oct 26, 2016 at 4:42
6

You can use the following function:

def rem_dupes(dup_list): 
    yooneeks = [] 
    for elem in dup_list: 
        if elem not in yooneeks: 
            yooneeks.append(elem) 
    return yooneeks

Example:

my_list = ['this','is','a','list','with','dupicates','in', 'the', 'list']

Usage:

rem_dupes(my_list)

['this', 'is', 'a', 'list', 'with', 'dupicates', 'in', 'the']

2
  • Unsuitable for large lists as it creates a duplicate.
    – ingyhere
    Mar 29, 2021 at 16:52
  • @ingyhere The OP did not suggest anything re: large lists. There is a always a tradeoff to every type of implementation, so the premise that every answer must default to "most scalable" is false.
    – Cybernetic
    Mar 29, 2021 at 16:59
5

Using set :

a = [0,1,2,3,4,3,3,4]
a = list(set(a))
print a

Using unique :

import numpy as np
a = [0,1,2,3,4,3,3,4]
a = np.unique(a).tolist()
print a
0
5

Without using set

data=[1, 2, 3, 1, 2, 5, 6, 7, 8]
uni_data=[]
for dat in data:
    if dat not in uni_data:
        uni_data.append(dat)

print(uni_data) 
5

The Magic of Python Built-in type

In python, it is very easy to process the complicated cases like this and only by python's built-in type.

Let me show you how to do !

Method 1: General Case

The way (1 line code) to remove duplicated element in list and still keep sorting order

line = [1, 2, 3, 1, 2, 5, 6, 7, 8]
new_line = sorted(set(line), key=line.index) # remove duplicated element
print(new_line)

You will get the result

[1, 2, 3, 5, 6, 7, 8]

Method 2: Special Case

TypeError: unhashable type: 'list'

The special case to process unhashable (3 line codes)

line=[['16.4966155686595', '-27.59776154691', '52.3786295521147']
,['16.4966155686595', '-27.59776154691', '52.3786295521147']
,['17.6508629295574', '-27.143305738671', '47.534955022564']
,['17.6508629295574', '-27.143305738671', '47.534955022564']
,['18.8051102904552', '-26.688849930432', '42.6912804930134']
,['18.8051102904552', '-26.688849930432', '42.6912804930134']
,['19.5504702331098', '-26.205884452727', '37.7709192714727']
,['19.5504702331098', '-26.205884452727', '37.7709192714727']
,['20.2929416861422', '-25.722717575124', '32.8500163147157']
,['20.2929416861422', '-25.722717575124', '32.8500163147157']]

tuple_line = [tuple(pt) for pt in line] # convert list of list into list of tuple
tuple_new_line = sorted(set(tuple_line),key=tuple_line.index) # remove duplicated element
new_line = [list(t) for t in tuple_new_line] # convert list of tuple into list of list

print (new_line)

You will get the result :

[
  ['16.4966155686595', '-27.59776154691', '52.3786295521147'], 
  ['17.6508629295574', '-27.143305738671', '47.534955022564'], 
  ['18.8051102904552', '-26.688849930432', '42.6912804930134'], 
  ['19.5504702331098', '-26.205884452727', '37.7709192714727'], 
  ['20.2929416861422', '-25.722717575124', '32.8500163147157']
]

Because tuple is hashable and you can convert data between list and tuple easily

4

below code is simple for removing duplicate in list

def remove_duplicates(x):
    a = []
    for i in x:
        if i not in a:
            a.append(i)
    return a

print remove_duplicates([1,2,2,3,3,4])

it returns [1,2,3,4]

2
  • 2
    If you don't care about order, then this takes significantly longer. list(set(..)) (over 1 million passes) will beat this solution by about 10 whole seconds - whereas this approach takes about 12 seconds, list(set(..)) only takes about 2 seconds!
    – dylnmc
    Sep 23, 2016 at 18:35
  • @dylnmc this is also a duplicate of a significantly older answer Jan 7, 2018 at 19:07
4

Here's the fastest pythonic solution comaring to others listed in replies.

Using implementation details of short-circuit evaluation allows to use list comprehension, which is fast enough. visited.add(item) always returns None as a result, which is evaluated as False, so the right-side of or would always be the result of such an expression.

Time it yourself

def deduplicate(sequence):
    visited = set()
    adder = visited.add  # get rid of qualification overhead
    out = [adder(item) or item for item in sequence if item not in visited]
    return out

Not the answer you're looking for? Browse other questions tagged or ask your own question.