Right way to loop through list of dictionary with a dictionary in Python

Viewed 406

I have an list of vehicles and a dict of filters. The code is as follows:

The dictionary of filters is as follows:

filters = {
  'make': 'SKODA',
  'model': 'Fabia',
  'fuel': 'Diesel',
  'transmission': '',
  'registration_year': '',
  'price': {'start': 10000, 'stop': 12000}
}

And the list of vehicles is as follows:

vehicles = [
  {'make': 'AUDI', 'model': 'Q2 Dsl', 'type': '1.6 TDi Sport', 'fuel': 'Diesel', 'mileage': '19896', 'registration_year': '2017', 'transmission': 'Handbediende versnellingsbak', 'price': 17800}, 
  {'make': 'AUDI', 'model': 'A6 SW Dsl', 'type': '2.0 TDi S line', 'fuel': 'Diesel', 'mileage': '87354', 'registration_year': '2013', 'transmission': 'Handbediende versnellingsbak', 'price': 52000},
  {'make': 'SKODA', 'model': 'Fabia', 'type': '1.6 CR TDi GreenLine Active DPF', 'fuel': 'Diesel', 'mileage': '90613', 'registration_year': '2012', 'transmission': 'Handbediende versnellingsbak', 'price': 11000},
  {'make': 'AUDI', 'model': 'A4 SW Dsl', 'type': '2.0 TDi S tronic', 'fuel': 'Diesel', 'mileage': '47402', 'registration_year': '2016', 'transmission': 'Sequentiele bak', 'price': 93000},
  {'make': 'VOLKSWAGEN', 'model': 'Touran', 'type': '1.4 TSI Trendline', 'fuel': 'Essence', 'mileage': '28588', 'registration_year': '2017', 'transmission': 'Handbediende versnellingsbak', 'price': 87000},
  {'make': 'AUDI', 'model': 'A4 Dsl', 'type': '2.0 TDi', 'fuel': 'Diesel', 'mileage': '66053', 'registration_year': '2014', 'transmission': 'Handbediende versnellingsbak', 'price': 62000}
]

Thus, I want to loop through all vehicles and all filters and get back only those vehicles that match the filters. In this case I should get only Skoda.

I tried with pydash library, but without success.

What have I tried is as follows:

def filter_item(vehicle):
  vehicles = []
  for key, value in filters.items():
    if key == "price":
      if vehicle[key] > value['start'] and vehicle[key] < value['stop']:
        vehicles.append(vehicle)
    else:
      if vehicle[key] == value:
        vehicles.append(vehicle)
  return vehicles

result = py_.filter(vehicles, lambda x: filter_item(x))

But in this case I get all vehicles with fuel type Diesel, but I should get only the second one if we start from 0, thus only Skoda.

Any idea how can I do that? And is there a better way or cleaner code to do it?

2 Answers

you need to check agaist all filter conditions, here is an example with python builtin filter:

def filter_item(vehicle):
    vehicles = []
    for key, value in filters.items():
        if not value: # skip empty filter
            continue
        if key == "price":
            if vehicle[key] > value['start'] and vehicle[key] < value['stop']:
                continue
        else:
            if vehicle[key] == value:
                continue
        return False # no filter matched
    return True


result = filter(filter_item, vehicles)

with pydash:

result = py_.filter(vehicles, filter_item)

note: callback function passed to fitler returns true, to indicates the current value should include in the result.

I think the price range should be split off from filters. It's awkward in there because it's the only key that you can't directly match with the corresponding values for the same key of a vehicle.

I'd write it like this

>>> filters = {
...:  'make': 'SKODA',
...:  'model': 'Fabia',
...:  'fuel': 'Diesel',
...:  'transmission': '',
...:  'registration_year': '',
...:}
>>> price_range = range(10000, 12000 + 1)
>>>
>>> [vehicle['make'] for vehicle in vehicles if
...:all(vehicle.get(k) == v for k, v in filters.items() if v) and
...:vehicle['price'] in price_range]
>>> ['SKODA']

or like this

>>> [vehicle for vehicle in vehicles if
...:all(vehicle.get(k) == v for k, v in filters.items() if v) and
...:vehicle['price'] in price_range]
>>> 
[{'fuel': 'Diesel',
  'make': 'SKODA',
  'mileage': '90613',
  'model': 'Fabia',
  'price': 11000,
  'registration_year': '2012',
  'transmission': 'Handbediende versnellingsbak',
  'type': '1.6 CR TDi GreenLine Active DPF'}]

in case you want the full vehicle-dictionaries that match.

Related