У меня есть словари Python, которые иногда для одного ключа имеют несколько значений, указанных через запятую (например, «значение1, значение2»). В этих случаях мне нужно разделить словарь на множество значений (см. Нижнюю часть вопроса для примера желаемого результата). В словаре может быть только одно значение на один ключ, в противном случае это должен быть совершенно отдельный словарь.

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

Например

x = {"name":"alice", "age":20,"hobby":"badminton, basketball", "language":"python"}
for eachkey, value in x.items():
    if ", " in value:
        x.popitem()

Получите следующую ошибку, прежде чем продолжить:

RuntimeError: dictionary changed size during iteration

Это пример ожидаемого ввода и вывода:

Ввод

x = {"name":"alice", "age":20,"hobby":"badminton, basketball", "language":"python"}

Вывод

[
    {"name":"alice", "age":20,"hobby":"badminton", "language":"python"},
    {"name":"alice", "age":20,"hobby":"basketball", "language":"python"}
]

Или другой пример с несколькими ключами с несколькими значениями :
(отредактировано + включено для уточнения после ответа @isaactfa)

Ввод

x = {"name":"alice", "age":20,"hobby":"badminton, basketball", "language":"python, go"}

Вывод

[
    {'name': 'alice', 'age': 20, 'hobby': 'badminton', 'language': 'python'}, 
    {'name': 'alice', 'age': 20, 'hobby': 'badminton', 'language': 'go'}, 
    {'name': 'alice', 'age': 20, 'hobby': 'basketball', 'language': 'python'}, 
    {'name': 'alice', 'age': 20, 'hobby': 'basketball', 'language': 'go'}
]
1
Leo E 2 Май 2019 в 14:25

2 ответа

Лучший ответ

Вы можете использовать функцию product():

from itertools import product

x = {"name":"alice", "age":20,"hobby":"badminton, basketball", "language":"python"}

vals = [v.split(', ') if isinstance(v, str) else [v] for v in x.values()]
print([dict(zip(x, p)) for p in product(*vals)])

Выход:

[{'name': 'alice', 'age': 20, 'hobby': 'badminton', 'language': 'python'},
 {'name': 'alice', 'age': 20, 'hobby': 'basketball', 'language': 'python'}]
2
Mykola Zotko 2 Май 2019 в 13:48

РЕДАКТИРОВАТЬ . Это не приводит к перестановкам словарей, если несколько ключей имеют более одного значения. Если это то, что вам нужно, проверьте ответ @ georg.

ОРИГИНАЛЬНЫЙ ОТВЕТ .

Это должно дать вам желаемый результат:

def make_dicts(x):
    tuples = [(k, *(s.strip() for s in v.split(","))) if isinstance(v, str) else (k, v) for k, v in x.items()]
    most_values = max(len(t) for t in tuples) - 1
    for i in range(most_values):
        yield {k: v[i % len(v)] for k, *v in tuples}

Теперь вы можете перебрать make_dicts(x), чтобы получить новые словари, или сделать list(make_dicts(x)), чтобы получить их в списке.

Как это работает, сначала он создает список кортежей, в которых ключ является первым элементом, а каждое значение разделяется запятой. Затем я нахожу самый большой из этих кортежей, потому что именно столько новых словарей будет создано (вычитая 1 для ключа). Для каждого нового словаря я сейчас просто говорю ему поставить ключ, а затем следующее значение, оборачивая индекс по модулю, чтобы дублировать ключи только с одним значением.

Надеюсь, это то, что вы искали.

1
isaactfa 2 Май 2019 в 12:15