Учитывая следующий список:
inp = ["arg1:", "list", "of", "args", "arg2:", "other", "list"]
Как я могу разработать такой словарь?
out = {"arg1": ["list", "of", "args"], "arg2": ["other", "list"]}
По сути, это случай:
arg1:
)Другими примерами могут быть:
inp = ["test:", "one", "help:", "two", "three", "four"]
out = {"test": ["one"], "help": ["two", "three", "four"]
---
inp = ["one:", "list", "two:", "list", "list", "three:", "list", "list", "list"]
out = {"one": ["list"], "two": ["list", "list"], "three": ["list", "list", "list"]}
Кажется, что это должно быть относительно просто (хотя, вероятно, не однострочно!), но я просто не могу уложиться в этом.
Любой совет ценится.
да, я знаю, чего хочу достичь. Я предполагаю, что я борюсь с тем, «как мне продолжать цикл, пока все элементы не будут обработаны»
Вам не нужна никакая рекурсия, просто берите элементы один за другим, если у вас есть двоеточие, измените ключ. Используйте dict.sedefault в качестве помощника для инициализации пустых списков:
inp = ['arg1:', 'list', 'of', 'args', 'arg2:', 'other', 'list']
out = {}
key = None # default key
for item in inp:
if item.endswith(':'):
key = item.removesuffix(':')
continue
out.setdefault(key, []).append(item)
Выход:
{'arg1': ['list', 'of', 'args'], 'arg2': ['other', 'list']}
Я выбрал этот, так как считаю, что его легче всего читать/понимать. Спасибо
Я не верю, что существует «питонический» способ сделать это без использования некоторых лямбда-выражений, которые сделали бы код нечитаемым.
Но, как вы заметили, довольно просто реализовать это с помощью цикла и «использовать» тот факт, что dict
изменяемы:
inp = ["arg1:", "list", "of", "args", "arg2:", "other", "list"]
result = {}
reference_point = result
for item in inp:
if item.endswith(':'):
if (item := item.rstrip(':')) not in result:
result[item] = []
reference_point = result[item]
else:
reference_point.append(item)
print(result)
Есть некоторые крайние случаи, которые я не учел, главным образом потому, что вы не указали, что должно происходить в таких случаях. Например, что если ввод начинается со «значения», а не с элемента, который заканчивается на :
.
Но это должно дать вам общее представление о том, как вы можете решить эту проблему.
если список начинается с элемента, который не содержит двоеточия, я создаю исключение - так что с этим достаточно легко справиться. Поэтому я могу гарантировать, что список начинается с такого пункта, спасибо за это. Кажется, я использовал неправильное слово в названии или перепутал, как его нужно написать.
import itertools
L = ["arg1:", "list", "of", "args", "arg2:", "other", "list"]
answer = {}
key = None
for k, group in itertools.groupby(L, lambda s: s.endswith(":")):
if k:
key = list(group)[0].rstrip(":")
else:
answer[key] = list(group)
Результат:
In [313]: answer
Out[313]: {'arg1:': ['list', 'of', 'args'], 'arg2:': ['other', 'list']}
key = next(group)[:-1]
(удалить двоеточие)
@MarkTolonen: Ты абсолютно прав. Спасибо за отчет об ошибке
Вот простая функция, которая обрабатывает списки, которые расположены по порядку и начинаются с ключа. Он работает для ваших примеров и обрабатывает аргумент функции с пустым списком по умолчанию (пустой список вернет пустой словарь).
def convertListToDict(argList = []):
returnDict = {}
newKey = None
for arg in argList:
if arg[-1] == ":":
newKey = arg[:-1]
returnDict[newKey] = []
elif newKey:
returnDict[newKey].append(arg)
return returnDict
Вы правы, это, вероятно, не должно быть однострочным.
out = {s[:-1]: (v := []) for s in inp if s[-1:] == ':' or v.append(s)}
Ну, но это все еще работает...
Один из простых способов — просто написать именно тот алгоритм, который вы описали.
def foo(xs: list[str]) -> dict[str, list[str]]:
d = {}
for x in xs:
if x[-1] == ':': d[x[:-1]] = l = []
else: l.append(x)
return d
inp = ["test:", "one", "help:", "two", "three", "four"]
out = {"test": ["one"], "help": ["two", "three", "four"]}
assert foo(inp) == out
inp = ["one:", "list", "two:", "list", "list", "three:", "list", "list", "list"]
out = {"one": ["list"], "two": ["list", "list"], "three": ["list", "list", "list"]}
assert foo(inp) == out
Лично я не предпочитаю это, так как это очень важно, а не питонично или функционально по своей природе.
Я обновлю ответ, если удастся написать краткий функциональный.
Хотя решение @mozway работает, оно излишне увеличивает накладные расходы на поиск по словарю и создание экземпляра нового списка для каждой итерации, где элемент не заканчивается на :
.
Вместо этого вы можете сохранить ссылку на последний созданный список и просто добавлять новые элементы в список, на который ссылаются:
inp = ["arg1:", "list", "of", "args", "arg2:", "other", "list"]
out = {}
for i in inp:
if i.endswith(':'):
out[i.removesuffix(':')] = lst = []
else:
lst.append(i)
out
становится:
{'arg1': ['list', 'of', 'args'], 'arg2': ['other', 'list']}
Демо здесь
Алгоритм вы в основном объяснили, теперь вам просто нужно написать его на Python.