web-dev-qa-db-ja.com

リストに追加するdefaultdictのワンステップ初期化?

defaultdictを次の行に沿って初期化できれば便利です。

d = defaultdict(list, (('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2),
   ('b', 3)))

生産する

defaultdict(<type 'list'>, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]})

代わりに、私は

defaultdict(<type 'list'>, {'a': 2, 'c': 3, 'b': 3, 'd': 4})

私が必要なものを手に入れるために、私はこれをしなければならないことになります:

d = defaultdict(list)
for x, y in (('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), ('b', 3)):
    d[x].append(y)

これは必要以上にIMOですが、ここで何かが足りませんか?

15
iruvar

あなたが説明する振る舞いは、defaultdictsの他の振る舞いと一致しません。あなたが欲しいものはFooDictのようです

>>> f = FooDict()
>>> f['a'] = 1
>>> f['a'] = 2
>>> f['a']
[1, 2]

それはできますが、defaultdictではできません。それをAppendDictと呼びましょう

import collections

class AppendDict(collections.MutableMapping):
    def __init__(self, container=list, append=None, pairs=()):
        self.container = collections.defaultdict(container)
        self.append = append or list.append
        for key, value in pairs:
            self[key] = value

    def __setitem__(self, key, value):
        self.append(self.container[key], value)

    def __getitem__(self, key): return self.container[key]
    def __delitem__(self, key): del self.container[key]
    def __iter__(self): return iter(self.container)
    def __len__(self): return len(self.container)

明らかに欠けているのは、defaultdictdictの単純な(特に「魔法の」ではない)サブクラスであるということです。最初の引数はすべて、missingキーのファクトリ関数を提供することです。 defaultdictを初期化すると、dictが初期化されます。

制作したい場合

defaultdict(<type 'list'>, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]})

値がリストである他のdictを初期化するのと同じ方法で初期化する必要があります。

d = defaultdict(list, (('a', [1, 2]), ('b', [2, 3]), ('c', [3]), ('d', [4])))

初期データが、2番目の要素が常に整数であるタプルの形式である必要がある場合は、forループを使用します。あなたはそれをもう1つのステップと呼びます。私はそれを明確で明白な方法と呼んでいます。

16
John Y

並べ替えとitertools.groupby長い道のり:

>>> L = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), ('b', 3)]
>>> L.sort(key=lambda t:t[0])
>>> d = defaultdict(list, [(tup[0], [t[1] for t in tup[1]]) for tup in itertools.groupby(L, key=lambda t: t[0])])
>>> d
defaultdict(<type 'list'>, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]})

これをワンライナーにするために:

L = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), ('b', 3)]
d = defaultdict(list, [(tup[0], [t[1] for t in tup[1]]) for tup in itertools.groupby(sorted(L, key=operator.itemgetter(0)), key=lambda t: t[0])])

お役に立てれば

4
inspectorG4dget

私はこれのほとんどが単純な for ループを避けるためにたくさんの煙と鏡だと思います:

di={}
for k,v in [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2),('b', 3)]:
    di.setdefault(k,[]).append(v)
# di={'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]}

あなたの目標が1行であり、私がまったく支持またはサポートできない虐待的な構文が必要な場合は、副作用の理解を使用できます。

>>> li=[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2),('b', 3)]
>>> di={};{di.setdefault(k[0],[]).append(k[1]) for k in li}
set([None])
>>> di
{'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]}

あなたが本当に読みにくいところに行きたい場合:

>>> {k1:[e for _,e in v1] for k1,v1 in {k:filter(lambda x: x[0]==k,li) for k,v in li}.items()}
{'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]}

あなたはそれをしたくありません。 forループのルークを使用してください!

3
dawg
>>> kvs = [(1,2), (2,3), (1,3)]
>>> reduce(
...   lambda d,(k,v): d[k].append(v) or d,
...   kvs,
...   defaultdict(list))
defaultdict(<type 'list'>, {1: [2, 3], 2: [3]})
1
user471651