Function to test for different conditions in input data I have the following function that checks a data record to see if it has met certain criteria specified separately in configuration settings. The configurations are set up as a list of dictionaries with certain trigger keywords that are used by the condition check function ('in','not in', etc).

The condition checker has a lot of duplicated code, but it's just different enough that a better setup is not obvious to me. I would appreciate any feedback.

Here is a minimal working example to illustrate:

def check_if_condition_was_met( row, condition ):
    condition_met = True
    for key, val in condition.iteritems():
        if key == 'in':
            for condition_key, condition_val in val.iteritems():
                if row[condition_key] in condition_val:  # IN
                    continue
                else:
                    condition_met = False
                    break
        elif key == 'not in':               
            for condition_key, condition_val in val.iteritems():
                if row[condition_key] not in condition_val:  # NOT IN
                    continue
                else:
                    condition_met = False
                    break
        elif key == 'max':         
            for condition_key, condition_val in val.iteritems():
                if not row[condition_key]:
                    condition_met = False
                    break
                if int(row[condition_key]) &lt;= int(condition_val): # MAX (&lt;=)
                    continue
                else:
                    condition_met = False
                    break
        elif key == 'min':
            for condition_key, condition_val in val.iteritems():
                if int(row[condition_key]) &gt;= int(condition_val): # MIN (&gt;=)
                    continue
                else:
                    condition_met = False
                    break
    return condition_met


if __name__ == '__main__':
    # data
    test_data = [
         {'Flag1':'Y', 'Flag2':'Canada','Number':35}
        ,{'Flag1':'Y', 'Flag2':'United States','Number':35}
        ,{'Flag1':'N', 'Flag2':'United States','Number':35}
        ,{'Flag1':'N', 'Flag2':'England','Number':35}
        ,{'Flag1':'N', 'Flag2':'Canada','Number':35}
        ,{'Flag1':'N', 'Flag2':'Canada','Number':5}
    ]

    # configuration
    test_conditions = [
         { 'in':{'Flag1':['N'], 'Flag2':['United States']} }
        ,{ 'in':{'Flag1':['Y'],'Flag2':['Canada']}, 'max':{'Number':7} }
        ,{ 'in':{'Flag1':['Y'],'Flag2':['Canada']}, 'min':{'Number':7} }
        ,{ 'not in':{'Flag1':['Y']}, 'min':{'Number':7} }
    ]

    for condition_id, condition in enumerate(test_conditions):
        print
        print 'now testing for condition %i' % condition_id
        for data_id, data in enumerate(test_data):
            print '%s | %s' % ( data_id, check_if_condition_was_met(data,condition) )
Similar to @Peilonrayz, I would map each of the comparators to a function, but I'd go further.


Function design: check_if_condition_was_met(…) is an awkward name, partly because of the unusual use of past tense.  But why make it so wordy?  Couldn't you just call it verify(…)?

Furthermore, it would be customary to put the condition parameter first, and the row second.  It would certainly read more naturally in English, especially after the function rename.  Also, based on the observation that verify(condition) could be considered as a test, it's a general functional-programming principle that the condition parameter should be considered more tightly associated with the verification process and should therefore be put first.
Use all(…):  You want to express the idea that a function should return True if all of the conditions must be met, and False if any condition fails.  You can do that using all() with a generator expression.   It's a lot less cumbersome than your condition_met, for, continue, and break.  The entire function can be simplified down to a single expression!
Naming:  I think that "key" and "val" are not quite descriptive enough.  I suggest the following renamings:

key → comparator
val → criteria
condition_key → key
condition_val → desired_val


I'm also skeptical about some of the behaviour when given anomalous input:


Why are there int(…) casts with 'min' and 'max'?  Is it for parsing strings as numbers?  None of your example cases needs such parsing, though.  Is it for truncating floats towards zero?  Probably not, but it might have that unintended effect, if the thresholds or data are already numeric.
What happens if the row is missing a key that is specified in the condition?  Maybe it's not a concern to you, but it might be more appropriate to have the function return False rather than raise a KeyError, as your code does?


Consider writing doctests to explain what the function does.  This is a situation where examples are more expressive than words.

Suggested solution

COMPARATORS = {
    'in':       lambda v, lst: v in lst,
    'not in':   lambda v, lst: v not in lst,
    'min':      lambda v, n: (v is not None) and (v &gt;= n),
    'max':      lambda v, n: (v is not None) and (v &lt;= n),
}

def verify(condition, row):
    """
    Verify that the specified criteria are all true for the given row.

    &gt;&gt;&gt; rows = [
    ...     {'Flag1':'Y', 'Flag2':'Canada',        'Number':35},
    ...     {'Flag1':'Y', 'Flag2':'United States', 'Number':35},
    ...     {'Flag1':'N', 'Flag2':'United States', 'Number':35},
    ...     {'Flag1':'N', 'Flag2':'England',       'Number':35},
    ...     {'Flag1':'N', 'Flag2':'Canada',        'Number':35},
    ...     {'Flag1':'N', 'Flag2':'Canada',        'Number':5},
    ... ]

    &gt;&gt;&gt; [verify({'in': {'Flag1': ['N'], 'Flag2': ['United States']}}, r)
    ...  for r in rows]
    [False, False, True, False, False, False]

    &gt;&gt;&gt; [verify({'not in': {'Flag1': ['Y']}, 'min': {'Number': 7}}, r)
    ...  for r in rows]
    [False, False, True, True, True, False]

    &gt;&gt;&gt; [verify({'not in': {'Blah': ['whatever']}}, r) for r in rows]
    [True, True, True, True, True, True]
    """
    return all(
        all(
            COMPARATORS[comparator](row.get(key), desired_val)
            for key, desired_val in criteria.iteritems()
        )
        for comparator, criteria in condition.iteritems()
    )