2016-07-05 2 views
-1

У меня есть отображение как следующийсписок Subsorting отображений

mapping = [ 
    "key1", 
    "key2", 
    "key3", 
    "key4", 
    "key5#1", 
    "key5#4", 
    "key5#2", 
    "key5#3", 
    "key6#1", 
    "key6#2" 
] 

и список списков, например [['val1', 'val2', 'val3', 'val4', 'val5', 'val6', 'val7', 'val8', 'val9' 'val10']], и я хочу закончить список словарей, например.

[{ 
    "key1": "val1", 
    "key2": "val2", 
    "key3": "val3", 
    "key4": "val4", 
    "key5": ["val5", "val7", "val8", "val6"], 
    "key6": ["val9", "val10"], 
}] 

Такое, что каждый ключ без # просто 1 значения, в то время как ключи с # имеет упорядоченный список отсортирован по целому числу после #. То, что я сейчас:

for i in range(len(mapping)): 
    if '#' in mapping[i]: 
     result.setdefault(mapping[i].split('#')[0], []).append(row[i]) 
    else: 
     result[mapping[i]] = row[i] 

Однако это не сортирует значения в соответствии с числом после #.

+0

Будет ли заказ после # импортировать или применять сортировку в конце? – Destrif

+0

Он должен быть отсортирован по целому числу после '#', а не соответствующему значению – YnkDK

+0

Думал, что вы могли бы это сделать, но не ... Это зависит от порядка ввода элемента result.setdefault (mysplit [0], []). insert (mysplit [1] -1, row [i]) – Destrif

ответ

2

Здесь вы можете использовать itertools.groupby. Идея состоит в том, чтобы создать диктовку с использованием ключей от сопоставления, и изначально каждый ключ будет инициализирован в список None размером с число повторений ключа.

from itertools import groupby 
dct = {} 
for k, g in groupby(mapping, lambda x: x.split('#')[0]): 
    length = sum(1 for _ in g) 
    dct[k] = [None] * length 
... 

Теперь dct выглядит следующим образом:

>>> dct 
{'key3': [None], 
'key2': [None], 
'key1': [None], 
'key6': [None, None], 
'key5': [None, None, None, None], 
'key4': [None]} 

Обратите внимание, что если отображение не сортируется уже тогда мы можем отсортировать его с помощью: mapping.sort(key=lambda x: x.split('#')[0]). Это сгруппирует похожие клавиши вместе.

Вышеуказанная вещь также может быть выполнена путем первого определения количества каждой клавиши, а затем инициализации dict с использованием счета. Таким образом, это можно сделать в O (N) раз, если аналогичные ключи в mapping уже не сгруппированы.

>>> from collections import Counter 
>>> dct = {k: [None] * v for k, v in Counter(
     key.split('#')[0] for key in mapping).items()} 

Теперь мы можем цикл по отображению и перечня и значений обновления в dct соответственно:

for key, value in zip(mapping, lst[0]): 
    if '#' not in key: 
     dct[key] = value 
    else: 
     key, index = key.split('#') 
     # Simply assign the value to the index 
     dct[key][int(index)-1] = value 
... 
>>> dct 
{'key3': 'val3', 
'key2': 'val2', 
'key1': 'val1', 
'key6': ['val9', 'val10'], 
'key5': ['val5', 'val7', 'val8', 'val6'], 
'key4': 'val4'} 
+0

Если я сортирую сопоставление, тогда было бы трудно определить сопоставление с другими списками, не так ли? – YnkDK

+0

@YnkDK Вы имели в виду порядок в выходе dict? –

+0

Это именно то, что я искал. Я проверил его с 'groupby', как было предложено первым. В чем преимущество использования «Counter»? – YnkDK

0

Это то, что вы просите?

for i, key in enumerate(mapping): 
    if '#' in key: 
     key, order = key.split('#') 
     result.setdefault(key, []).insert(int(order) - 1, row[i]) 
    else: 
     result[key] = row[i] 

Примечания:

  1. Использование enumerate - for i in range(len(something)) считается плохой привычкой
  2. Кастинг order к int требуется Python3 - вставка принимает INT, строка не будет работать.
+1

Вы не можете, если последний элемент пришел первым, вы не сможете его добавить (вне допустимого диапазона) – Destrif

+1

Кажется, что @Destrif прав. Если я попробую 'key5 # 4, key5 # 3, key5 # 2, key5 # 1', я не получаю правильный заказ – YnkDK

+1

Вы правы! Оставляя мой ответ в качестве предупреждения для других, чтобы не повторять мои ошибки, как говорят другие, 'itertools.groupby' кажется правильным инструментом для работы. – Nee

0

Звучит как работа за groupby и список разрезаний. Обратите внимание, что вам не хватает запятой в row после val9.

def split_key(k): 
    if '#' in k: 
     return k.split('#')[0] 
    else: 
     return k 

def group_keys(keys): 
    for K, G in groupby(keys, key=split_key): 
     yield split_key(K), sum(1 for g in G) 

def slice_row(mapping, row): 
    i = 0 
    for key, length in group_keys(mapping): 
     if length == 1: 
      yield key, row[i] 
     else: 
      yield key, row[i:i + length] 
     i += length 

print(dict(slice_row(mapping, row[0]))) 

# >>> {'key4': 'val4', 'key6': ['val9', 'val10'], 'key5': ['val5', 'val6', 'val7', 'val8'], 'key3': 'val3', 'key1': 'val1', 'key2': 'val2'} 
Смежные вопросы