Search code examples
pythonjsonlistfilterlist-manipulation

Filter objects in python, based on criteria in JSON


I have a relatively simple use case, where I need to load up the types of vehicles that fit a specific criteria. This criterion is shown in the "sample_input" variable.

Now I need to display cars that fit the correct criteria. I am reading my car database JSON and then creating car objects and adding them to my carDatabase list, from this list I need to select cars that fit the user's request.

So essentially what happens is this I have loaded a simple sample_input variable in my Main.Py

The code should do this but it doesn't (better implementations are always welcome)

Check if Name is empty []; if it is, move on; if not, search for cars with the same name, move to the next criterion which is country of origin, check if it is empty, if it is not look for cars with the Name(s) and country of origins(s), and etc etc for the rest of criteria.

import json

from Vehicle import Vehicle

sample_input = {
    "Name": [],
    "country of origin": ["france", "uk"],
    "transmission": [],
    "body type": ["coupe"],
    "drive type": [],
    "doors": [],
    "fuel type": []
}


def test():
    with open("data.json") as omapper:
        data = json.load(omapper)
        Database= []
        for i in data["vehicles"]:
            name = i["Name"]
            origin = i["country of origin"]
            transmission = i["transmission"]
            bodytype = i["body type"]
            drivetype = i["drive type"]
            doors = i["doors"]
            fueltype = i["fuel type"]
            vehicle= Vehicle(name,origin,transmission,bodytype,drivetype,doors,fueltype)
            Database.append(car)

    UserOutput = []

    for vehicles in Database:
        if vehicles.origin in sample_input["country of origin"] and vehicles.body_type in sample_input["body type"] and vehicles.doors in sample_input["doors"] != "":
            print(vehicle.name)


if __name__ == "__main__":
   test()

Vehicle.py: Model class

 class Vehicle:
        def __init__(self, name, origin, transmission, body_type, drive_type, doors, fuel_type):
            self.name = name
            self.origin = origin
            self.transmission = transmission
            self.body_type = body_type
            self.drive_type = drive_type
            self.doors = doors
            self.fuel_type = fuel_type

Vehicle JSON: All the vehicles my application supports.

  {
    "attributes": {
        "country of origin": ["japan", "america", "germany", "south korea", "italy", "sweden"],
        "transmission": ["automatic", "manual"],
        "body type": ["hatchback", "sedan", "SUV", "ute", "coupe", "convertible", "van"],
        "drive type": ["RWD", "FWD", "4WD"],
        "doors": ["4 door", "2 door"],
        "fuel type": ["petrol", "diesel", "electric", "hybrid"]
    },
    "vehicles": [
    {
        "Name": "studebaker dictator",
        "country of origin": "america",
        "transmission": "manual",
        "body type": "sedan",
        "drive type": "RWD",
        "doors": "2 door",
        "fuel type": "petrol"
    },
    {
        "Name": "mitsubishi zero",
        "country of origin": "japan",
        "transmission": "manual",
        "body type": "hatchback",
        "drive type": "FWD",
        "doors": "2 door",
        "fuel type": "petrol"
    },
   ...
    ]
    }

Solution

  • You just need to spell out the conditions you already laid out in your prose exposition.

    for vehicle in Database:
        if (not sample_input["country of origin"] or vehicle.origin in sample_input["country of origin"]) and (
            not sample_input["transmission"] or vehicle.transmission in sample_input["transmission"]) and (
            not sample_input["body type"] or vehicle.body_type in sample_input["body type"]
        ):
            print(vehicle.name)
    

    not sample_input["x"] will be True if sample_input["x"] is an empty list; if it is not, we check that vehicle.x is in the list.

    Note also how I changed the loop variable to the singular form; we are examining one vehicle at a time.

    If you need to be able to access each possible attribute in a loop, try something like

    class Vehicle:
        # ...
        def attr_map(self, label):
            """
            Map a label like 'country of origin' to
            the corresponding internal attribute,
            and return that.
            """
            return {
                'country of origin': self.origin,
                'transmission': self.transmission,
                'body type': self.body_type,
                # ... etc
            }[label]
    

    and then just loop over the attributes you want:

    for vehicle in Database:
        vehicle_selected= True
        for attr in sample_input:
            if sample_input[attr] and vehicle.attr_map(attr) not in sample_input[attr]:
                vehicle_selected = False
                break
        if vehicle_selected:
            print(vehicle.name)
    

    You could do something similar with the i in your JSON import loop if you wanted to; then the method to map a key to an attribute isn't necessary, and you can simply check ... and i[attr] in sample_input[attr]