Range as dictionary key in Python
<p>So, I had an idea that I could use a range of numbers as a key for a single value in a dictionary.</p>

<p>I wrote the code bellow, but I cannot get it to work.  Is it even possible?</p>

<pre><code>    stealth_roll = randint(1, 20)
    # select from a dictionary of 4 responses using one of four ranges.
    ## not working.
    stealth_check = {
                    range(1, 6) : 'You are about as stealthy as thunderstorm.',
                    range(6, 11) : 'You tip-toe through the crowd of walkers, while loudly calling them names.',
                    range(11, 16) : 'You are quiet, and deliberate, but still you smell.',
                    range(16, 20) : 'You move like a ninja, but attracting a handful of walkers was inevitable.'
                    }

    print stealth_check[stealth_roll]
</code></pre>

<p>Yes, you can, only if you convert your <code>range</code> lists as immutable <code>tuple</code>, so they are hashable and accepted as keys of your dictionary:</p>

<pre><code>stealth_check = {
                tuple(range(1, 6)) : 'You are about as stealthy as thunderstorm.',
</code></pre>

<p>EDIT: actually it works in Python 3 as <code>range</code> is an immutable sequence type and generate an immutable <code>tuple</code> instead of a <code>list</code> as L3viathan stated.</p>

<p>but you cannot access them with a single integer as key though. Your last line won't work.</p>

<p>I took some time to create a solution which would work whatever the values may be (picking one entry in the dictionary works as long as the lines are not "weighted" by bigger ranges.</p>

<p>It calls <code>bisect</code> on the sorted keys to find the insertion point, hacks it a bit, and finds the best value in the dictionary, with <code>O(log(N))</code> complexity, which means it can handle a really big list (maybe a little too much here :) but the dictionary is also too much in that case)</p>

<pre><code>from random import randint
import bisect

stealth_roll = randint(1, 20)
# select from a dictionary of 4 responses using one of four thresholds.

stealth_check = {
                1 : 'You are about as stealthy as thunderstorm.',
                6 : 'You tip-toe through the crowd of walkers, while loudly calling them names.',
                11 : 'You are quiet, and deliberate, but still you smell.',
                16 : 'You move like a ninja, but attracting a handful of walkers was inevitable.'
                }

sorted_keys = sorted(stealth_check.keys())


insertion_point = bisect.bisect_left(sorted_keys,stealth_roll)

# adjust, as bisect returns not exactly what we want
if insertion_point==len(sorted_keys) or sorted_keys[insertion_point]!=stealth_roll:
    insertion_point-=1

print(insertion_point,stealth_roll,stealth_check[sorted_keys[insertion_point]])
</code></pre>

python dictionary range