Python: Dictionary where key is tuple of unknown order

Question:

I am trying to find a way to create a dictionary so that I can map a list of values to an appropriate function.

For example, I have something that looks like:

function_map = {('Put','Put','Call','Call'): FunctionOne,
                 ('Put','Call','Combo'): FunctionTwo,
                 ('Call','Put'):FunctionThree,
                 ('Put','Combo'):FunctionFour}

What I’m struggling with is how to deal with can a lookup value that can be in any permutation of the key.

For example, I might try to do function_map[(Call, Put, Call, Put)], and I would want this to return FunctionOne. Or function_map[(Combo, Put)] I would want FunctionFour returned.

Any help is appreciated!

Asked By: keynesiancross

||

Answers:

A solution to your provided example could be something like this:

from itertools import permutations

function_map = {1: FunctionOne,
                2: FunctionTwo,
                3: FunctionThree,
                4: FunctionFour}

permutations_map = {1: list(permutations(('Put', 'Put', 'Call', 'Call'))),
                    2: list(permutations(('Put', 'Call', 'Combo'))),
                    3: list(permutations(('Call', 'Put'))),
                    4: list(permutations(('Put', 'Combo')))}


def get_function(perm: tuple):
    for key, value in permutations_map.items():
        if perm in value:
            return function_map[key]


function_output = get_function(('Call', 'Put', 'Call', 'Put'))()

where function_output is the output of FunctionOne.

If you run this code very frequently, the accepted answer is suboptimal because it computes the permutations each time you call it (making it slower), while this is just a lookup.

Answered By: Kyriakos Psarakis

Try this:

import itertools as it

function_map = {('Put','Put','Call','Call'): FunctionOne,
                ('Put','Call','Combo'): FunctionTwo,
                ('Call','Put'): FunctionThree,
                ('Put','Combo'): FunctionFour}

def func_map(x):
    for k, v in function_map.items():
        if x in it.permutations(k):
            return v
    return False

func_map(('Combo', 'Put'))()

(Goes into FunctionFour)

Answered By: The Thonnu

If the value of each dictionary key can be unordered, you can sort each tuple to ensure the order is consistent.

Example

function_map = {
    ('Call', 'Call', 'Put', 'Put'): FunctionOne,
    ('Call', 'Combo', 'Put'): FunctionTwo,
    ('Call', 'Put'): FunctionThree,
    ('Combo', 'Put'): FunctionFour
}

In order to access the values of the dictionary, I recommend you to use .get instead of accessing via [key]. It avoids unexpected errors while trying to access not existing keys.

For your examples, you may call with the following statements:

function_map.get(tuple(sorted(('Call', 'Put', 'Call', 'Put'))))
# output: <function __main__.FunctionOne>

function_map.get(tuple(sorted(('Combo', 'Put'))))
# output: <function __main__.FunctionFour>

The output while accessing not existing key

function_map.get("not existing key")
#output: <None>

# If you need an other values while accessing not existing key
function_map.get("not existing key", "default value")
# output: 'default value'
Answered By: luangtatipsy

@The Thonnu‘s answer is memory efficient but lookup complexity is fairly high. The simple alternative which I’ve mentioned in this comment is to map all possible permutations of every tuple to same function:

from itertools import permutations

function_map = (
    dict.fromkeys(permutations(('Put', 'Put', 'Call', 'Call')), FunctionOne) |
    dict.fromkeys(permutations(('Put', 'Put', 'Combo')), FunctionTwo) |
    dict.fromkeys(permutations(('Call', 'Put')), FunctionThree) |
    dict.fromkeys(permutations(('Put','Combo')), FunctionFour)
)
function_map['Call', 'Put', 'Call', 'Put']()

This option takes more time to generate mapping (around 10 times slower than regular dict initialization) but it makes each lookup significantly faster (at least 10 times faster than lookup with function, benchmark code).

So if you will continuously search for function in this mapping, my answer is preferable.

Answered By: Olvin Roght

Another alternative solution is to use .get(). It will solve your problem.

function_map.get(('Put','Combo'))

If you want to execute the function try:

function_map.get(('Put','Combo'))()
Answered By: Asad Hussain
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.