Loại bỏ trùng lặp trong danh sách


995

Tôi cần phải viết một chương trình để kiểm tra xem một danh sách có bất kỳ sự trùng lặp nào không và nếu nó xóa nó và trả về một danh sách mới với các mục không được sao chép / xóa. Đây là những gì tôi có nhưng thành thật mà nói tôi không biết phải làm gì.

def remove_duplicates():
    t = ['a', 'b', 'c', 'd']
    t2 = ['a', 'c', 'd']
    for t in t2:
        t.append(t.remove())
    return t

22
Mô tả của bạn cho biết bạn kiểm tra "danh sách" để tìm bản sao, nhưng mã của bạn sẽ kiểm tra hai danh sách.
Brendan Long


* bằng cách sử dụng set: list (set (ElementS_LIST)) * bằng dictionary: list (dict.fromkeys (ElementS_LIST))
Shayan Amani

Câu trả lời:


1641

Cách tiếp cận phổ biến để có được một bộ sưu tập các mặt hàng độc đáo là sử dụng a set. Bộ là bộ sưu tập không có thứ tự của các đối tượng riêng biệt . Để tạo một tập hợp từ bất kỳ lần lặp nào, bạn chỉ cần chuyển nó vào set()hàm tích hợp. Nếu sau này bạn cần một danh sách thực một lần nữa, bạn có thể chuyển tập hợp tương tự cho list()hàm.

Ví dụ sau sẽ bao gồm mọi thứ bạn đang cố gắng làm:

