1. ホーム
  2. python

[解決済み] OrderedDictの先頭に要素を追加するには?

2022-11-24 12:15:13

質問

こんなものがあります。

d1 = OrderedDict([('a', '1'), ('b', '2')])

こうすれば

d1.update({'c':'3'})

すると、こうなる。

OrderedDict([('a', '1'), ('b', '2'), ('c', '3')])

が、これが欲しい。

[('c', '3'), ('a', '1'), ('b', '2')]

新しい辞書を作成せずに

どのように解決するのですか?

Python 2にはこれを行うためのビルトインメソッドがありません。これが必要な場合は prepend() を操作するメソッド/関数を書く必要があります。 OrderedDict の内部を O(1) の複雑さで操作するメソッドです。

Python 3.2 以降では が必要です。 を使用します。 move_to_end メソッドを使用します。このメソッドでは last 引数は、要素を一番下に移動させるかどうかを指定します ( last=True ) か、それとも一番上 ( last=False ) の OrderedDict .

最後に、もしあなたが迅速で汚い、そして 遅い 解決策としては、単に新しい OrderedDict をゼロから作成すればよいのです。

4種類のソリューションの詳細です。


拡張 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)

Python 3.2 の導入 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 - 遅い!!!

それが嫌な方や パフォーマンスが問題でない場合 であれば、最も簡単な方法は新しいdictを作成することです。

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