0

Can I use switch case alternative for below scenario?

If x is any one of 5, 6, 7, 8, I want to return 0, if x is any one of 9, 10, 11, 12, I want to return 1, if x is any one of 13, 14, 15, 16, I want to return 2, if x is any one of 17, 18, 19, 20, I want to return 3.

I came across something like this for single value, but not sure how I can use similar syntax for above scenario.

def group(x):
 return {
  [5, 6, 7, 8]: 0,
  [9, 10, 11, 12]: 1,
  [13, 14, 15, 16]: 2,
  [17, 18, 19, 20]: 3,
  }.get(x, "Invalid")

Can someone please help me to conclude if I can use above way or only if else is the option?

Mohd
  • 5,523
  • 7
  • 19
  • 30
  • Possible duplicate of [Replacements for switch statement in Python?](https://stackoverflow.com/questions/60208/replacements-for-switch-statement-in-python) – wwii Aug 05 '17 at 04:47

3 Answers3

4

You can use tuples as keys for the dictionary and then use a generator to get the value or return 'Invalid' if the number isn't in any of the keys, for example:

def group(x):
    d = {(5, 6, 7, 8): 0, (9, 10, 11, 12): 1, (13, 14, 15, 16): 2, (17, 18, 19, 20): 3}
    return next((d[i] for i in d if x in i), 'Invalid')

print group(11)
print group(17)

next() will find the next key that has x in it and return the value

output:

1
3
Mohd
  • 5,523
  • 7
  • 19
  • 30
  • Not that it will matter a whole lot in most cases, but you should use sets instead of tuples for the the keys: `d = {{5, 6, 7, 8}: 0, {9, 10, 11, 12}: 1, ...`. This will give you O(1) lookups vs. O(n). See here: https://wiki.python.org/moin/TimeComplexity –  Aug 05 '17 at 03:47
  • 1
    but how can sets be dictionary keys, aren't `sets` unhashable type. – Bijoy Aug 05 '17 at 03:52
  • 1
    @Bijoy One can use `frozenset`s: `d = {frozenset({5, 6, 7, 8}): 0...}` – DYZ Aug 05 '17 at 03:54
  • @Bijoy Yes, normal sets are unhashable and therefore can't be used as dictionary keys – Mohd Aug 05 '17 at 03:56
  • 1
    Yea, my bad. I was thinking of `frozenset`s but said the other. Time for bed I guess. :) –  Aug 05 '17 at 03:56
  • or `return next((v for g, v in d.items() if x in g))`. Also you can use an optional second argument to `next` for a default value (in case of StopIteration). `next(..., "Invalid")` – Adam Smith Aug 05 '17 at 03:58
  • @AdamSmith `next(..., 'Invalid')` looks better, ill update the answer thanks! – Mohd Aug 05 '17 at 04:06
  • Sure this works, but it severely defeats the purpose of a dict. The moment you have to involve iteration with a dict for lookup, you know you're doing something wrong. – cs95 Aug 05 '17 at 04:09
3

Lists cannot serve as dictionary keys. Create a separate dictionary item for each key:

return {5: 0, 6: 0, 7: 0, 8: 0, 9: 1, ..., 12: 1, ...}.get(x, "Invalid")
DYZ
  • 55,249
  • 10
  • 64
  • 93
  • I wanted to avoid that, so that if there is any alternative to do it which I haven't thought of... –  Aug 05 '17 at 03:37
  • @DYZ if you ignore the arithmetic solution, yes ;) – Adam Smith Aug 05 '17 at 04:01
  • 1
    @AdamSmith The arithmetic solution is very specific to these particular numbers. – DYZ Aug 05 '17 at 04:06
  • This is the best solution in terms of the fact that it respects the use of a dict, and works because the ranges (appear to be) mutually exclusive. – cs95 Aug 05 '17 at 04:10
  • @Sweety You should seriously consider this as the best possible solution for your problem. – cs95 Aug 05 '17 at 04:11
1

You can use arithmetic for this

def group(x):
    """5-8   -> 0
       9-12  -> 1
       13-16 -> 2
       17-20 -> 3
    """
    # scale down our starting values, so 5-8 -> 0-3
    return (x-5) // 4

If you want a generalized solution to this for any arbitrary grouping -> return value pairings, you'll need to write a function factory.

def selector(groups, return_values, fallback=None):
    if len(groups) != len(return_values):
        raise ValueError("groups and return_values must have equal length")
    def wrapped(needle):
        nonlocal groups, return_values, fallback
        for grp, retval in zip(groups, return_values):
            if needle in grp:
                return retval
        # if no matches
        return fallback
    return wrapped

Then you can do:

group = selector([(5, 6, 7, 8), (9, 10, 11, 12),
                  (13, 14, 15, 16), (17, 18, 19, 20)],
                 [0, 1, 2, 3],
                 fallback="Invalid")
group(3)
Adam Smith
  • 52,157
  • 12
  • 73
  • 112