Перебирать список строк для получения групп строк

Учитывая следующий список:

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"]}

Кажется, что это должно быть относительно просто (хотя, вероятно, не однострочно!), но я просто не могу уложиться в этом.

Любой совет ценится.

Алгоритм вы в основном объяснили, теперь вам просто нужно написать его на Python.

interjay 25.07.2024 18:55

да, я знаю, чего хочу достичь. Я предполагаю, что я борюсь с тем, «как мне продолжать цикл, пока все элементы не будут обработаны»

turbonerd 25.07.2024 18:59
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
2
75
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Ответ принят как подходящий

Вам не нужна никакая рекурсия, просто берите элементы один за другим, если у вас есть двоеточие, измените ключ. Используйте 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']}

Я выбрал этот, так как считаю, что его легче всего читать/понимать. Спасибо

turbonerd 25.07.2024 19:29

Я не верю, что существует «питонический» способ сделать это без использования некоторых лямбда-выражений, которые сделали бы код нечитаемым.

Но, как вы заметили, довольно просто реализовать это с помощью цикла и «использовать» тот факт, что 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)

Есть некоторые крайние случаи, которые я не учел, главным образом потому, что вы не указали, что должно происходить в таких случаях. Например, что если ввод начинается со «значения», а не с элемента, который заканчивается на :.

Но это должно дать вам общее представление о том, как вы можете решить эту проблему.

если список начинается с элемента, который не содержит двоеточия, я создаю исключение - так что с этим достаточно легко справиться. Поэтому я могу гарантировать, что список начинается с такого пункта, спасибо за это. Кажется, я использовал неправильное слово в названии или перепутал, как его нужно написать.

turbonerd 25.07.2024 19:03
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] (удалить двоеточие)
Mark Tolonen 25.07.2024 19:13

@MarkTolonen: Ты абсолютно прав. Спасибо за отчет об ошибке

inspectorG4dget 29.07.2024 00:18

Вот простая функция, которая обрабатывает списки, которые расположены по порядку и начинаются с ключа. Он работает для ваших примеров и обрабатывает аргумент функции с пустым списком по умолчанию (пустой список вернет пустой словарь).

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)}

Попробуйте это онлайн!

Ну, но это все еще работает...

no comment 25.07.2024 19:17

Один из простых способов — просто написать именно тот алгоритм, который вы описали.

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']}

Демо здесь

Другие вопросы по теме