2010-08-19 7 views
2

У меня есть два списка словарей Python, entries9 и entries10. Я хочу сравнить элементы и написать совместные элементы в новый список под названием joint_items. Я также хочу сохранить непревзойденные элементы в двух новых списках, unmatched_items_9 и unmatched_items_10.Pythonic способ сравнить два списка и распечатать непревзойденные элементы?

Это мой код. Получение joint_items и unmatched_items_9 (во внешнем списке) довольно просто: но как мне получить unmatched_items_10 (во внутреннем списке)?

for counter, entry1 in enumerate(entries9): 
    match_found = False 
    for counter2,entry2 in enumerate(entries10): 
     if match_found: 
      continue 
     if entry1[a]==entry2[a] and entry1[b]==entry2[b]: # the dictionaries only have some keys in common, but we care about a and b 
      match_found = True 
      joint_item = entry1 
      joint_items.append(joint_item) 
      #entries10.remove(entry2) # Tried this originally, but realised it messes with the original list object! 
    if match_found: 
     continue 
    else: 
     unmatched_items_9.append(entry1) 

Производительность на самом деле не проблема, так как это одноразовый скрипт.

+0

Я думаю, что вы можете заменить 'if match_found: continue' на' if match_found: break' (в конце) во внутреннем цикле. – SiggyF

+0

Хорошая точка @SiggyF! Да, я могу. – AP257

ответ

8

эквивалент того, что вы сейчас делаете, но наоборот, является:

unmatched_items_10 = [d for d in entries10 if d not in entries9] 

В то время как более кратким, чем ваш способ кодирования его, это та же самая проблема производительности: это займет время пропорционально количеству элементов в каждом списке. Если длины, которые вас интересуют, составляют около 9 или 10 (как указывают эти цифры), никаких проблем.

Но для перечней существенной длины вы можете получить гораздо лучшую производительность, сортируя списки и «переходя их» параллельно «так сказать» (время пропорционально N log N, где N - это длина более длинного списка). Есть и другие возможности (растущего усложнения;), если даже этот более продвинутый подход недостаточен для того, чтобы получить требуемую производительность. Я воздержусь от предложения очень сложных вещей, если вы не укажете, что вы требуете, чтобы он получил хорошую производительность (в этом случае, пожалуйста, укажите типичные длины каждого списка и типичного содержимого диктонов, являющихся их товарами, начиная с Конечно, такими «деталями» являются решающее значение для выбора алгоритмов, которые являются хорошим компромиссом между скоростью и простотой).

Edit: ОП редактировать его Q, чтобы показать, что он заботится о, для любых двух dicts d1 и d2 один каждый из двух списков, не является ли d1 == d2 (что то, что in оператор проверяет), а d1[a]==d2[a] and d1[b]==d2[b]. В этом случае оператор in не может быть использован (ну, не без фанки упаковки, но это осложнение, что лучше всего избегать, когда это возможно ;-), но all встроенных заменяет его сподручно:

unmatched_items_10 = [d for d in entries10 
         if all(d[a]!=d1[a] or d[b]!=d2[b] for d2 in entries9)] 

Я переключил логика вокруг (до != и or, за De Morgan's laws), так как мы хотим, чтобы dicts не соответствовали. Однако, если вы предпочитаете:

unmatched_items_10 = [d for d in entries10 
         if not any(d[a]==d1[a] and d[b]==d2[b] for d2 in entries9)] 

Лично мне не нравится if not any и if not all, по стилистическим причинам, но математика безупречна (на то, что на странице Википедии называет Extensions к законам де Моргана, поскольку any является квантор существования и all универсальный квантификатор, так сказать ;-). Производительность должна быть примерно эквивалентной (но тогда ОП пояснил в комментарии, что производительность для них не очень важна).

+0

Благодарим вас за подробный ответ. Производительность не является проблемой - это одноразовый скрипт для очистки некоторых данных, и не имеет значения, сколько времени потребуется. К сожалению, хотя «d not in entries9» не работает, потому что условие соответствия более сложное - мне нужно сравнить определенные поля. Это больше похоже на «если d [a] == entries9_item [a] и d [b] == entries9_item [b]". Я уточню вопрос, чтобы сделать это более ясным. – AP257

+0

@ AP257, вам было бы приятно сказать, что, в первую очередь, вы знаете, что проверки равенства - это, очевидно, особые случаи, и именно это вы использовали ;-). Во всяком случае, редактирование моего ответа, чтобы показать, как изменяется код. –

+0

Извините. Спасибо за это - очень аккуратное использование всех() и any(). Чтобы получить список joint_items, вы думаете, что я просто должен сделать «joint_items = [d для d в entry10, если все (d [a] == d1 [a] или d [b] == d2 [b] для d2 в записи9)] "? Это кажется повторяющимся, но, вероятно, более безопасным, чем возиться с исходными объектами. – AP257

0

У Python stdlib есть класс, difflib.SequenceMatcher, который выглядит так, как будто он может делать то, что вы хотите, хотя я не знаю, как его использовать!

0

Вы можете использовать sets и связанные с ними методы, например intersection. Вам, однако, нужно будет превратить ваши словари в неизменяемые данные, чтобы вы могли хранить их в set (например, string s). Будет что-то вроде этой работы?

a = set(str(x) for x in entries9) 
b = set(str(x) for x in entries10) 

# You'll have to change the above lines if you only care about _some_ of the keys 

joint_items = a.union(b) 
unmatched_items = a - b 

# Now you can turn them back into dicts: 
joint_items  = [eval(i) for i in joint_items] 
unmatched_items = [eval(i) for i in unmatched_items] 
+0

Я использовал бы 'dict.items' и' dict', а не 'str' и' eval', если это возможно. – SiggyF

Смежные вопросы