Evan Zamir
Evan Zamir

Reputation: 8481

What is a good pattern for validating multiple complex conditions?

I'm working on a piece of code that needs to validate whether two users are "matched" under several different criteria. If it helps, think of this as a dating app, where we are trying to match people based on age, sexual preference, ethnicity preference, etc. Here's an example with 3 conditions, each of which is a function.

def is_match(row):
    return True \
        and ethnicity(user_a, user_b) \
        and sexual_orientation(user_a, user_b) \
        and age(user_a, user_b) \

Now, let's say I want to add another condition for proximity, I would just add it to the function:

def is_match(row):
    return True \
        and ethnicity(user_a, user_b) \
        and sexual_orientation(user_a, user_b) \
        and age(user_a, user_b) \
        and proximity(user_a, user_b)

Of course, this is feasible for a small application, but I can imagine a point where other co-workers may want to check out the code and pass their own conditions to it, and this doesn't seem abstract enough. I know there must be a pattern here to follow. Should I pass in each function as an array? How would you recommend doing this? I'm working in Python, but you can use any language you want to demonstrate the patterns.

Upvotes: 1

Views: 1738

Answers (3)

Boa
Boa

Reputation: 2677

def is_match(list_of_functions, user_a, user_b):
   return all([cur_fun(user_a, user_b) for cur_fun in list_of_functions])

Edit:

The following variant is more efficient, since it short-circuits the moment it hits a non-True value, rather than necessarily evaluating all of the functions:

def is_match(list_of_functions, user_a, user_b):
   for cur_fun in list_of_functions
       if not cur_fun(user_a, user_b):
            return False
   return True

Upvotes: 1

Nuncjo
Nuncjo

Reputation: 1340

I think sets are good for such tasks, because it's rather comparsion than validation. I will give You example:

user_a = {
    'ethnicity': 1,
    'sexual_orientation': 'straight',
    'age': 37,
}

user_b = {
    'ethnicity': 2,
    'sexual_orientation': 'straight',
    'age': 34,
}

differences = set(user_a.items()) ^ set(user_b.items()) # s.symmetric_difference(t)
commons = set(user_a.items()) & set(user_b.items()) # s.intersection(t)

print({'differences': differences, 'commons': commons})

output:

{'differences': {('ethnicity', 2), ('ethnicity', 1), ('age', 37), ('age', 34)}, 'commons': {('sexual_orientation', 'straight')}}

So You could just load data of two users to dicts and compare.

Upvotes: 2

Sievajet
Sievajet

Reputation: 3533

Checkout the Specification Pattern. This is exactly what you need. Its written in C# but can be easily converted

Upvotes: 1

Related Questions