#python #list #dictionary #nested #data-wrangling
#python #Список #словарь #вложенный #перебор данных
Вопрос:
У меня есть словарь, который выглядит следующим образом:
dict_in = {'key0': {a: [A, B, C], b: [A, B, C]}, 'key1': {c: [A, B, C], d: [A, B, C]}, 'key2': {e: 0, f: 0}}
Мне нужна функция (или что-то еще), которая возвращает тот же словарь, но без списков. На месте списков находится единственное значение, соответствующее n-му значению в каждом списке. Для ключей, у которых нет соответствующего списка, я просто хочу, чтобы значение оставалось неизменным. Например, некоторая функция как таковая:
nth_val = 2
def take_nth_value_from_lists_in_nested_dict(list_in, nth_val):
...things and stuff...
return dict_out
dict_out = {'key0': {a: B, b: B}, 'key1': {c: B, d: B}, 'key2': {e: 0, f: 0}}
Обратите внимание, что key2 остался прежним. Я понятия не имею, как это сделать элегантно, поэтому любая помощь будет высоко оценена!
Комментарии:
1. Что должно произойти, если в списке нет n позиций? Также является ли список произвольно вложенным?
2. @DaniMesejo хороший момент. В идеале это должно вызывать исключение.
Ответ №1:
Я бы сделал это с помощью рекурсии:
def take_nth_value_from_lists_in_nested_dict(obj, n: int):
if isinstance(obj, dict):
return {k: take_nth_value_from_lists_in_nested_dict(v, n) for k, v in obj.items()}
if isinstance(obj, list):
return take_nth_value_from_lists_in_nested_dict(obj[n-1], n)
return obj
>>> take_nth_value_from_lists_in_nested_dict({'a': ['A', 'B', 'C'], 'b': ['A', 'B', 'C'], 'key1': {'c': ['A', 'B', 'C'], 'd': ['A', 'B', 'C']}, 'key2': {'e': 0, 'f': 0}}, 2)
{'a': 'B', 'b': 'B', 'key1': {'c': 'B', 'd': 'B'}, 'key2': {'e': 0, 'f': 0}}
Это должно работать для произвольно глубокой вложенности, в том числе внутри элементов списка (например, если N-й элемент списка сам по себе является dict, содержащим больше dicts / списков).
Комментарии:
1. Очень впечатляет! На самом деле, ваше решение было настолько быстрым и впечатляющим, что заставило меня усомниться в своих способностях и перспективах как программиста 🙂
Ответ №2:
Однострочное решение может выглядеть следующим образом:
list_in = {'key0': {'a': ['A', 'B', 'C'], 'b': ['A', 'B', 'C']}, 'key1': {'c': ['A', 'B', 'C'], 'd': ['A', 'B', 'C']}, 'key2': {'e': 0, 'f': 0}}
nth = 1
list_out = {k: {k2: v2 if not isinstance(v2,list) else v2[nth]
for k2,v2 in v.items() }
for k,v in list_in.items() }
print list_out
{'key2': {'e': 0, 'f': 0}, 'key1': {'c': 'B', 'd': 'B'}, 'key0': {'a': 'B', 'b': 'B'}}
Обратите внимание, что я заменил ключи и значения списков строками.
Комментарии:
1. Спасибо! Впечатлен, что вы могли получить это в одной строке. Хотя мне больше нравится подход @Samwise.
2. Да, это решение работает только для этого конкретного случая. Samwise будет работать для общего случая.
Ответ №3:
Вы могли бы попробовать с:
list_in = {'key0': {'a': ['A', 'B', 'C'], 'b': ['A', 'B', 'C']}, 'key1': {'c': ['A', 'B', 'C'], 'd': ['A', 'B', 'C']}, 'key2': {'e': 0, 'f': 0}}
nth = 1
for k,v in list_in.items():
for k2 in v.keys():
element = list_in[k][k2]
if isinstance(element, list):
list_in[k][k2] = element[nth]
print(list_in)
Он возвращает:
{'key0': {'a': 'B', 'b': 'B'},
'key1': {'c': 'B', 'd': 'B'},
'key2': {'e': 0, 'f': 0}}
Комментарии:
1. Это словарь со списками в нем, поэтому вам придется перебирать каждую пару ключ-значение на произвольную глубину. Ваше предложение будет работать только со списками.
2. Я предложил небольшую функцию для списка. Я обновил свой ответ. Спасибо за ваш комментарий.
Ответ №4:
как я вас правильно понимаю, вы хотели бы прочитать n-й элемент списка во вложенном словаре и, следовательно, требуется функция, которая это сделает.
Я решил эту задачу рекурсивно, но прежде чем мы углубимся в то, как работает функция, давайте сначала попробуем понять, что мы можем сделать со словарем. Ниже приведены четыре полезных метода при работе со словарем:
.keys()
.values()
.items()
.update()
test_dict = {'a':1, 'b':2, 'c':3}
# Read out keys
list(test_dict.keys())
>>> ['a', 'b', 'c']
# Read out values
list(test_dict.values())
>>> [1, 2, 3]
# Read out keys and values together
list(test_dict.items())
>>> [('a', 1), ('b', 2), ('c', 3)]
# Update a dictionary with a new key, value pair or overwrite an existing one
test_dict.update({'d':4})
test_dict
>>> {'a': 1, 'b': 2, 'c': 3, 'd': 4}
Обратите внимание, что мы получаем с помощью метода .items():
Список с парами ключ, значение в виде кортежей
# I'm a tuple
('something','something else','etc.')
Мы можем использовать свойство итерации списка с помощью цикла for, и это именно то, что нам нужно в начале функции. Итак, давайте начнем кодировать:
def nth_element_from_list_in_nested_dict(nested_dict, list_index):
# Iterate trough a list with tuples
for key,val in nested_dict.items():
Переменные key, val теперь представляют записи отдельных кортежей:
Из нашего test_dict это будет:
# On the first iteration
key='a'
val=1
# On the second iteration
key='b'
val=2
# and so on...
Поскольку мы предполагаем, что словарь, переданный функции, является вложенным, поэтому мы хотим проверить, является ли переменная val другим словарем. Это можно сделать в if-инструкции:
def nth_element_from_list_in_nested_dict(nested_dict, list_index):
# Iterate trough a list with tuples
for key,val in nested_dict.items():
# Check if variable val is another dictionary
if isinstance(val, dict):
# isinstance(val, type: for example dict or list) returns True if val is of specified type
Теперь начинается рекурсивная часть функции. Пока значение переданного словаря функции является другим словарем, мы снова вызываем функцию внутри функции, пока значение не будет другого типа (например, список!). Потому что, когда у нас есть список в качестве значения, нам не нужно копать глубже, потому что мы достигли значения, из которого мы можем выбрать n-й элемент. Надеюсь, это все еще имеет смысл… Давайте продолжим работу с кодом:
def nth_element_from_list_in_nested_dict(nested_dict, list_index):
# Iterate trough a list with tuples
for key,val in nested_dict.items():
# Check if variable val is another dictionary
if isinstance(val, dict):
nth_element_from_list_in_nested_dict(val, list_index) # calls the function again when val is another dictionary
elif isinstance(val, list):
Когда мы нашли значение, представляющее собой список, теперь мы можем обновить этот словарь n-м элементом из этого списка:
def nth_element_from_list_in_nested_dict(nested_dict, list_index):
# Iterate trough a list with tuples
for key,val in nested_dict.items():
# Check if variable val is another dictionary
if isinstance(val, dict):
nth_element_from_list_in_nested_dict(val, list_index) # calls the function again when val is another dictionary
elif isinstance(val, list):
nested_dict.update({key:val[list_index]}) # updates dictionary with the n-th element from val (when val is of type list)
Все, что нам нужно сделать сейчас, это вернуть обновленный словарь и посмотреть, действительно ли функция выполняет то, что предназначено для вашего примера словаря (я заменил ваши переменные в list_in строками, поскольку они не были определены)
### Input ###
list_in = {'key0':
{'a': ['A','B', 'C'],
'b': ['A', 'B', 'C']
},
'key1':
{'c': ['A', 'B', 'C'],
'd': ['A', 'B', 'C']
},
'key2': {
'e': 0,
'f': 0,
}
}
### Function ###
def nth_element_from_list_in_nested_dict(nested_dict, list_index):
#
for key,val in nested_dict.items():
if isinstance(val, dict):
nth_element_from_list_in_nested_dict(val, list_index)
elif isinstance(val, list):
nested_dict.update({key:val[list_index]})
return nested_dict
### Tests ###
# replace with first element (list_index=0)
to_be_updated = list_in.copy()
nth_element_from_list_in_nested_dict(to_be_updated, 0)
>>> {'key0': {'a': 'A', 'b': 'A'},
'key1': {'c': 'A', 'd': 'A'},
'key2': {'e': 0, 'f': 0}}
# replace with third element (list_index=2)
to_be_updated = list_in.copy()
nth_element_from_list_in_nested_dict(to_be_updated, 2)
>>> {'key0': {'a': 'C', 'b': 'C'},
'key1': {'c': 'C', 'd': 'C'},
'key2': {'e': 0, 'f': 0}}
Это оказалось довольно длинным объяснением, и, возможно, некоторые части (например, рекурсивная часть в функции) трудно понять. Кроме того, эта функция подвержена ошибкам, когда списки имеют разную длину.
Тем не менее, я надеюсь, что это поможет вам и другим продвигаться вперед 😉