>>> t = [1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> t
[1, 2, 3, 1, 2, 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]

Như bạn có thể thấy từ kết quả ví dụ, thứ tự ban đầu không được duy trì . Như đã đề cập ở trên, bộ chính chúng là các bộ sưu tập không có thứ tự, vì vậy thứ tự bị mất. Khi chuyển đổi một bộ trở lại danh sách, một thứ tự tùy ý được tạo ra.

Duy trì trật tự

Nếu thứ tự là quan trọng với bạn, thì bạn sẽ phải sử dụng một cơ chế khác. Một giải pháp rất phổ biến cho việc này là dựa vào OrderedDictđể giữ thứ tự các phím trong khi chèn:

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

Bắt đầu với Python 3.7 , từ điển tích hợp cũng được đảm bảo duy trì thứ tự chèn, do đó bạn cũng có thể sử dụng trực tiếp nếu bạn sử dụng Python 3.7 trở lên (hoặc CPython 3.6):

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

Lưu ý rằng điều này có thể có một số chi phí đầu tiên của việc tạo một từ điển trước, sau đó tạo một danh sách từ đó. Nếu bạn thực sự không cần phải giữ trật tự, bạn thường sử dụng một bộ tốt hơn, đặc biệt là vì nó mang lại cho bạn nhiều thao tác hơn để làm việc. Kiểm tra câu hỏi này để biết thêm chi tiết và các cách khác để giữ trật tự khi xóa trùng lặp.


Cuối cùng lưu ý rằng cả giải pháp setcũng như OrderedDict/ dictyêu cầu các mục của bạn đều có thể băm được . Điều này thường có nghĩa là họ phải bất biến. Nếu bạn phải xử lý các mục không thể băm (ví dụ: liệt kê các đối tượng), thì bạn sẽ phải sử dụng một cách tiếp cận chậm, trong đó về cơ bản bạn sẽ phải so sánh mọi mục với mọi mục khác trong một vòng lặp lồng nhau.


4
Điều này không hoạt động đối với các yếu tố danh sách không thể xóa được (ví dụ: danh sách các danh sách)
KNejad

3
@KNejad Đó là những gì đoạn cuối nói.
chọc

Ôi trời ơi. Nên đọc toàn bộ. Điều cuối cùng tôi làm là sử dụng bộ dữ liệu thay vì danh sách để phương pháp này vẫn có thể hoạt động.
KNejad

thêm điều này vào ví dụ, t = [3, 2, 1, 1, 2, 5, 6, 7, 8], cho thấy sự khác biệt rõ ràng!
sailfish009

"... chi phí đầu tiên của việc tạo một từ điển ... Nếu bạn không thực sự cần phải giữ trật tự, tốt hơn hết là bạn nên sử dụng một bộ." - Tôi đã mô tả điều này bởi vì tôi tò mò nếu nó thực sự đúng. Thời gian của tôi cho thấy rằng thực sự bộ này nhanh hơn một chút: 1,12bs trên mỗi vòng lặp (bộ) so với 1,53 Luồng trên mỗi vòng lặp (dict) trên các vòng lặp 1M với chênh lệch thời gian tuyệt đối khoảng 4 lần lặp trên 4M. Vì vậy, nếu bạn đang làm điều này trong một vòng lặp bên trong chặt chẽ, bạn có thể quan tâm, nếu không thì có lẽ là không.
millerdev

414

Trong Python 2.7 , cách mới để loại bỏ các bản sao khỏi một lần lặp trong khi giữ nó theo thứ tự ban đầu là:

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

Trong Python 3.5 , OrderedDict có triển khai C. Thời gian của tôi cho thấy rằng đây là cách nhanh nhất và ngắn nhất trong các cách tiếp cận khác nhau cho Python 3.5.

Trong Python 3.6 , dict thông thường trở nên vừa có trật tự. (Tính năng này được giữ cho CPython và PyPy nhưng có thể không có trong các triển khai khác). Điều đó cho chúng ta một cách khấu trừ nhanh nhất mới trong khi vẫn giữ trật tự:

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

Trong Python 3.7 , lệnh chính quy được đảm bảo cho cả hai thứ tự trên tất cả các cài đặt. Vì vậy, giải pháp ngắn nhất và nhanh nhất là:

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

10
Tôi nghĩ rằng đây là cách duy nhất để giữ các mục theo thứ tự.
Herberth Amaral


5
@MartijnPieters Correcting: Tôi nghĩ rằng đây là cách đơn giản duy nhất để giữ các mục theo thứ tự.
Herberth Amaral

12
Đối với điều này cũng vậy, nội dung của danh sách gốc phải được băm
Davide

Như @Davide đã đề cập, danh sách ban đầu phải có thể băm. Điều này có nghĩa, điều này không hoạt động cho một danh sách từ điển. TypeError: unhashable type: 'dictlist'
CraZ

187

Đó là một lót: list(set(source_list))sẽ thực hiện các mẹo.

A setlà thứ không thể có bản sao.

Cập nhật: một cách tiếp cận duy trì trật tự là hai dòng:

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

Ở đây chúng tôi sử dụng thực tế là OrderedDictghi nhớ thứ tự chèn của các khóa và không thay đổi nó khi một giá trị tại một khóa cụ thể được cập nhật. Chúng tôi chèn Truedưới dạng giá trị, nhưng chúng tôi có thể chèn bất cứ thứ gì, giá trị không được sử dụng. (cũng sethoạt động rất giống dictvới a với các giá trị bị bỏ qua.)


5
Điều này chỉ hoạt động nếu source_listcó thể băm.
Adrian Keister

@AdrianKeister: Đây là sự thật. Có những đối tượng có ngữ nghĩa bình đẳng hợp lý nhưng không thể băm, ví dụ như danh sách. OTOH nếu chúng ta không thể có một lối tắt như vội vàng, chúng ta sẽ kết thúc bằng một thuật toán bậc hai chỉ so sánh mọi phần tử với tất cả các phần tử duy nhất được biết hiện tại. Điều này có thể hoàn toàn OK cho các đầu vào ngắn, đặc biệt là với rất nhiều bản sao.
9000

Đúng, chính xác. Tôi nghĩ rằng câu trả lời của bạn sẽ có chất lượng cao hơn nếu bạn tính đến trường hợp sử dụng rất phổ biến này.
Adrian Keister

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

33
Lưu ý rằng phương pháp này hoạt động trong thời gian O (n ^ 2) và do đó rất chậm trên các danh sách lớn.
dotancohen

@Chris_Rands: Không chắc chắn frozensethoạt động với nội dung không thể băm. Tôi vẫn nhận được lỗi không thể băm khi sử dụng frozenset.
Adrian Keister

85

Nếu bạn không quan tâm đến đơn hàng, chỉ cần làm điều này:

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

A setđược đảm bảo không có trùng lặp.


3
Không hoạt động trừ khi lcó thể băm.
Adrian Keister

41

Để tạo một danh sách mới giữ nguyên thứ tự các yếu tố đầu tiên của các mục trùng lặp trong L

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

ví dụ if L=[1, 2, 2, 3, 4, 2, 4, 3, 5]sau đó newlistsẽ là[1,2,3,4,5]

Điều này kiểm tra từng phần tử mới chưa xuất hiện trước đây trong danh sách trước khi thêm nó. Ngoài ra, nó không cần nhập khẩu.


3
Điều này có độ phức tạp thời gian là O (n ^ 2) . Các câu trả lời với setOrderedDictcó thể có độ phức tạp thời gian khấu hao thấp hơn.
blubberdiblub

Tôi đã sử dụng trong mã của mình giải pháp này và hoạt động rất tốt nhưng tôi nghĩ rằng nó tốn thời gian
Gerasimos Ragavanis

@blubberdiblub bạn có thể giải thích cơ chế hiệu quả mã nào tồn tại trong set và OrderedDict có thể khiến chúng tốn ít thời gian hơn không? (không bao gồm chi phí tải chúng)
ilias iliadis

@iliasiliadis Việc triển khai thông thường của bộdict băm sử dụng hoặc (một số hình thức của sự cân) cây. Bạn phải xem xét việc xây dựng bộ hoặc dict và tìm kiếm trong nó (nhiều lần), nhưng phức tạp khấu hao của họ thường vẫn thấp hơn O (n ^ 2) . "Khấu hao" theo thuật ngữ đơn giản có nghĩa là trung bình (họ có thể có những trường hợp xấu nhất với độ phức tạp cao hơn so với trường hợp trung bình). Điều này chỉ có liên quan khi bạn có một số lượng lớn các mặt hàng.
blubberdiblub

25

Một đồng nghiệp đã gửi câu trả lời được chấp nhận như một phần mã của anh ấy cho tôi để xem mã hóa ngày hôm nay. Trong khi tôi chắc chắn ngưỡng mộ sự thanh lịch của câu trả lời trong câu hỏi, tôi không hài lòng với màn trình diễn. Tôi đã thử giải pháp này (tôi sử dụng thiết lập để giảm thời gian tra cứu)

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

Để so sánh hiệu quả, tôi đã sử dụng một mẫu ngẫu nhiên gồm 100 số nguyên - 62 là duy nhất

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

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

Dưới đây là kết quả của các phép đo

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

Vâng, điều gì xảy ra nếu thiết lập được loại bỏ khỏi giải pháp?

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

Kết quả không tệ như với OrderedDict , nhưng vẫn hơn 3 lần so với giải pháp ban đầu

In [136]: %timeit ordered_set(x)
10000 loops, best of 3: 52.6 us per loop

Đẹp sử dụng thiết lập tra cứu nhanh để tăng tốc độ so sánh lặp. Nếu tự không quan trọng danh sách (set (x)) vẫn là 6x nhanh hơn này
Joop

@Joop, đó là câu hỏi đầu tiên của tôi cho đồng nghiệp của tôi - thứ tự có vấn đề; nếu không, đó sẽ là một vấn đề không quan trọng
núi lửa

phiên bản tối ưu của bộ được đặt hàng, cho bất cứ ai quan tâm : 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

25

Ngoài ra còn có các giải pháp sử dụng Pandas và Numpy. Cả hai đều trả về mảng numpy để bạn phải sử dụng hàm .tolist()nếu bạn muốn có một danh sách.

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

Giải pháp gấu trúc

Sử dụng chức năng Pandas unique():

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

Giải pháp Numpy

Sử dụng chức năng numpy unique().

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

Lưu ý rằng numpy.unique () cũng sắp xếp các giá trị . Vì vậy, danh sách t2được trả lại sắp xếp. Nếu bạn muốn bảo quản đơn hàng như trong câu trả lời này :

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

Tuy nhiên, giải pháp này không quá thanh lịch so với các giải pháp khác, so với pandas.unique (), numpy.unique () cho phép bạn cũng kiểm tra xem các mảng lồng nhau có phải là duy nhất dọc theo một trục được chọn hay không.


Điều này sẽ chuyển đổi danh sách thành mảng numpy, một mớ hỗn độn và sẽ không hoạt động cho chuỗi.
227666

1
@ user227666 cảm ơn bạn đã đánh giá nhưng điều đó không đúng, nó hoạt động ngay cả với chuỗi và bạn có thể thêm .tolist nếu bạn muốn nhận danh sách ...
GM

1
Tôi nghĩ điều này giống như cố gắng giết một con ong bằng búa tạ. Hoạt động, chắc chắn! Nhưng, nhập một thư viện cho mục đích này có thể là một chút quá mức, phải không?
Ghi nợ Ray

@DebosmitRay nó có thể hữu ích nếu bạn làm việc trong Khoa học dữ liệu, nơi bạn thường làm việc với numpy và nhiều lần bạn cần phải làm việc với mảng numpy.
GM

câu trả lời hay nhất vào năm 2020 @DebosmitRay tôi hy vọng bạn thay đổi ý định và sử dụng numpy / gấu trúc mỗi khi bạn có thể
Egos

21

Một cách làm khác:

>>> seq = [1,2,3,'a', 'a', 1,2]
>> dict.fromkeys(seq).keys()
['a', 1, 2, 3]

1
Lưu ý rằng trong các phiên bản Python hiện đại (2.7+ tôi nghĩ, nhưng tôi không nhớ chắc chắn), keys()trả về một đối tượng xem từ điển, không phải danh sách.
Dustin Wyatt

16

Đơn giản và dễ dàng:

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

Đầu ra:

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

5
Tuy nhiên, độ phức tạp bậc hai - inlà hoạt động O (n) và bạn cleanlistsẽ có nhiều nhất nsố => trường hợp xấu nhất ~ O (n ^ 2)
jermenkoo

6
danh sách hiểu không nên được sử dụng cho các tác dụng phụ.
Jean-François Fabre

13

Trong câu trả lời này, sẽ có hai phần: Hai giải pháp duy nhất và biểu đồ tốc độ cho các giải pháp cụ thể.

Xóa các mục trùng lặp

Hầu hết các câu trả lời này chỉ loại bỏ các mục trùng lặp có thể băm được , nhưng câu hỏi này không có nghĩa là nó không chỉ cần các mục có thể băm , nghĩa là tôi sẽ cung cấp một số giải pháp không yêu cầu các mục có thể băm .

bộ sưu tập. Bộ đếm là một công cụ mạnh mẽ trong thư viện tiêu chuẩn có thể hoàn hảo cho việc này. Chỉ có một giải pháp khác thậm chí có Counter trong đó. Tuy nhiên, giải pháp đó cũng bị giới hạn ở các khóa có thể băm .

Để cho phép các khóa không thể xóa trong Counter, tôi đã tạo một lớp Container, nó sẽ cố lấy hàm băm mặc định của đối tượng, nhưng nếu thất bại, nó sẽ thử chức năng nhận dạng của nó. Nó cũng định nghĩa một phương trình và phương thức băm . Điều này là đủ để cho phép các mặt hàng không thể phá vỡ trong giải pháp của chúng tôi. Các đối tượng không thể xóa sẽ được xử lý như thể chúng có thể băm được. Tuy nhiên, hàm băm này sử dụng danh tính cho các đối tượng không thể xóa được, nghĩa là hai đối tượng bằng nhau mà cả hai đều không thể thực hiện được. Tôi đề nghị bạn ghi đè lên điều này và thay đổi nó để sử dụng hàm băm của một loại có thể thay đổi tương đương (như sử dụng hash(tuple(my_list))if my_listlà một danh sách).

Tôi cũng đã thực hiện hai giải pháp. Một giải pháp khác giữ thứ tự của các mục, sử dụng một lớp con của cả OrderedDict và Counter được đặt tên là 'OrderedCorer'. Bây giờ, đây là các chức năng:

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 là sắp xếp không theo thứ tự, oremd được sắp xếp theo thứ tự. Bạn có thể biết rõ cái nào nhanh hơn, nhưng tôi sẽ giải thích bằng mọi cách. Việc sắp xếp không theo thứ tự là hơi nhanh hơn. Nó giữ ít dữ liệu hơn, vì nó không cần thứ tự.

Bây giờ, tôi cũng muốn hiển thị các so sánh tốc độ của từng câu trả lời. Vì vậy, tôi sẽ làm điều đó ngay bây giờ.

Chức năng nào là nhanh nhất?

Để loại bỏ trùng lặp, tôi đã thu thập 10 hàm từ một vài câu trả lời. Tôi đã tính tốc độ của từng chức năng và đưa nó vào một biểu đồ bằng matplotlib.pyplot .

Tôi chia điều này thành ba vòng biểu đồ. Băm là bất kỳ đối tượng nào có thể được băm, không thể băm là bất kỳ đối tượng nào không thể băm. Trình tự được sắp xếp là một chuỗi bảo tồn trật tự, một chuỗi không có thứ tự không giữ trật tự. Bây giờ, đây là một vài điều khoản nữa:

Unableered Hashable là cho bất kỳ phương thức loại bỏ trùng lặp nào, mà không nhất thiết phải giữ trật tự. Nó không phải làm việc cho những thứ không thể, nhưng nó có thể.

Thứ tự Hashable là cho bất kỳ phương thức nào giữ thứ tự của các mục trong danh sách, nhưng nó không phải làm việc cho các mục không thể, nhưng nó có thể.

Thứ tự không thể xóa được là bất kỳ phương pháp nào giữ thứ tự của các mục trong danh sách và hoạt động cho các mục không thể xóa được.

Trên trục y là số giây cần thiết.

Trên trục x là số mà hàm được áp dụng.

Chúng tôi đã tạo các chuỗi cho các hàm băm không có thứ tự và các thứ tự băm được sắp xếp theo cách hiểu sau: [list(range(x)) + list(range(x)) for x in range(0, 1000, 10)]

Đối với không thể đặt hàng: [[list(range(y)) + list(range(y)) for y in range(x)] for x in range(0, 1000, 10)]

Lưu ý rằng có một 'bước' trong phạm vi vì nếu không có nó, điều này sẽ mất gấp 10 lần. Ngoài ra bởi vì theo quan điểm cá nhân của tôi, tôi nghĩ rằng nó có thể trông dễ đọc hơn một chút.

Cũng lưu ý các phím trên chú giải là những gì tôi đã cố đoán là phần quan trọng nhất của chức năng. Đối với chức năng nào làm tồi tệ nhất hoặc tốt nhất? Các biểu đồ nói cho chính nó.

Với việc giải quyết, đây là các biểu đồ.

Hashables không có thứ tự

nhập mô tả hình ảnh ở đây (Phóng to lên) nhập mô tả hình ảnh ở đây

Đặt hàng Hashables

nhập mô tả hình ảnh ở đây (Phóng to lên) nhập mô tả hình ảnh ở đây

Đặt mua Unhashables

nhập mô tả hình ảnh ở đây (Phóng to lên) nhập mô tả hình ảnh ở đây


11

Tôi đã có một lệnh trong danh sách của mình, vì vậy tôi không thể sử dụng cách tiếp cận trên. Tôi đã nhận được lỗi:

TypeError: unhashable type:

Vì vậy, nếu bạn quan tâm đến trật tự và / hoặc một số mặt hàng là unhashable . Sau đó, bạn có thể tìm thấy điều này hữu ích:

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

Một số có thể xem xét việc hiểu danh sách với một tác dụng phụ để không phải là một giải pháp tốt. Đây là một thay thế:

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

6
mapvới một tác dụng phụ thậm chí còn gây hiểu lầm nhiều hơn một listcomp với tác dụng phụ. Ngoài ra, lambda x: unique_list.append(x)chỉ là một cách nhanh hơn và chậm hơn để vượt qua unique_list.append.
abarnert

Cách rất hữu ích để nối các phần tử chỉ trong một dòng, cảm ơn!
ZLNK

2
@ZLNK làm ơn, đừng bao giờ sử dụng nó. Ngoài việc xấu về mặt khái niệm, nó còn cực kỳ kém hiệu quả, bởi vì bạn thực sự tạo ra một danh sách lớn tiềm năng và vứt nó đi chỉ để thực hiện phép lặp cơ bản.
Eli Korvigo

10

Tất cả các cách tiếp cận giữ trật tự mà tôi đã thấy ở đây cho đến nay đều sử dụng so sánh ngây thơ (với độ phức tạp thời gian O (n ^ 2) tốt nhất) hoặc kết hợp nặng OrderedDicts/ set+ listđược giới hạn ở các đầu vào có thể băm. Đây là một giải pháp O (nlogn) độc lập băm:

Cập nhật đã thêm keyđối số, tài liệu và khả năng tương thích Python 3.

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

Tuy nhiên, giải pháp này đòi hỏi các yếu tố có trật tự. Tôi sẽ sử dụng nó để xác định danh sách các danh sách của mình: thật khó để tuple()liệt kê và băm chúng. | | | | - Nói chung, quá trình băm mất một thời gian tỷ lệ thuận với kích thước của toàn bộ dữ liệu, trong khi giải pháp này mất một thời gian O (nlog (n)), chỉ phụ thuộc vào độ dài của danh sách.
loxaxs

Tôi nghĩ rằng cách tiếp cận dựa trên tập hợp là rẻ như nhau (O (n log n)), hoặc rẻ hơn, so với việc sắp xếp + phát hiện các lỗi. (Tuy nhiên, cách tiếp cận này sẽ song song tốt hơn nhiều.) Nó cũng không bảo toàn chính xác thứ tự ban đầu, nhưng nó đưa ra một thứ tự có thể dự đoán được.
9000

@ 9000 Đó là sự thật. Tôi chưa bao giờ đề cập đến sự phức tạp về thời gian của cách tiếp cận dựa trên bảng băm, rõ ràng là O (n). Ở đây bạn có thể tìm thấy nhiều câu trả lời kết hợp bảng băm. Tuy nhiên, chúng không phải là phổ quát vì chúng yêu cầu các đối tượng có thể băm được. Hơn nữa, chúng còn nhiều bộ nhớ hơn.
Eli Korvigo

Cần thời gian để đọc và hiểu câu trả lời này. Có một điểm trong việc liệt kê khi bạn không sử dụng các chỉ số? Các reduce() đã được làm việc trên một bộ sưu tập được sắp xếp srt_enum, tại sao bạn lại áp dụng sortedmột lần nữa?
Brayoni

@Brayoni loại đầu tiên là có để nhóm các giá trị bằng nhau, loại thứ hai là có để khôi phục lại thứ tự ban đầu. Việc liệt kê là cần thiết để theo dõi thứ tự tương đối ban đầu.
Eli Korvigo

9

Nếu bạn muốn duy trì thứ tự và không sử dụng bất kỳ mô-đun bên ngoài nào ở đây là một cách dễ dàng để làm điều này:

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

Lưu ý: Phương pháp này duy trì thứ tự xuất hiện, vì vậy, như đã thấy ở trên, chín sẽ xuất hiện sau một vì đây là lần đầu tiên nó xuất hiện. Tuy nhiên, đây là kết quả tương tự như bạn sẽ làm

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

nhưng nó ngắn hơn nhiều, và chạy nhanh hơn.

Điều này hoạt động bởi vì mỗi lần fromkeyshàm cố gắng tạo khóa mới, nếu giá trị đã tồn tại, nó sẽ chỉ ghi đè lên nó. Tuy nhiên, điều này sẽ không ảnh hưởng đến từ điển, vì fromkeystạo ra một từ điển trong đó tất cả các khóa đều có giá trị None, vì vậy nó sẽ loại bỏ tất cả các bản sao theo cách này một cách hiệu quả.


Ngoài ra hãy thử nó ở đây
Vineeshvs

8

Bạn cũng có thể làm điều này:

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

Lý do mà các công việc trên là indexphương thức chỉ trả về chỉ mục đầu tiên của một phần tử. Các yếu tố trùng lặp có chỉ số cao hơn. Tham khảo tại đây :

list.index (x [, start [, end]])
Trả về chỉ mục dựa trên zero trong danh sách của mục đầu tiên có giá trị là x. Tăng ValueError nếu không có mục đó.


Điều này là không hiệu quả khủng khiếp. list.indexlà một hoạt động thời gian tuyến tính, làm cho giải pháp của bạn là bậc hai.
Eli Korvigo

Bạn đúng. Nhưng tôi cũng tin rằng giải pháp khá rõ ràng là một lớp lót duy trì trật tự. Mọi thứ khác đã ở đây.
Atonal

7

Hãy thử sử dụng các bộ:

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

print t | t1
print t - t1

7

Giảm biến thể với bảo quản đặt hàng:

Giả sử rằng chúng tôi có danh sách:

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

Giảm biến thể (không hiệu quả):

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

5 x nhanh hơn nhưng tinh vi hơn

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

Giải trình:

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]

