Как создать словарь, используя один список?

У меня есть список URL и заголовков с сайта газеты в моей стране. Как общий пример:

x = ['URL1','news1','news2','news3','URL2','news1','news2','URL3','news1']

Каждый элемент URL имеет соответствующую последовательность элементов "новости", которые могут различаться по длине. В приведенном выше примере URL1 имеет 3 соответствующие новости, а URL3 - только одну.

Иногда URL не имеет соответствующего элемента "новости":

y = ['URL4','news1','news2','URL5','URL6','news1']

Я легко могу найти каждый индекс URL и элементы "новости" каждого URL.

У меня такой вопрос: Возможно ли преобразовать этот список в словарь, в котором элемент URL является ключом, а элементы "news" - значением списка/кортежа?

Ожидаемый результат

z = {'URL1':('news1', 'news2', 'news3'),
     'URL2':('news1', 'news2'),
     'URL3':('news1'),
     'URL4':('news1', 'news2'),
     'URL5':(),
     'URL6':('news1')}

Я видел похожий вопрос в этом сообщении, но он не решил мою проблему.

Ответы

Ответ 1

Вы можете сделать это так:

>>> y = ['URL4','news1','news2','URL5','URL6','news1']
>>> result = {}
>>> current_url = None
>>> for entry in y:
...     if entry.startswith('URL'):
...         current_url = entry
...         result[current_url] = ()
...     else:
...         result[current_url] += (entry, )
...         
>>> result
{'URL4': ('news1', 'news2'), 'URL5': (), 'URL6': ('news1',)}

Ответ 2

Вы можете использовать itertools.groupby с функцией key для идентификации URL:

from itertools import groupby
def _key(url):
    return url.startswith("URL") #in the body of _key, write code to identify a URL

data = ['URL1','news1','news2','news3','URL2','news1','news2','URL3','news1', 'URL4','news1','news2','URL5','URL6','news1']
new_d = [list(b) for _, b in groupby(data, key=_key)]
grouped = [[new_d[i], tuple(new_d[i+1])] for i in range(0, len(new_d), 2)]
result = dict([i for [*c, a], b in grouped for i in [(i, ()) for i in c]+[(a, b)]])

Выход:

{
 'URL1': ('news1', 'news2', 'news3'), 
 'URL2': ('news1', 'news2'), 
 'URL3': ('news1',), 
 'URL4': ('news1', 'news2'), 
 'URL5': (), 
 'URL6': ('news1',)
}

Ответ 3

Вы можете просто использовать индексы URL-ключей в списке и получить то, что находится между индексами, и назначить первый

Как это:

x = ['URL1','news1','news2','news3','URL2','news1','news2','URL3','news1']
urls = [x.index(y) for y in x if 'URL' in y]
adict = {}
for i in range(0, len(urls)):
    if i == len(urls)-1:
        adict[x[urls[i]]] = x[urls[i]+1:len(x)]
    else:
        adict[x[urls[i]]] = x[urls[i]+1:urls[i+1]]
print(adict)

выход:

{'URL1': ['news1', 'news2', 'news3'], 'URL2': ['news1', 'news2'], 'URL3': ['news1']}

Ответ 4

библиотека more-itertools содержит функцию split_before(), которая очень удобна для этой цели:

{s[0]: tuple(s[1:]) for s in mt.split_before(x, lambda e: e.startswith('URL'))}

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

Если ваш реальный вариант использования включает в себя реальные URL или что-то еще, а не строки вида URL#, просто замените lambda e: e.startswith('URL') любой функцией, которую вы можете использовать для выбора ключевых элементов, кроме элементов значения.

Ответ 5

Другое решение, использующее groupby, однострочное:

x = ['URL1','news1','news2','news3','URL2','news1','news2','URL3','news1', 'URL4','news1','news2','URL5','URL6','news1']

from itertools import groupby

out = {k: tuple(v) for _, (k, *v) in groupby(x, lambda k, d={'g':0}: (d.update(g=d['g']+1), d['g']) if k.startswith('URL') else (None, d['g']))}

from pprint import pprint
pprint(out)

Печать:

{'URL1': ('news1', 'news2', 'news3'),
 'URL2': ('news1', 'news2'),
 'URL3': ('news1',),
 'URL4': ('news1', 'news2'),
 'URL5': (),
 'URL6': ('news1',)}