Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to map a series of conditions as keys in a dictionary?

I know you can use a dictionary as an alternative to a switch statement such as the following:

def printMessage(mystring):
    # Switch statement without a dictionary
    if mystring == "helloworld":
        print "say hello"
    elif mystring == "byeworld":
        print "say bye"
    elif mystring == "goodafternoonworld":
        print "good afternoon"


def printMessage(mystring):
    # Dictionary equivalent of a switch statement
    myDictionary = {"helloworld": "say hello",
                    "byeworld": "say bye",
                    "goodafternoonworld": "good afternoon"}
    print myDictionary[mystring]

However if conditions are used, other than equality (==) which return true of false these cant be mapped as easily i.e.:

if i > 0.5:
    print "greater than 0.5"
elif i == 5:
    print "it is equal to 5"
elif i > 5 and i < 6:
    print "somewhere between 5 and 6"

The above cannot be directly converted to a dictionary key-value pair as is:

# this does not work
mydictionary  = { i > 0.5: "greater than 0.5" }

A lambda can be used since it is hash-able but the only way to get the resulting string out of the map is by passing the same lambda object into the dictionary and not when the evaluation of the lambda is true:

x = lambda i: i > 0.5
mydictionary[x] = "greater than 0.5"
# you can get the string by doing this:
mydictionary[x]
# which doesnt result in the evaluation of x

# however a lambda is a hashable item in a dictionary
mydictionary = {lambda i: i > 0.5: "greater than 0.5"}

Does anyone know of a technique or method to create a mapping between a lambda evaluation and a return value? (this maybe similar to pattern matching in functional language)

like image 375
Har Avatar asked Apr 03 '15 14:04

Har


People also ask

Can you have a list as a key in a dictionary?

A dictionary or a list cannot be a key. Values, on the other hand, can literally be anything and they can be used more than once.

How do you create a dictionary with multiple values per key?

In python, if we want a dictionary in which one key has multiple values, then we need to associate an object with each key as value. This value object should be capable of having various values inside it. We can either use a tuple or a list as a value in the dictionary to associate multiple values with a key.

How do you iterate keys in a dictionary?

There are two ways of iterating through a Python dictionary object. One is to fetch associated value for each key in keys() list. There is also items() method of dictionary object which returns list of tuples, each tuple having key and value.

Can a dictionary have same key multiple times?

If you want to keep duplicate keys in a dictionary, you have two or more different values that you want to associate with same key in dictionary. The dictionary can not have the same keys, but we can achieve a similar effect by keeping multiple values for a key in the dictionary.


2 Answers

Your conditions are sequential in nature; you want to test one after the other, not map a small number of keys to a value here. Changing the order of the conditions could alter the outcome; a value of 5 results in "greater than 0.5" in your sample, not "it is equal to 5".

Use a list of tuples:

myconditions  = [
    (lambda i: i > 0.5, "greater than 0.5"),
    (lambda i: i == 5, "it is equal to 5"),
    (lambda i: i > 5 and i < 6, "somewhere between 5 and 6"),
]

after which you can access each one in turn until one matches:

for test, message in myconditions:
    if test(i):
        return message

Re-ordering the tests will change the outcome.

A dictionary works for your first example because there is a simple equality test against multiple static values that is optimised by a dictionary, but there are no such simple equalities available here.

like image 190
Martijn Pieters Avatar answered Oct 06 '22 00:10

Martijn Pieters


You can't use a dictionary to map arbitrary conditionals since more than one of them could be true at the same time. Instead you need to evaluate each one sequentially and execute the associated code the first time a true one is encountered. Here's an outline of one way to formally implement something like that which even allows the equivalent of a default: case.

from collections import namedtuple

Case = namedtuple('Case', ['condition', 'code'])

cases = (Case('i > 0.5',
            """print 'greater than 0.5'"""),

         Case('i == 5',
            """print 'it is equal to 5'"""),

         Case('i > 5 and i < 6',
            """print 'somewhere between 5 and 6'"""))

def switch(cases, **namespace):
    for case in cases:
        if eval(case.condition, namespace):
            exec(case.code, namespace)
            break
    else:
        print 'default case'

switch(cases, i=5)

Output:

greater than 0.5
like image 31
martineau Avatar answered Oct 06 '22 01:10

martineau