Пересечение списка диктов на основе общего ключа

Скажем, у меня есть два списка диктов:

dates = [{'created':'2010-12-01'},{'created':'2010-12-02'},....]
elts = [{'created':'2010-12-01', 'key1':'val1', 'key2':'val2'}, {'created':'2010-12-05','key1':'val1'}]

Список дат представляет собой набор смежных дат.

Список elts может быть где угодно от 1 до len(dates), и то, что я хочу сделать, это в основном дополнить elts, чтобы у него был dict для даты, независимо от того, есть ли другие ключи.

Это мое наивное решение:

for d in dates:
    for e in elts:
        if d['created'] == e['created']:
            d.update(dict(key1=e['key1']))

Таким образом, у меня будет окончательный array d со всеми датами в каждом dict, но могут быть/не быть другие ключи/валы.

Какое хорошее «питоновское» решение?


person Maverick    schedule 03.01.2011    source источник
comment
Похоже, то, что вам действительно нужно, - это диктовка диктовок, с внешней диктовкой, указанной в дате «создания».   -  person Karl Knechtel    schedule 04.01.2011
comment
d.update(dict(key1=e['key1'])) это странный способ написать d['key1']=e['key1']   -  person Jochen Ritzel    schedule 04.01.2011


Ответы (4)


Я думаю, что ваш вопрос немного неверен, поскольку ваше решение, похоже, на самом деле не отвечает на ваш вопрос, но если вы хотите создать запись в elts для каждой даты в dates, которая еще не появляется в elts, вы можете использовать это :

all_dates = set(e['created'] for e in dates) # gets a list of all dates that exist in `dates`
elts_dates = set(e['created'] for e in elts) # same for elts

missing_dates = all_dates - elts_dates

for entry in missing_dates:
    elts.append(dict(created=entry))

Вот фрагмент http://codepad.org, который показывает этот фрагмент в действии: http://codepad.org/n4NbjvPM

person Alex Vidal    schedule 03.01.2011
comment
+1, это я тоже понимаю вопрос. set() отлично работает с генераторными выражениями, поэтому эффективнее использовать set(e['created'] for e in dates) и т. д. - person John La Rooy; 04.01.2011
comment
@gnibbler: Хороший вопрос. Я обновил свой ответ, чтобы вместо этого использовать выражения генератора (и обновил фрагмент кодовой панели, чтобы использовать новые изменения) - person Alex Vidal; 04.01.2011

РЕДАКТИРОВАТЬ: другое решение:

Сделайте набор дат, который у вас уже есть:

dates_in_elts = set(e['created'] for e in elts)

for d in dates:
    if d['created'] not in dates_in_elts:
        e.append(d)

Это перебирает каждый список только один раз, а не перебирает elts для каждой даты в датах.

person Thomas K    schedule 03.01.2011
comment
Я бы рекомендовал не использовать set(), так как он не сохранит ключи и значения вместе. - person Ishpeck; 04.01.2011
comment
@lshpecK: Если вы хотите поддерживать их в актуальном состоянии, лучше всего держать их вместе. Но если вы создаете и используете его сразу, набор работает абсолютно нормально. - person Thomas K; 04.01.2011

Вместо этого я, вероятно, сделал бы эти списки словарями.

  dates_d = dict([(x['created'], x) for x in dates])
  elts_d = dict([(x['created'], x) for x in elts])
  dates_d.update(elts_d)

Если вам затем нужно, чтобы это был список диктов по порядку, вы можете сделать это легко:

  dates = [dates_d[x] for x in sorted(dates_d)]

Однако, если вы не делаете ничего, кроме их слияния, ваше решение может быть более удобочитаемым. Но списки словарей, я подозреваю, не очень удобный формат данных в данном случае.

person Lennart Regebro    schedule 03.01.2011

Возможно, я неправильно прочитал, но мне кажется, что конечный результат вашего кода заключается в том, что для каждого словаря в elts вы действительно хотите просто скопировать этот дикт из elts, чтобы перезаписать соответствующий дикт в датах.

>>> for d in dates:
...    for e in elts:
...       if d['created'] == e['created']:
...          d.update(e)

В этот момент именно словарь дат отражает то, что, как я думаю, вы хотите.

person jonesy    schedule 04.01.2011
comment
это правильно, даты отражают, после чего я просто делаю elts = dates и использую это. - person Maverick; 04.01.2011