2014-12-28 88 views
3

我有一個列表,每個都有兩個鍵/值對。我需要將第一個鍵的共享相同值的字典合併爲第二個鍵的值。例如:按屬性聚合一個對象數組

[ 
    {'foo': 34, 'bar': 2}, 
    {'foo': 34, 'bar': 3}, 
    {'foo': 35, 'bar': 1}, 
    {'foo': 35, 'bar': 7}, 
    {'foo': 35, 'bar': 2} 
] 

會出來爲:

[ 
    {'foo': 34, 'bar': 5}, 
    {'foo': 35, 'bar': 10} 
] 

我寫了下面的函數,它的工作原理,但似乎可怕囉嗦了,我幾乎可以肯定有一個很酷的Python的把戲,這將是更清潔,更高性能。

def combine(arr): 
    arr_out = [] 
    if arr: 
     arr_out.append({'foo': arr[0]['foo'], 'bar': 0}) 
     for i in range(len(arr)): 
      if arr[i]['foo'] == arr_out[-1]['foo']: 
       arr_out[-1]['bar'] += arr[i]['bar'] 
      else: 
       arr_out.append({'foo': arr[i]['foo'], 'bar': arr[i]['bar']}) 
    return arr_out 

任何人有任何建議嗎?

+1

[itertools.groupby](https://docs.python.org/2/library/itertools.html#itertools.groupby) –

+0

我選擇了fortheye的回答,因爲在我的timeit測試中,它的執行效率提高了約30%在我可能的陣列大小上。儘管如此,我懷疑對於更大的陣列,差異可能會更小。有趣的是,我的原始代碼在兩者之間執行,儘管它是冗長的,正如我所說的。 – domoarrigato

+0

@domoarrigato我懷疑時差是因爲'groupby'的輸入應該是'sorted',即O(lg N)。但是我的字典解決方案只是O(N)。 – thefourtheye

回答

3
  1. 組基礎上,foobar值和添加。

    >>> grouper = {} 
    >>> for d in data: 
    ...  grouper[d["foo"]] = grouper.get(d["foo"], 0) + d["bar"] 
    ... 
    >>> grouper 
    {34: 5, 35: 10} 
    
  2. 然後重建與列表解析http://stardict.sourceforge.net/Dictionaries.php下載的列表中,像這樣

    >>> [{"foo": item, "bar": grouper[item]} for item in grouper] 
    [{'foo': 34, 'bar': 5}, {'foo': 35, 'bar': 10}] 
    
5

使用itertools.groupby

>>> arr = [ 
...  {'foo': 34, 'bar': 2}, 
...  {'foo': 34, 'bar': 3}, 
...  {'foo': 35, 'bar': 1}, 
...  {'foo': 35, 'bar': 7}, 
...  {'foo': 35, 'bar': 2} 
... ] 
>>> import itertools 
>>> key = lambda d: d['foo'] 
>>> [{'foo': key, 'bar': sum(d['bar'] for d in grp)} 
... for key, grp in itertools.groupby(sorted(arr, key=key), key=key)] 
[{'foo': 34, 'bar': 5}, {'foo': 35, 'bar': 10}] 

如果列表已經排序,則可以省略sorted電話:

>>> [{'foo': key, 'bar': sum(d['bar'] for d in grp)} 
... for key, grp in itertools.groupby(arr, key=key)] 
[{'foo': 34, 'bar': 5}, {'foo': 35, 'bar': 10}] 
0

此解決方案使用collections.defaultdict

def combine(arr): 
    c = collections.defaultdict(int) 
    for i in arr: 
     c[i['foo']] += i['bar'] 
    # c == {34: 5, 35: 10} 

    return [{'foo': k, 'bar': c[k]} for k in sorted(c)] 

字典c是defaultdict,與'foo'的值作爲鍵,'bar'的值作爲值。