7

Cách tiếp cận tốt nhất để loại bỏ các bản sao khỏi danh sách là sử dụng hàm set () , có sẵn trong python, một lần nữa chuyển đổi tập hợp đó thành danh sách

In [2]: some_list = ['a','a','v','v','v','c','c','d']
In [3]: list(set(some_list))
Out[3]: ['a', 'c', 'd', 'v']

@MeetZaveri vui mừng.!
Anurag Misra

Khởi tạo danh sách và bộ mới không miễn phí. Điều gì xảy ra nếu chúng ta làm điều này nhiều lần liên tiếp (ví dụ: trong một vòng lặp rất chặt chẽ) và các danh sách rất nhỏ?
Z4-tier

6

Bạn có thể sử dụng chức năng sau:

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

Ví dụ :

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

Sử dụng:

rem_dupes(my_list)

['This', 'là', 'a', 'list', 'với', 'dupicates', 'in', 'the']


5

Có nhiều câu trả lời khác gợi ý các cách khác nhau để thực hiện việc này, nhưng chúng đều là các hoạt động hàng loạt và một số trong số chúng vứt bỏ thứ tự ban đầu. Điều đó có thể ổn tùy thuộc vào những gì bạn cần, nhưng nếu bạn muốn lặp lại các giá trị theo thứ tự của phiên bản đầu tiên của mỗi giá trị và bạn muốn loại bỏ trùng lặp một cách nhanh chóng so với tất cả cùng một lúc, bạn có thể sử dụng máy phát điện này:

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

