1
\$\begingroup\$

results is a list of dict having panel name and work type and price to do that work on that panel. There can be two dicts for same name and work type where one of them is on car model level and another is on car type level. In such cases we would give preference to car model level dict.

output is the group of panels and prices per work type (removing the prices on car type if car model price exist for same panel and work type combination).

results = sorted(results, key=lambda datum: datum['car_model'])

#removing the car type if car model exists.
d = defaultdict(dict)
for l in results:
    d[l['name']+'-'+l['work_type']] = l

results = d.values()

#grouping the results at panel level.
d = defaultdict(dict)
for l in results:
    d.setdefault(l['name'],[]).append(l)

#creating dict such that panel name is key and price_list is array of all the panel prices.
output = []
for key,value in d.iteritems():
    output.append({"name":key,"price_list":value})

Example input:

results = [
        {
            "id":1,
            "car_type":1,
            "car_model":None,
            "name":"Door",
            "work_type":"Dent",
            "price":2300,
        },
        {
            "id":2,
            "car_type":1,
            "car_model":None,
            "name":"Door",
            "work_type":"Scratch",
            "price":1200,
        },
        {
            "id":3,
            "car_type":None,
            "car_model":2,
            "name":"Door",
            "work_type":"Dent",
            "price":2500,
        },
        {
            "id":4,
            "car_type":1,
            "car_model":None,
            "name":"Fender",
            "work_type":"Dent",
            "price":2300,
        },
        {
            "id":5,
            "car_type":1,
            "car_model":None,
            "name":"Fender",
            "work_type":"Scratch",
            "price":1300,
        }
    ]

Expected output:

[{'name': 'Fender',
  'price_list': [{'car_model': None,
    'car_type': 1,
    'id': 5,
    'name': 'Fender',
    'price': 1300,
    'work_type': 'Scratch'},
   {'car_model': None,
    'car_type': 1,
    'id': 4,
    'name': 'Fender',
    'price': 2300,
    'work_type': 'Dent'}]},
 {'name': 'Door',
  'price_list': [{'car_model': 2,
    'car_type': None,
    'id': 3,
    'name': 'Door',
    'price': 2500,
    'work_type': 'Dent'},
   {'car_model': None,
    'car_type': 1,
    'id': 2,
    'name': 'Door',
    'price': 1200,
    'work_type': 'Scratch'}]}]
\$\endgroup\$
3
  • \$\begingroup\$ Yes. The output is expected output but I have removed few fields from output to make it short. Updated the question with correct output. \$\endgroup\$
    – Anuj
    Commented May 19, 2016 at 10:55
  • \$\begingroup\$ Won't defining car_model as None result in an error when doing results = sorted(results, key=lambda datum: datum['car_model']) ? \$\endgroup\$ Commented May 19, 2016 at 13:12
  • \$\begingroup\$ Not in python 2.7. Although it will give error in Python 3 \$\endgroup\$
    – Anuj
    Commented May 19, 2016 at 14:21

1 Answer 1

3
\$\begingroup\$

General comments

results = sorted(results, key=lambda datum: datum['car_model'])

Good use of sorted!

#removing the car type if car model exists.
d = defaultdict(dict)
for l in results:
    d[l['name']+'-'+l['work_type']] = l

results = d.values()

Why are you using a defaultdict? A normal dict suffices.

#grouping the results at panel level.
d = defaultdict(dict)
for l in results:
    d.setdefault(l['name'],[]).append(l)

Again, why a defaultdict, a normal dict suffices.

#creating dict such that panel name is key and price_list is array of all the panel prices.
output = []
for key,value in d.iteritems():
    output.append({"name":key,"price_list":value})

Good enough, I suppose. Alternatively, use

output = [{"name": key, "price_list": value} for key, value in d.iteritems()]

Variable naming

Variable names such as d and l just don't cut it anymore. Please use descriptive variable names.

Revised code

results = sorted(results, key=lambda datum: datum['car_model'])

# Make sure results are unique by {name}-{work_type}
results = {l['name'] + '-' + l['work_type']: l for l in results}.values()

# Grouping the results at panel level.
d = {}
for l in results:
    d.setdefault(l['name'],[]).append(l)

# Creating dict such that panel name is key and price_list is array of all the panel prices.
output = [{"name": key, "price_list": value} for key, value in d.iteritems()]

Separate by functions

I see several actions: getting unique values, grouping by a key, and finally outputting it.

Getting unique values

def unique_by(lst, key=lambda x: x):
    return {key(v): v for v in lst}.values()

You can call it like

results = unique_by(results, key=lambda l: l['name'] + '-' + l['work_type'])

or better

results = unique_by(results, key=lambda l: (l['name'], l['work_type']))

and using operator.itemgetter:

results = unique_by(results, key=itemgetter('name', 'work_type'))

Grouping by a key

def group_by(lst, key=None):
    assert key is not None, 'You must supply a key function'
    retval = {}
    for v in lst:
        retval.setdefault(key(v), []).append(v)
    return retval

You can call it like

grouped = group_by(results, key=lambda l: l['name'])

or

grouped = group_by(results, key=itemgetter('name'))

Revised, again

from operator import itemgetter

results = sorted(results, key=itemgetter('car_model'))
results = unique_by(results, key=itemgetter('name', 'work_type'))
grouped = group_by(results, key=itemgetter('name'))
output = [{"name": name, "price_list": items} for name, items in grouped.iteritems()]

(where unique_by and group_by are defined as above.)

Note: untested, but it should work (modulo syntax errors).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.