Search code examples
pythonpython-3.xcomparison

Find the intersection of two lists of objects by using a comparison function


tl;dr:

I have two lists of class objects in Python (both are the same type), and when comparing them, I need to identify objects that exists in both lists by comparing them using a function that returns True or False.

I need to compare a car's tires to determine if the tire is a match ("it'll fit"), and sometimes if a tire is identical (same brand/make/year).

Given the example below, how can I compare the car's current tires to a list of potential tires I got back from the previous find call using the compare.is_exact_match function?

I've seen similar questions like this one which lets me compare objects based on the value of a property, but I'm not sure if (how) this same method could work for a function comparison.

The details:

I have a Car, and one of its properties is a set of tires (subclass Tire):

def Car:
    def __init__(self, ...args):
         self.make = make
         self.model = model
         self.year = year
         self.trim = trim
         self.color = color
         self.vin = vin
         self.tires: [Tire] = tires
         self.potential_matches: [Tire] = []

    def Tire:
        def __init__(self, ...args):
            self.brand = brand
            self.model_number = model_number
            self.year = year
            self.diameter = diameter
            self.width = width
            self.sidewall = sidewall
            self.circumference = self.circumference
            self.revs_per_mile = self.revs_per_mile

I have some a somewhat complicated heuristic that compares lists of cars to find matching tires for this car. This is a mock example, but let's assume this is a tire swapping marketplace so you understand how we go there. In this theoretical product, tire data in the marketplace is often incomplete. Determining if a tire is a match is expensive, so we do some prework to reduce that footprint. Once we have that, we often need to further compare tires to differentiate between "just a match" and "an identical tire".

This looks something like:

@classmethod
def find(cls, car: Car, marketplace_cars: [Car]) -> [Car.Tire]:
    matches = list(filter(lambda c:
         # One thing we can try is comparing the car first. If the car is a match,
         # the tires probably are too.
         car.make == c.make
         and car.model == c.model
         and car.year == c.year
         and car.trim == c.trim

         # If any of those fail, run the expensive heuristic
         and compare.is_match(car, c)
    marketplace_cars))

    # Extract a list of tires from the list of cars that were identified as having matches
    matching_tires = list(itertools.chain(*[m.tires for m in matches]))

    return matching_tires

Now from this list, we want to further narrow it down to show any tires that are identical matches. This means comparing make, model, and mileage as well as all the other stuff in the first comparison. The compare.is_exact_match takes individual Tire objects this time and compares them.

And this is where I'm lost:

How can I compare the car's current tires to a list of potential tires I got back from the previous find call using the compare.is_exact_match function?

@classmethod
def find_exact(cls, car: Car) -> [Car.Tire]:

    # Pseudo code, because what do I do here?
    # Should/can I do this with sets, or?
    exact_matches = list(filter(
                          lambda tire, match: compare.is_exact_match(tire, match), 
                          (car.tires, car.potential_matches)))

    # Returns an array of [Car.Tire] that are exact matches
    return exact_matches

Solution

  • It turns out I was really overthinking this, and a simple list comprehension was all I needed.

    @classmethod
    def find_exact(cls, car: Car) -> [Car.Tire]:
    
        exact_matches = [m for t in car.tires for m in car.potential_matches if compare.is_exact_match(t, m)]
        """
        for t in car.tires:
            for m in car.potential_matches:
                if compare.is_exact_match(t, m):
                    m
        """
    
        # Returns an array of [Car.Tire] that are exact matches
        return exact_matches