Điều này trả về một trình tạo / iterator, vì vậy bạn có thể sử dụng nó ở bất cứ đâu mà bạn có thể sử dụng một trình vòng lặp.

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

print()

Đầu ra:

1 2 3 4 5 6 7 8

Nếu bạn muốn a list, bạn có thể làm điều này:

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

print(unique_list)

Đầu ra:

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

seen = set(iterable); for item in seen: yield itemgần như chắc chắn nhanh hơn. (Tôi chưa thử trường hợp cụ thể này, nhưng đó sẽ là phỏng đoán của tôi.)
dylnmc

2
@dylnmc, đó là một hoạt động hàng loạt, và nó cũng mất đơn đặt hàng. Câu trả lời của tôi được dự định cụ thể là trên đường bay và theo thứ tự xuất hiện đầu tiên. :)
Cyphase

5

Không cần sử dụng bộ

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

Bạn có thể sử dụng setđể loại bỏ trùng lặp:

mylist = list(set(mylist))

Nhưng lưu ý kết quả sẽ không được sắp xếp. Nếu đó là một vấn đề:

mylist.sort()

1
Bạn chỉ có thể làm: mylist = sort (list (set (mylist)))
Erik Campobadal

5

Một cách tiếp cận tốt hơn có thể là,

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]

