В основном у меня есть список OrderedDicts;

lst = [
    OrderedDict([('type', 'character'), ('letter', 'a')]),
    OrderedDict([('type', 'character'), ('letter', 'b')]),
    OrderedDict([('type', 'character'), ('letter', 'c')]),
    OrderedDict([('type', 'character'), ('letter', 'd')]),
    OrderedDict([('type', 'integer'), ('number', '1')]),
    OrderedDict([('type', 'integer'), ('number', '2')]),
    OrderedDict([('type', 'integer'), ('number', '3')]),
    OrderedDict([('type', 'integer'), ('number', '4')])
]

Я хочу изменить это в

lst = [
    OrderedDict([('type', 'character'), ('letter', ['a', 'b', 'c', 'd'])]),
    OrderedDict([('type', 'integer'), ('number', ['1', '2', '3', '4'])])
]

Я подумал о том, чтобы получить список значений типов ['character', 'integer'] и просмотреть все словари, чтобы попытаться сгруппировать букву / цифру в списке, а затем создать новый список словарей, которые будут заполнены данными, которые я есть. Не уверен, что это лучший подход, хотя

Ценим помощь

4
zigzag 7 Дек 2016 в 17:54

3 ответа

Лучший ответ

Учитывая, что ваш список словарей уже отсортирован , вы можете использовать itertools.groupby напрямую. Отображение между элементами и связанными с ними строками в словаре помогает избежать многократного вызова d.items и делает код расширяемым для новых типов:

from collections import OrderedDict
from itertools import groupby

_map = {'character': 'letter', 'integer': 'number'}

l = [OrderedDict([('type', k), (_map[k], [d[_map[k]] for d in g])]) for k, g in groupby(lst, lambda x: x['type'])]
print(l)
# [OrderedDict([('type', 'character'), ('letter', ['a', 'b', 'c', 'd'])]), OrderedDict([('type', 'integer'), ('number', ['1', '2', '3', '4'])])]
2
Moses Koledoye 7 Дек 2016 в 15:27

Для ответа с меньшим пониманием списка (который иногда помогает с удобочитаемостью). См .:

from collections import OrderedDict

lst = [
    OrderedDict([('type', 'character'), ('letter', 'a')]),
    OrderedDict([('type', 'character'), ('letter', 'b')]),
    OrderedDict([('type', 'character'), ('letter', 'c')]),
    OrderedDict([('type', 'character'), ('letter', 'd')]),
    OrderedDict([('type', 'integer'), ('number', '1')]),
    OrderedDict([('type', 'integer'), ('number', '2')]),
    OrderedDict([('type', 'integer'), ('number', '3')]),
    OrderedDict([('type', 'integer'), ('number', '4')])
]

types_found = []  # using a list to maintain original order
types_dict = {}   # using a dict for speed and storage

for entry in lst:
    t = entry.get("type", "unknown")
    if t not in types_dict:
        types_found.append(t)
        types_dict[t] = OrderedDict([("type", t)])
    for k, v in entry.items():
        if k != "type":
            types_dict[t].setdefault(k, []).append(v)

new_list = [types_dict[t] for t in types_found]
# okay, so I did use one list comprehension, but it's a simple one :)

Для вышеизложенного я предполагаю, что важно следующее:

  • У вас могут быть другие записи типа, отличные от «символ» или «целое число»; так что вы хотите открытого решения.
  • Вы хотите сохранить исходный порядок, найденный в lst.
  • В вашем производственном коде могут отсутствовать ключи; или больше ключей, чем ожидалось.

Это было написано с Python 3.5. Для некоторых старых версий вам, возможно, придется заменить «.items ()» на «.iteritems ()».

0
JohnAD 7 Дек 2016 в 16:31

Это должно работать:

lst = [OrderedDict([('type', t), (kind, [d.items()[1][1] for d in lst if d['type'] == t])]) for (t, kind) in set((d['type'], d.items()[1][0]) for d in lst)]

Выходы :

[OrderedDict([('type', 'integer'), ('number', ['1', '2', '3', '4'])]), OrderedDict([('type', 'character'), ('letter', ['a', 'b', 'c', 'd'])])]
1
kmaork 7 Дек 2016 в 15:20