Я знаю, что нужно написать что-то простое и медленное с циклом, но мне нужно, чтобы это работало очень быстро в больших масштабах.
вход:
lst = [[1, 1, 2], ["txt1", "txt2", "txt3"]]
желаемый результат:
d = {1 : ["txt1", "txt2"], 2 : "txt3"]
В python есть что-то встроенное, что делает ключ расширения dict()
вместо его замены?
dict(list(zip(lst[0], lst[1])))
Вы должен хотите ['txt3']
, а не 'txt3'
. Подробности смотрите в моем ответе.
Один из вариантов — использовать dict.setdefault
:
out = {}
for k, v in zip(*lst):
out.setdefault(k, []).append(v)
Выход:
{1: ['txt1', 'txt2'], 2: ['txt3']}
Если вам нужен сам элемент для одноэлементных списков, одним из способов является добавление условия, которое проверяет его при создании выходного словаря:
out = {}
for k,v in zip(*lst):
if k in out:
if isinstance(out[k], list):
out[k].append(v)
else:
out[k] = [out[k], v]
else:
out[k] = v
или если lst[0]
отсортировано (как в вашем образце), вы можете использовать itertools.groupby
:
from itertools import groupby
out = {}
pos = 0
for k, v in groupby(lst[0]):
length = len([*v])
if length > 1:
out[k] = lst[1][pos:pos+length]
else:
out[k] = lst[1][pos]
pos += length
Выход:
{1: ['txt1', 'txt2'], 2: 'txt3'}
Но, как отмечает @timgeb, это, вероятно, не то, что вам нужно, потому что впоследствии вам придется проверять тип данных каждый раз, когда вы обращаетесь к этому словарю (если значение является списком или нет), что является ненужной проблемой, которую вы могли бы избежать, имеющие все значения в виде списков.
Обратите внимание, что OP не нужны списки с одним элементом. Что ошибочно, конечно.
@timgeb Спасибо, что указали на это; и я согласен с вашим комментарием
Вы можете использовать defaultdict
, чтобы сгруппировать строки по соответствующему ключу, а затем сделать второй проход по списку, чтобы извлечь строки из одноэлементных списков. Независимо от того, что вы делаете, вам нужно получить доступ к каждому элементу в обоих списках хотя бы один раз, поэтому необходима структура итерации некоторый (и даже если вы не используете итерацию явно, все, что вы используете, почти наверняка будет использовать итерацию под капотом ):
from collections import defaultdict
lst = [[1, 1, 2], ["txt1", "txt2", "txt3"]]
result = defaultdict(list)
for key, value in zip(lst[0], lst[1]):
result[key].append(value)
for key in result:
if len(result[key]) == 1:
result[key] = result[key][0]
print(dict(result)) # Prints {1: ['txt1', 'txt2'], 2: 'txt3'}
Обратите внимание, что OP не нужны списки с одним элементом. Что ошибочно, конечно.
Отредактировано, спасибо, что указали на это.
Если вы имеете дело с большими наборами данных, может быть полезно добавить решение pandas
.
>>> import pandas as pd
>>> lst = [[1, 1, 2], ["txt1", "txt2", "txt3"]]
>>> s = pd.Series(lst[1], index=lst[0])
>>> s
1 txt1
1 txt2
2 txt3
>>> s.groupby(level=0).apply(list).to_dict()
{1: ['txt1', 'txt2'], 2: ['txt3']}
Обратите внимание, что это также создает списки для отдельных элементов (например, ['txt3']
), которые я настоятельно рекомендую. Наличие как списков, так и строк в качестве возможных значений будем приводит к ошибкам, поскольку оба эти типа являются итерируемыми. Вам нужно будет не забывать проверять тип каждый раз, когда вы обрабатываете значение dict.
нет никакого способа позволить этому «работать очень быстро в больших масштабах». - вам нужно перебрать все элементы, вам нужно создать внутренние списки, вам нужно либо проверить/добавить, либо использовать что-то вроде defaultdict.