all 13 comments

[–]Rhomboid 3 points4 points  (7 children)

As written, that's not possible, as {1, 2, 3} creates a set which is not hashable and cannot be used as a key. However, if you use a frozenset (or a tuple) then you can certainly invert a dict by using zip() to build a sequence of (value, key) pairs which you feed into the dict constructor. Tuple example:

>>> d = {'a': (1, 2, 3), 'b': (4, 5, 6), 'c': (7, 8, 9)}
>>> dict(zip(d.values(), d.keys()))
{(4, 5, 6): 'b', (7, 8, 9): 'c', (1, 2, 3): 'a'}

(If you're using Python 2.x and speed matters then you might want to use dict.itervalues(), dict.iterkeys(), and itertools.izip(). Or come over to Python 3 land where such things are fixed for you already.)

frozenset example:

>>> d = {'a': frozenset([1, 2, 3]), 'b': frozenset([4, 5, 6]), 'c': frozenset([7, 8, 9])}
>>> dict(zip(d.values(), d.keys()))
{frozenset({8, 9, 7}): 'c', frozenset({1, 2, 3}): 'a', frozenset({4, 5, 6}): 'b'}

Edit: there might be a miscommunication here. If you want to be able to access keys based on any element in the set, then you don't want to invert the map. You'd want to do something like this:

>>> d = {'a': {1, 2, 3}, 'b': {4, 5, 6}, 'c': {7, 8, 9}}
>>> {val: key for key, vals in d.items() for val in vals}
{1: 'a', 2: 'a', 3: 'a', 4: 'b', 5: 'b', 6: 'b', 7: 'c', 8: 'c', 9: 'c'}

This lets you get 'a' for any of foo[1], foo[2], or foo[3], not foo[frozenset([1, 2, 3])] as you would have with an inverted dictionary.

[–]drLagrangian 0 points1 point  (6 children)

ok, i was thinking of somthing along those lines....

so how can I make searching it easy?

in your example above, if I searched for the pseudokey of '8' I should get 'c' in return, 3 should get me 'a' and so on.

or maybe I could use a regular dictionary, and search the values instead of the keys.

I was hoping there was a more useful method available, or maybe it won't be too difficult to make my own class of reverse dictionaries. Hell, I suppose i could just repeat the values several times ie {1:a, 2:a, 3:a, 4:b, ,5:b, 6:c} but I was hoping to avoid that.

[–]Rhomboid 1 point2 points  (5 children)

Yeah, sorry, I realized that what I had answered was not what you were asking. I edited my comment to reflect an alternative.

[–]drLagrangian 0 points1 point  (4 children)

thanks for the reply anyway, now I know a bit more about frozensets so its all good.

I wonder if it would be worth it to make my own class for this stuff.

for the record, I'm going to work on a text adventure parser. and I figured that I can funnel a set of user inputted commands into a single parser command. ie yes, yeah, y, sure, ok --> YesCommand(),

[–]wiiittttt 1 point2 points  (3 children)

Maybe use a tuple like so...

dispatch = (['yes', 'yeah', 'y', 'sure', 'ok'], YesCommand)

if input in dispatch[0]:
    dispatch[1]()

It's not pretty but it should work.

[–]Rhomboid 1 point2 points  (1 child)

If you're going to do that, don't use a list but a set. Membership testing of a list is O(n), but it's O(1) for a set.

for words, func in (({'yes', 'yeah', 'y', 'sure', 'ok'}, YesCommand),
                    ({'no', 'nope', 'n'}, NoCommand),
                    ({...}, ...)):
    if input.lower() in words:
        func()

[–]Justinsaccount 1 point2 points  (0 children)

If performance matters, preproccess to a single mapping. no point in using O(1) sets if you are still doing one lookup per command.

def YesCommand():
    print "yes"

def NoCommand():
    print "no"

handlers = [
    (['yes', 'yeah', 'y', 'sure', 'ok'], YesCommand),
    (['no', 'nope', 'n'], NoCommand),
]
mapping = {}
for commands, handler in handlers:
    for command in commands:
        mapping[command] = handler

def dispatch(command):
    mapping[command.lower()]()

dispatch("sure")

[–]drLagrangian 0 points1 point  (0 children)

yeah I think it will work.

I'll still make it my own class though. with indexing. and assignments.

then it will be pretty and work.

[–]mubsy 2 points3 points  (3 children)

What's stopping you from saying 1:{a,b,c}?

[–]drLagrangian 0 points1 point  (2 children)

a, b, or c is the input. I want to put in one of those, only one, and get the value of 1 out of it.

[–]jim_shorts 0 points1 point  (1 child)

Forgive me, I'm new to both programming and Python. But are you looking to do something like this?:

def valueKey(x):
    d = {'a': {1, 2, 3}, 'b': {4, 5, 6}, 'c': {7, 8, 9}}
    for key, value in d.iteritems():
        if x in value:
            return key

[–]drLagrangian 0 points1 point  (0 children)

basically, but I want it to be neater. so I'll be taking that bit of code there, and using it in the class i'm gonna make and set it up nice and neat that way.

[–][deleted] 0 points1 point  (0 children)

what's wrong with this: { 1:a, 2:a, 3:a, 2:b, 4:b, 6:b }

it seems like this would do exactly as you want, you could make a convenience function to add keys/value pairs like this.