Blabber
Blabber

Reputation: 349

Python list of dictionaries containing lists comprehension

I have a list of dictionary containing lists:

a = [{'alpha': 'a', 'val': 10, 'num': ['one', 'two']},
     {'alpha': 'b', 'val': 22, 'num': ['two']},
     {'alpha': 'c', 'val': 1, 'num': ['seven']},
     {'alpha': 'a', 'val': 10, 'num': ['three','nine']},
     {'alpha': 'b', 'val': 9, 'num': ['two', 'four']}]  

The output I want is:

[{'alpha': 'a', 'TotalVal': 20, num: ['one', 'two', 'three', 'nine'], 'numlen': 4}, 
 {'alpha': 'b', 'TotalVal': 31, num: ['two', 'four'], 'numlen': 2}, 
 {'alpha': 'c', 'val': 1, 'num': ['seven'], 'numlen': 1}]  

I have tried the following:

sumVal = collections.defaultdict(float)
for info in a:
    sumVal[info['alpha']] += info['val']

sumVal = [{'alpha': c, 'TotalVal': sumVal[c]} for c in sumVal]  

numList = collections.defaultdict(list)
for info in a:
    numList[info['alpha']].append(info['num'])
numList = [{'alpha': k, 'num': set(v), 'len': len(set(v))} for k, v in numList.items()]  

def merge_lists(l1, l2, key):
    merged = {}
    for item in l1+l2:
        if item[key] in merged:
            merged[item[key]].update(item)
        else:
            merged[item[key]] = item
    return [val for (_, val) in merged.items()]

final = merge_lists(sumVal, numList, 'alpha')  

I do not get the desired output for numList. Get the following error.

TypeError: unhashable type: 'list'

How can I get the desired output in lesser number of steps and get rid of the error?

Upvotes: 0

Views: 132

Answers (5)

Tonechas
Tonechas

Reputation: 13743

Here's the simplest solution I came up with:

a = [{'alpha':'a','val':10,'num':['one','two']},
     {'alpha':'b','val':22,'num':['two']},
     {'alpha':'c','val':1,'num':['seven']},
     {'alpha':'a','val':10,'num':['three','nine']},
     {'alpha':'b','val':9,'num':['two','four']}] 

a2 = []
alphas = set(d['alpha'] for d in a)
for alpha in alphas:
    TotalVal, num, numlen = 0, set(), 0
    for d in a:
        if d['alpha'] == alpha:
            TotalVal += d['val']
            num = num | set(d['num'])
            numlen += 1
    new_dict = {'alpha': alpha, 'num': list(num), 'numlen': numlen}
    if numlen > 1:
        new_dict['TotalVal'] = TotalVal
    else:
        new_dict['val'] = TotalVal
    a2.append(new_dict)

Demo:

>>> for d in a2: print(d)
{'alpha': 'a', 'num': ['three', 'nine', 'two', 'one'], 'numlen': 2, 'TotalVal': 20}
{'alpha': 'c', 'num': ['seven'], 'numlen': 1, 'val': 1}
{'alpha': 'b', 'num': ['four', 'two'], 'numlen': 2, 'TotalVal': 31}

Upvotes: 1

Stan Zeez
Stan Zeez

Reputation: 1188

#!/usr/bin/env python

a = [{'alpha':'a','val':10,'num':['one','two', 'one', 'two']},{'alpha':'b','val':22,'num':['two']},{'alpha':'c','val':1,'num':['seven']},{'alpha':'a','val':10,'num':['three','nine']},{'alpha':'b','val':9,'num':['two','four']}]


def merge_lists(src, key):
    merged = {}
    for i in src:
        _key = i[key]
        if _key in merged:
            merged[_key]['TotalVal'] += i['val']
            merged[_key]['num'].extend(i['num'])
            merged[_key]['num'] = list(set(i['num']))
            merged[_key]['numlen'] = len(merged[_key]['num'])
        else:
            merged[_key] = {'TotalVal': i['val'], 'alpha': i['alpha'], 'num': i['num'], 'numlen': 1}

    return [val for (_, val) in merged.items()]


final = merge_lists(a, 'alpha')

print(final)

Output: [{'alpha': 'a', 'TotalVal': 20, 'num': ['nine', 'three'], 'numlen': 2}, {'alpha': 'c', 'TotalVal': 1, 'num': ['seven'], 'numlen': 1}, {'alpha': 'b', 'TotalVal': 31, 'num': ['four', 'two'], 'numlen': 2}]

Upvotes: 1

frist
frist

Reputation: 1958

Try this code:

a = [{'alpha':'a','val':10,'num':['one','two']},{'alpha':'b','val':22,'num':['two']},{'alpha':'c','val':1,'num':['seven']},{'alpha':'a','val':10,'num':['three','nine']},{'alpha':'b','val':9,'num':['two','four']}]

def merge_dicts(x, y):
    x.update(y)
    return x

def r(acc, x):
    if x['alpha'] in acc:
        acc[x['alpha']]['TotalVal'] += x['val']
        acc[x['alpha']]['num'] |= set(x['num'])
        acc[x['alpha']]['numlen'] = len(acc[x['alpha']]['num'])
    else:
        acc[x['alpha']] = {
            'TotalVal': x['val'],
            'num': set(x['num']),
            'numlen': len(set(x['num'])),
        }
    return acc

result = map(lambda (x, y): merge_dicts({'alpha': x}, y),
             reduce(r, a, {}).iteritems())
print(result)

Upvotes: 1

Wokpak
Wokpak

Reputation: 603

Not as short as other answers but simple in its implementation

a = [{'alpha':'a','val':10,'num':['one','two']},
    {'alpha':'b','val':22,'num':['two']},
    {'alpha':'c','val':1,'num':['seven']},
    {'alpha':'a','val':10,'num':['three','nine']},
    {'alpha':'b','val':9,'num':['two','four']}] 

new_list = []

# Loop through entries
for entry in a:
    # Store first entries
    if entry['alpha'] not in [i['alpha'] for i in new_list]:
        new_dict = {'alpha': entry['alpha'],
                    'TotalVal': entry['val'],
                    'num': entry['num'],
                    'numlen': len(entry['num'])}
        new_list.append(new_dict)
        continue

    # Add in additional entries
    for i, n in enumerate(new_list):
        if n['alpha'] == entry['alpha']:
            entry_vals = entry.values()
            new_list[i]['TotalVal'] = new_list[i]['TotalVal'] + entry['val']
            new_list[i]['num'] = new_list[i]['num'] + entry['num']
            new_list[i]['numlen'] = len(new_list[i]['num'])

# filter final data
for i, n in enumerate(new_list):
    # Remove duplicate entries in num
    for entry in n['num']:
        if n['num'].count(entry) > 1:
            new_list[i]['num'].remove(entry)

    # Update numlen
    new_list[i]['numlen'] = len(new_list[i]['num'])

print new_list

Upvotes: 1

beauxq
beauxq

Reputation: 1286

The problem is in this line:

numList[info['alpha']].append(info['num'])

appending a list onto a list, puts the list that you are appending inside the list you are appending to.

I think what you want is extend.

append vs. extend

Upvotes: 1

Related Questions