và trật tự vẫn được bảo tồn.


Mặc dù điều này có thể hoạt động tốt, nhưng sử dụng một thư viện nặng như gấu trúc cho mục đích này có vẻ như là một việc quá mức.
Glutexo

4

Điều này quan tâm đến đơn đặt hàng mà không gặp quá nhiều rắc rối (OrderdDict & những người khác). Có lẽ không phải là cách Pythonic nhất, cũng không phải là cách ngắn nhất, nhưng thực hiện thủ thuật:

def remove_duplicates(list):
    ''' Removes duplicate items from a list '''
    singles_list = []
    for element in list:
        if element not in singles_list:
            singles_list.append(element)
    return singles_list

1. Bạn không bao giờ nên bỏ qua các tên dựng sẵn (ít nhất, quan trọng như list); 2. Phương pháp của bạn có tỷ lệ cực kỳ tệ: đó là bậc hai về số lượng phần tử trong list.
Eli Korvigo

1. Đúng, nhưng đây là một ví dụ; 2. Chính xác, và đó chính xác là lý do tại sao tôi cung cấp nó. Tất cả các giải pháp được đăng ở đây đều có ưu và nhược điểm. Một số hy sinh đơn giản hoặc trật tự, tôi hy sinh khả năng mở rộng.
cgf

