Как сравнить список списков/наборов в python?

Что является самым простым способом сравнения 2 списков/наборов и вывода различий? Есть ли встроенные функции, которые помогут мне сравнить вложенные списки/наборы?

Входы:

First_list = [['Test.doc', '1a1a1a', 1111], 
              ['Test2.doc', '2b2b2b', 2222],  
              ['Test3.doc', '3c3c3c', 3333]
             ]  
Secnd_list = [['Test.doc', '1a1a1a', 1111], 
              ['Test2.doc', '2b2b2b', 2222], 
              ['Test3.doc', '8p8p8p', 9999], 
              ['Test4.doc', '4d4d4d', 4444]]  

Ожидаемый результат:

Differences = [['Test3.doc', '3c3c3c', 3333],
               ['Test3.doc', '8p8p8p', 9999], 
               ['Test4.doc', '4d4d4d', 4444]]

Ответ 1

Итак, вам нужна разница между двумя списками элементов.

first_list = [['Test.doc', '1a1a1a', 1111], 
              ['Test2.doc', '2b2b2b', 2222], 
              ['Test3.doc', '3c3c3c', 3333]]
secnd_list = [['Test.doc', '1a1a1a', 1111], 
              ['Test2.doc', '2b2b2b', 2222], 
              ['Test3.doc', '8p8p8p', 9999], 
              ['Test4.doc', '4d4d4d', 4444]]

Сначала я перечислил список списков в список кортежей, так как кортежи хешируются (списки отсутствуют), поэтому вы можете преобразовать список кортежей в набор кортежей:

first_tuple_list = [tuple(lst) for lst in first_list]
secnd_tuple_list = [tuple(lst) for lst in secnd_list]

Затем вы можете создавать наборы:

first_set = set(first_tuple_list)
secnd_set = set(secnd_tuple_list)

EDIT (предложенный sdolan): Вы могли бы сделать последние два шага для каждого списка в одном слое:

first_set = set(map(tuple, first_list))
secnd_set = set(map(tuple, secnd_list))

Примечание: map - это команда функционального программирования, которая применяет функцию в первом аргументе (в данном случае к функции tuple) к каждому элементу во втором аргументе (который в нашем случае представляет собой список списков).

и найти симметричную разницу между множествами:

>>> first_set.symmetric_difference(secnd_set) 
set([('Test3.doc', '3c3c3c', 3333),
     ('Test3.doc', '8p8p8p', 9999),
     ('Test4.doc', '4d4d4d', 4444)])

Примечание first_set ^ secnd_set эквивалентно symmetric_difference.

Также, если вы не хотите использовать наборы (например, используя python 2.2), его довольно просто сделать. Например, со списком:

>>> [x for x in first_list if x not in secnd_list] + [x for x in secnd_list if x not in first_list]
[['Test3.doc', '3c3c3c', 3333],
 ['Test3.doc', '8p8p8p', 9999],
 ['Test4.doc', '4d4d4d', 4444]]

или с функциональной командой filter и lambda. (Вы должны проверить оба пути и объединить).

>>> filter(lambda x: x not in secnd_list, first_list) + filter(lambda x: x not in first_list, secnd_list)

[['Test3.doc', '3c3c3c', 3333],
 ['Test3.doc', '8p8p8p', 9999],
 ['Test4.doc', '4d4d4d', 4444]]

Ответ 2

Не уверен, что для этого есть хорошая функция, но "ручной" способ сделать это не сложно:

differences = []

for list in firstList:
    if list not in secondList:
        differences.append(list)

Ответ 3

>>> First_list = [['Test.doc', '1a1a1a', '1111'], ['Test2.doc', '2b2b2b', '2222'], ['Test3.doc', '3c3c3c', '3333']] 
>>> Secnd_list = [['Test.doc', '1a1a1a', '1111'], ['Test2.doc', '2b2b2b', '2222'], ['Test3.doc', '3c3c3c', '3333'], ['Test4.doc', '4d4d4d', '4444']] 


>>> z = [tuple(y) for y in First_list]
>>> z
[('Test.doc', '1a1a1a', '1111'), ('Test2.doc', '2b2b2b', '2222'), ('Test3.doc', '3c3c3c', '3333')]
>>> x = [tuple(y) for y in Secnd_list]
>>> x
[('Test.doc', '1a1a1a', '1111'), ('Test2.doc', '2b2b2b', '2222'), ('Test3.doc', '3c3c3c', '3333'), ('Test4.doc', '4d4d4d', '4444')]


>>> set(x) - set(z)
set([('Test4.doc', '4d4d4d', '4444')])

Ответ 4

Я думаю, вам придется конвертировать списки в настройки:

>>> a = {('a', 'b'), ('c', 'd'), ('e', 'f')}
>>> b = {('a', 'b'), ('h', 'g')}
>>> a.symmetric_difference(b)
{('e', 'f'), ('h', 'g'), ('c', 'd')}

Ответ 5

http://docs.python.org/library/difflib.html является хорошим отправным местом для того, что вы ищете.

Если вы примените его рекурсивно к дельтам, вы сможете обрабатывать вложенные структуры данных. Но это займет определенную работу.

Ответ 6

Используя множество понятий, вы можете сделать его однострочным. Если вы хотите:

чтобы получить набор кортежей, тогда:

Differences = {tuple(i) for i in First_list} ^ {tuple(i) for i in Secnd_list}

Или получить список кортежей, затем:

Differences = list({tuple(i) for i in First_list} ^ {tuple(i) for i in Secnd_list})

Или получить список списков (если вы действительно хотите), то:

Differences = [list(j) for j in {tuple(i) for i in First_list} ^ {tuple(i) for i in Secnd_list}]

PS: Я читаю здесь: fooobar.com/info/17721/..., что функция map() не является питоническим способом делать что-то.