Как добавить элемент в верхней части OrderedDict в python?
у меня есть это
d1 = OrderedDict([('a', '1'), ('b', '2')])
если я сделаю это
d1.update({'c':'3'})
тогда я получаю это
OrderedDict([('a', '1'), ('b', '2'), ('c', '3')])
но я хочу, чтобы этот
[('c', '3'), ('a', '1'), ('b', '2')]
без создания нового словаря
9 ответов
в Python 2 нет встроенного метода для этого. Если вам это нужно, вам нужно написать prepend()
метод/функция, которая работает на OrderedDict
внутренние с O (1) сложностью.
для Python 3.2 и более поздних, вы можете использовать move_to_end
1 метод. Метод принимает last
аргумент, который указывает, будет ли элемент перемещен вниз (last=True
) или верхней (last=False
) из OrderedDict
.
наконец, если вы хотите быстрый, грязный и медленно решение, вы можете просто создать новый OrderedDict
С нуля.
детали для четырех различных решений:
расширения OrderedDict
и добавьте новый метод экземпляра
from collections import OrderedDict
class MyOrderedDict(OrderedDict):
def prepend(self, key, value, dict_setitem=dict.__setitem__):
root = self._OrderedDict__root
first = root[1]
if key in self:
link = self._OrderedDict__map[key]
link_prev, link_next, _ = link
link_prev[1] = link_next
link_next[0] = link_prev
link[0] = root
link[1] = first
root[1] = first[0] = link
else:
root[1] = first[0] = self._OrderedDict__map[key] = [root, first, key]
dict_setitem(self, key, value)
демо:
>>> d = MyOrderedDict([('a', '1'), ('b', '2')])
>>> d
MyOrderedDict([('a', '1'), ('b', '2')])
>>> d.prepend('c', 100)
>>> d
MyOrderedDict([('c', 100), ('a', '1'), ('b', '2')])
>>> d.prepend('a', d['a'])
>>> d
MyOrderedDict([('a', '1'), ('c', 100), ('b', '2')])
>>> d.prepend('d', 200)
>>> d
MyOrderedDict([('d', 200), ('a', '1'), ('c', 100), ('b', '2')])
автономная функция, которая манипулирует OrderedDict
объекты
эта функция делает то же самое, принимая объект dict, ключ и значение. Я лично предпочитаю класс:
from collections import OrderedDict
def ordered_dict_prepend(dct, key, value, dict_setitem=dict.__setitem__):
root = dct._OrderedDict__root
first = root[1]
if key in dct:
link = dct._OrderedDict__map[key]
link_prev, link_next, _ = link
link_prev[1] = link_next
link_next[0] = link_prev
link[0] = root
link[1] = first
root[1] = first[0] = link
else:
root[1] = first[0] = dct._OrderedDict__map[key] = [root, first, key]
dict_setitem(dct, key, value)
демо:
>>> d = OrderedDict([('a', '1'), ('b', '2')])
>>> ordered_dict_prepend(d, 'c', 100)
>>> d
OrderedDict([('c', 100), ('a', '1'), ('b', '2')])
>>> ordered_dict_prepend(d, 'a', d['a'])
>>> d
OrderedDict([('a', '1'), ('c', 100), ('b', '2')])
>>> ordered_dict_prepend(d, 'd', 500)
>>> d
OrderedDict([('d', 500), ('a', '1'), ('c', 100), ('b', '2')])
использовать OrderedDict.move_to_end()
(Python >= 3.2)
1в Python 3.2 введен the OrderedDict.move_to_end()
метод. Используя его, мы можем переместить существующий ключ в любой конец словаря в O(1) времени.
>>> d1 = OrderedDict([('a', '1'), ('b', '2')])
>>> d1.update({'c':'3'})
>>> d1.move_to_end('c', last=False)
>>> d1
OrderedDict([('c', '3'), ('a', '1'), ('b', '2')])
если нам нужно вставить элемент и переместить его наверх, все в один шаг, мы можем напрямую использовать его для создания prepend()
фантик (не представленных здесь).
создать новый OrderedDict
- медленно!!!
если вы не хотите этого делать и производительность не является проблемой тогда самый простой способ-создать новый дикт:
from itertools import chain, ifilterfalse
from collections import OrderedDict
def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in ifilterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element
d1 = OrderedDict([('a', '1'), ('b', '2'),('c', 4)])
d2 = OrderedDict([('c', 3), ('e', 5)]) #dict containing items to be added at the front
new_dic = OrderedDict((k, d2.get(k, d1.get(k))) for k in \
unique_everseen(chain(d2, d1)))
print new_dic
выход:
OrderedDict([('c', 3), ('e', 5), ('a', '1'), ('b', '2')])
Я только что написал подкласс OrderedDict
в моем проекте для аналогичной цели. вот так.
операции вставки также являются неизменными времени O(1)
(они не требуют перестройки структуры данных), в отличие от большинства этих решений.
>>> d1 = ListDict([('a', '1'), ('b', '2')])
>>> d1.insert_before('a', ('c', 3))
>>> d1
ListDict([('c', 3), ('a', '1'), ('b', '2')])
вы должны сделать новый экземпляр OrderedDict
. Если ваши ключи уникальны:
d1=OrderedDict([("a",1),("b",2)])
d2=OrderedDict([("c",3),("d",99)])
both=OrderedDict(list(d2.items()) + list(d1.items()))
print(both)
#OrderedDict([('c', 3), ('d', 99), ('a', 1), ('b', 2)])
но если нет, остерегайтесь, как это поведение может быть или не может быть желательным для вас:
d1=OrderedDict([("a",1),("b",2)])
d2=OrderedDict([("c",3),("b",99)])
both=OrderedDict(list(d2.items()) + list(d1.items()))
print(both)
#OrderedDict([('c', 3), ('b', 2), ('a', 1)])
Если вы знаете, что вам понадобится ключ "c", но не знаете значения, вставьте " c " с фиктивным значением при создании dict.
d1 = OrderedDict([('c', None), ('a', '1'), ('b', '2')])
и изменить его позже.
d1['c'] = 3
теперь это возможно с move_to_end (key, last=True)
>>> d = OrderedDict.fromkeys('abcde')
>>> d.move_to_end('b')
>>> ''.join(d.keys())
'acdeb'
>>> d.move_to_end('b', last=False)
>>> ''.join(d.keys())
'bacde'
https://docs.python.org/3/library/collections.html#collections.OrderedDict.move_to_end
Если вам нужна функциональность, которой нет, просто расширьте класс тем, что вы хотите:
from collections import OrderedDict
class OrderedDictWithPrepend(OrderedDict):
def prepend(self, other):
ins = []
if hasattr(other, 'viewitems'):
other = other.viewitems()
for key, val in other:
if key in self:
self[key] = val
else:
ins.append((key, val))
if ins:
items = self.items()
self.clear()
self.update(ins)
self.update(items)
не очень эффективно, но работает:
o = OrderedDictWithPrepend()
o['a'] = 1
o['b'] = 2
print o
# OrderedDictWithPrepend([('a', 1), ('b', 2)])
o.prepend({'c': 3})
print o
# OrderedDictWithPrepend([('c', 3), ('a', 1), ('b', 2)])
o.prepend([('a',11),('d',55),('e',66)])
print o
# OrderedDictWithPrepend([('d', 55), ('e', 66), ('c', 3), ('a', 11), ('b', 2)])
Я бы предложил добавить prepend()
метод для этого чистого Python рецепт или вывод из него подкласса. Код для этого может быть довольно эффективным, учитывая, что базовая структура данных для заказа является связанным списком.
FWIW вот быстрый-N-грязный код, который я написал для вставки в произвольную позицию индекса. Не обязательно эффективный, но он работает на месте.
class OrderedDictInsert(OrderedDict):
def insert(self, index, key, value):
self[key] = value
for ii, k in enumerate(list(self.keys())):
if ii >= index and k != key:
self.move_to_end(k)
вы можете использовать другую структуру в целом, но есть способы сделать это в python 2.7.
d1 = OrderedDict([('a', '1'), ('b', '2')])
d2 = OrderedDict(c='3')
d2.update(d1)
d2 будет содержать
>>> d2
OrderedDict([('c', '3'), ('a', '1'), ('b', '2')])
а, в python 3.2 можно использовать OrderedDict.move_to_end('c', last=False)
для перемещения заданного ключа после вставки.
Примечание: примите во внимание, что первый вариант медленнее для больших наборов данных из-за создания нового OrderedDict и копирование старых значений.