đây là thuật toán "Shlemiel the họa sĩ" ...
Z4-tier

4

mã bên dưới là đơn giản để loại bỏ trùng lặp trong danh sách

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

nó trả về [1,2,3,4]


2
Nếu bạn không quan tâm đến trật tự, thì việc này sẽ mất nhiều thời gian hơn. list(set(..))(hơn 1 triệu lượt) sẽ đánh bại giải pháp này khoảng 10 giây - trong khi phương pháp này mất khoảng 12 giây, list(set(..))chỉ mất khoảng 2 giây!
dylnmc

@dylnmc đây cũng là một bản sao của một câu trả lời
Eli Korvigo

4

Đây là giải pháp pythonic nhanh nhất đến với những người khác được liệt kê trong các câu trả lời.

Sử dụng chi tiết thực hiện đánh giá ngắn mạch cho phép sử dụng khả năng hiểu danh sách, đủ nhanh. visited.add(item)luôn luôn trả về Nonekết quả, được đánh giá là False, vì vậy phía bên phải củaor sẽ luôn là kết quả của biểu thức như vậy.

Thời gian cho chính mình

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

4

Sử dụng bộ :

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

Sử dụng duy nhất :

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

4

Không may. Hầu hết các câu trả lời ở đây hoặc không giữ trật tự hoặc quá dài. Dưới đây là một câu trả lời đơn giản, bảo quản.

