Разверните dict, содержащий элементы списка, в список пар dict
Если у меня есть словарь, содержащий списки в одном или нескольких его значений:
data = {
'a':0,
'b':1,
'c':[0, 1, 2],
'pair':['one','two']
}
Как я могу получить список кортежей в паре pair
и перебирая c
со всем остальным постоянным? Например
output = [
({
'a':0,
'b':1,
'c':0,
'pair':'one'
},
{
'a':0,
'b':1,
'c':0,
'pair':'two'
}),
({
'a':0,
'b':1,
'c':1,
'pair':'one'
},
...
]
7 ответов
Что ж, это не выглядит особенно элегантно, но вы можете использовать вложенный для цикла или понимания списка:
output = []
for i in data['c']:
output.append(tuple({'a': 0, 'b': 1, 'c': i, 'pair': p} for p in data))
или же
output = [tuple({'a': 0, 'b': 1, 'c': i, 'pair': p} for p in data['pair']) for i in data['c']]
Более чистое решение может отделить генерацию dict компонента в функцию, например:
def gen_output_dict(c, pair):
return {'a': 0, 'b': 1, 'c': c, 'pair': pair}
output = []
for i in data['c']:
output.append(tuple(gen_output_dict(i, p) for p in data['pair']))
Ты можешь использовать itertools.product
в список значений и отслеживать ключ, из которого каждый элемент произошел. С ключом 'pair'
имеет особое значение, вы должны рассматривать это отдельно.
Код
from itertools import product
def unzip_dict(d):
keys = [k for k, v in d.items() if isinstance(v, list) and k != 'pair']
values = [d[k] for k in keys]
for values in product(*values):
yield tuple({**d, **dict(zip(keys, values)), 'pair': pair} for pair in d['pair'])
пример
data = {
'a': 0,
'c': [1, 2],
'pair': ['one', 'two']
}
print(*unzip_dict(data))
Выход
({'a': 0, 'c': 1, 'pair': 'one'}, {'a': 0, 'c': 1, 'pair': 'two'})
({'a': 0, 'c': 2, 'pair': 'one'}, {'a': 0, 'c': 2, 'pair': 'two'})
@ r3robertson, Вы также можете попробовать приведенный ниже код. Код основан на концепции list comprehension
& deepcopy() operation
в Python.
Проверьте мелкую копию против Deepcopy в Python.
import pprint;
import copy;
data = {
'a': 0,
'b': 1,
'c': [0, 1, 2],
'pair': ['one','two'],
};
def get_updated_dict(data, index, pair_name):
d = copy.deepcopy(data);
d.update({'c': index, 'pair': pair_name});
return d;
output = [tuple(get_updated_dict(data, index, pair_name) for pair_name in data['pair']) for index in data['c']];
# Pretty printing the output list.
pprint.pprint(output, indent=4);
Выход "
[ ( { 'a': 0, 'b': 1, 'c': 0, 'pair': 'one'},
{ 'a': 0, 'b': 1, 'c': 0, 'pair': 'two'}),
( { 'a': 0, 'b': 1, 'c': 1, 'pair': 'one'},
{ 'a': 0, 'b': 1, 'c': 1, 'pair': 'two'}),
( { 'a': 0, 'b': 1, 'c': 2, 'pair': 'one'},
{ 'a': 0, 'b': 1, 'c': 2, 'pair': 'two'})]
Красивая печать с использованием модуля JSON "
Примечание: здесь кортеж преобразуется в список, так как кортежи не поддерживаются внутри JSON.
import json;
print(json.dumps(output, indent=4));
Выход "
[
[
{
"a": 0,
"c": 0,
"b": 1,
"pair": "one"
},
{
"a": 0,
"c": 0,
"b": 1,
"pair": "two"
}
],
[
{
"a": 0,
"c": 1,
"b": 1,
"pair": "one"
},
{
"a": 0,
"c": 1,
"b": 1,
"pair": "two"
}
],
[
{
"a": 0,
"c": 2,
"b": 1,
"pair": "one"
},
{
"a": 0,
"c": 2,
"b": 1,
"pair": "two"
}
]
]
Следующее является довольно расширенным решением:
data = {
'a':0,
'b':1,
'c':[0, 1, 2],
'pair':['one','two']
}
# Get the length of the longest sequence
length = max(map(lambda x: len(x) if isinstance(x, list) else 1, data.values()))
# Loop through the data and change scalars to sequences
# while also making sure that smaller sequences are stretched to match
# or exceed the length of the longest sequence
for k, v in data.items():
if isinstance(v, list):
data[k] = v * int(round(length/len(v), 0))
else:
data[k] = [v] * length
# Create a dictionary to keep track of which outputs
# need to end up in which tuple
seen = dict.fromkeys(data.get('pair'), 0)
output = [tuple()] * len(seen)
# Loop through the data and place dictionaries in their
# corresponding tuples.
for v in zip(*data.values()):
d = dict(zip(data, v))
output[seen[d.get('pair')]] += (d,)
seen[d.get('pair')] += 1
print(output)
Идея состоит в том, чтобы преобразовать скаляры в ваших данных в последовательности, длина которых соответствует длине самой длинной последовательности в исходных данных. Поэтому первым делом я назначил переменную length
размер самой длинной последовательности. Вооружившись этими знаниями, мы перебираем исходные данные и расширяем уже существующие последовательности, чтобы соответствовать размеру самой длинной последовательности, преобразовывая скаляры в последовательности. Как только это будет сделано, мы переходим к созданию output
переменная. Но сначала мы создаем словарь под названием seen
чтобы помочь нам как создать список кортежей, так и отслеживать, какая группа словарей в каком кортере заканчивается. Это позволяет нам запустить один последний цикл, чтобы поместить группы словарей в соответствующие им кортежи.
Текущий вывод выглядит следующим образом:
[({'a': 0, 'b': 1, 'c': 0, 'pair': 'one'},
{'a': 0, 'b': 1, 'c': 1, 'pair': 'two'}),
({'a': 0, 'b': 1, 'c': 2, 'pair': 'one'},)]
Пожалуйста, дайте мне знать, если вам нужны дополнительные уточняющие детали. В противном случае, я надеюсь, что это послужит какой-то цели.
Не слишком идеально, но вот мое решение.
data = { 'a':0, 'b':1, 'c':[0, 1, 2], 'pair':['one','two'] }
a,b = data['pair'], data['c']
for t in range(0, len(b)):
for u in range(0, len(a)):
for h in a:
data['c']=b[t]
data['pair']=a[u]
print(tuple([data]))
Если вы уверены в определенных ключах, вы можете использовать, как показано ниже.
record_dict = {'id':'0123abc', 'роли': ['abc', 'cda', 'xyz']}output = []
для индекса ключ в enumerate(record_dict['roles']):output.append({'id': record_dict.get('id'), 'roles': key})print(output)
Ты можешь использовать itertools
:
import itertools
data = {
'a':0,
'b':1,
'c':[0, 1, 2],
'pair':['one','two']
}
def expand_dict(data):
grouped = [a for a, b in data.items() if isinstance(b, list)]
p = [[a, list(b)] for a, b in itertools.groupby(itertools.product(*[data[i] for i in grouped]), key=lambda x:x[0])]
return [tuple({**data, **dict(zip(grouped, i))} for i in c) for _, c in p]
print(expand_dict(data))
Выход:
[({'a': 0, 'b': 1, 'c': 0, 'pair': 'one'}, {'a': 0, 'b': 1, 'c': 0, 'pair': 'two'}),
({'a': 0, 'b': 1, 'c': 1, 'pair': 'one'}, {'a': 0, 'b': 1, 'c': 1, 'pair': 'two'}),
({'a': 0, 'b': 1, 'c': 2, 'pair': 'one'}, {'a': 0, 'b': 1, 'c': 2, 'pair': 'two'})]
Это решение также будет работать на вводе со многими возможными списками значений:
data = {'a':[5, 6, 1, 3], 'b':1, 'c':[0, 1, 2], 'pair':['one', 'two']}
print(expand_dict(data))
Выход:
[({'a': 5, 'b': 1, 'c': 0, 'pair': 'one'}, {'a': 5, 'b': 1, 'c': 0, 'pair': 'two'}, {'a': 5, 'b': 1, 'c': 1, 'pair': 'one'}, {'a': 5, 'b': 1, 'c': 1, 'pair': 'two'}, {'a': 5, 'b': 1, 'c': 2, 'pair': 'one'}, {'a': 5, 'b': 1, 'c': 2, 'pair': 'two'}), ({'a': 6, 'b': 1, 'c': 0, 'pair': 'one'}, {'a': 6, 'b': 1, 'c': 0, 'pair': 'two'}, {'a': 6, 'b': 1, 'c': 1, 'pair': 'one'}, {'a': 6, 'b': 1, 'c': 1, 'pair': 'two'}, {'a': 6, 'b': 1, 'c': 2, 'pair': 'one'}, {'a': 6, 'b': 1, 'c': 2, 'pair': 'two'}), ({'a': 1, 'b': 1, 'c': 0, 'pair': 'one'}, {'a': 1, 'b': 1, 'c': 0, 'pair': 'two'}, {'a': 1, 'b': 1, 'c': 1, 'pair': 'one'}, {'a': 1, 'b': 1, 'c': 1, 'pair': 'two'}, {'a': 1, 'b': 1, 'c': 2, 'pair': 'one'}, {'a': 1, 'b': 1, 'c': 2, 'pair': 'two'}), ({'a': 3, 'b': 1, 'c': 0, 'pair': 'one'}, {'a': 3, 'b': 1, 'c': 0, 'pair': 'two'}, {'a': 3, 'b': 1, 'c': 1, 'pair': 'one'}, {'a': 3, 'b': 1, 'c': 1, 'pair': 'two'}, {'a': 3, 'b': 1, 'c': 2, 'pair': 'one'}, {'a': 3, 'b': 1, 'c': 2, 'pair': 'two'})]