s = [1,2,3,4,5,2,5,6,7,1,3,9,3,5]
x=[]

[x.append(i) for i in s if i not in x]
print(x)

Điều này sẽ cung cấp cho bạn x với các bản sao được loại bỏ nhưng vẫn giữ trật tự.


3

Cách rất đơn giản trong Python 3:

>>> n = [1, 2, 3, 4, 1, 1]
>>> n
[1, 2, 3, 4, 1, 1]
>>> m = sorted(list(set(n)))
>>> m
[1, 2, 3, 4]

2
sorted(list(...))là dự phòng ( sortedđã ngầm chuyển đổi đối số của nó thành một đối số mới list, sắp xếp nó, sau đó trả về cái mới list, vì vậy sử dụng cả hai phương tiện để tạo một tạm thời không cần thiết list). Chỉ sử dụng listnếu kết quả không cần được sắp xếp, chỉ sử dụng sortednếu kết quả cần được sắp xếp.
ShadowRanger

3

The Magic of Python Kiểu tích hợp

Trong python, rất dễ xử lý các trường hợp phức tạp như thế này và chỉ bằng loại tích hợp sẵn của python.

Hãy để tôi chỉ cho bạn cách làm!

Cách 1: Trường hợp chung

Cách ( mã 1 dòng ) để loại bỏ phần tử trùng lặp trong danh sách và vẫn tiếp tục sắp xếp thứ tự

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

Bạn sẽ nhận được kết quả

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

Cách 2: Trường hợp đặc biệt

TypeError: unhashable type: 'list'

Trường hợp đặc biệt để xử lý không thể xóa được ( 3 mã dòng )

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)

Bạn sẽ nhận được kết quả:

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

Vì tuple có thể băm và bạn có thể chuyển đổi dữ liệu giữa danh sách và tuple dễ dàng

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.