21

I have a snippet of code which orders a dictionary alphabetically. Is there a way to select the ith key in the ordered dictionary and return its corresponding value? i.e.

import collections
initial = dict(a=1, b=2, c=2, d=1, e=3)
ordered_dict = collections.OrderedDict(sorted(initial.items(), key=lambda t: t[0]))
print(ordered_dict)

OrderedDict([('a', 1), ('b', 2), ('c', 2), ('d', 1), ('e', 3)])

I want to have some function along the vein of...

select = int(input("Input dictionary index"))
#User inputs 2
#Program looks up the 2nd entry in ordered_dict (c in this case)
#And then returns the value of c (2 in this case)

How can this be achieved? Thanks.

(Similar to Accessing Items In a ordereddict, but I only want to output the value of the key-value pair.)

Community
  • 1
  • 1
Pingk
  • 532
  • 2
  • 7
  • 17

5 Answers5

26

In Python 2:

If you want to access the key:

>>> ordered_dict = OrderedDict([('a', 1), ('b', 2), ('c', 2), ('d', 1), ('e', 3)])
>>> ordered_dict.keys()[2]
'c'

If want to access the value:

>>> ordered_dict.values()[2]
2

If you're using Python 3, you can convert the KeysView object returned by the keys method by wrapping it as a list:

>>> list(ordered_dict.keys())[2]
'c'
>>> list(ordered_dict.values())[2]
2

Not the prettiest solution, but it works.

Daniel Holmes
  • 1,952
  • 2
  • 17
  • 28
Daniel Lee
  • 2,030
  • 1
  • 23
  • 29
12

Using itertools.islice is efficient here, because we don't have to create any intermediate lists, for the sake of subscripting.

from itertools import islice
print(next(islice(ordered_dict.items(), 2, None)))

If you want just the value, you can do

print ordered_dict[next(islice(ordered_dict, 2, None))]
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
  • Ahh, not quite what I want. Your method returns the key-value pair, but I only want the code to return the _value of the key_ – Pingk Mar 24 '14 at 13:49
  • +1. `islice` is preferable but for a small dictionary `list(ordered_dict.values())[2]` might be faster i.e., don't assume what is faster until you've measured it – jfs Mar 24 '14 at 13:55
  • @JFSebastian, Interesting, what makes islice preferable for small dictionaries? Just the logic behind the code or something else? – Pingk Mar 24 '14 at 14:01
  • 1
    @Pingk He says, `islice` is preferable but not for small dicts – thefourtheye Mar 24 '14 at 14:03
  • Sorry, I read it wrong, but what makes it faster/slower compared to list? @thefourtheye – Pingk Mar 24 '14 at 15:35
  • is it really faster to use the key to index instead of using islice over `.values`? – Janus Troelsen Feb 28 '18 at 08:18
6

Do you have to use an OrderedDict or do you just want a dict-like type that supports indexing? If the latter, then consider a sorted dict object. Some implementations of SortedDict (which orders pairs based on the key sort order) support fast n-th indexing. For example, the sortedcontainers project has a SortedDict type with random-access indexing.

In your case it would look something like:

>>> from sortedcontainers import SortedDict
>>> sorted_dict = SortedDict(a=1, b=2, c=2, d=1, e=3)
>>> print sorted_dict.iloc[2]
'c'

If you do a lot of lookups, this will be a lot faster than repeatedly iterating to the desired index.

GrantJ
  • 8,162
  • 3
  • 52
  • 46
  • Thanks very much, and I may use this in the future, but the method I've logged as the correct answer does a very similar method to this. I'm curious though, will the dictionary stay ordered if I change the value of one of the k-v pairs, or will it re-arrange itself like the standard dictionary? – Pingk Apr 14 '14 at 11:10
  • Yes, it will automatically stay sorted by the keys. It'll be much faster than the accepted solution if your making edits to the dictionary and then indexing. – GrantJ Apr 14 '14 at 17:44
  • Awesome, I'll probably use this next time then. I don't think I'll change the accepted answer, but I'll vote you up. – Pingk Apr 17 '14 at 22:16
1

You could do something along these lines (od is the ordered dict):

def get_idx(od, idx):
   from itertools import islice
   idx = (idx + len(od)) % len(od)
   t = islice(od.items(), idx, idx + 1)
   return next(t)

>>>x

OrderedDict([('a', 2), ('b', 3), ('k', 23), ('t', 41), ('q', 23)])

>>>get_idx(x, 1)
('b', 3)
>>>get_idx(x, 2)
('k', 23)
>>>get_idx(x, 4)
('q', 23)
>>>get_idx(x, -1)
('q', 23)
>>>get_idx(x, -2)
('t', 41)
Lin Endian
  • 93
  • 3
  • 9
0

Don't underestimate just a plain 'ole for loop:

from collections import OrderedDict

od=OrderedDict([('a', 1), ('b', 2), ('c', 2), ('d', 1), ('e', 3)])

def ith(od, tgt):
    for i, t in enumerate(od.items()):
        if i==tgt:
            print('element {}\'s key is "{}"'.format(i,t[0]))
            break
    else:
        print('element {} not found'.format(tgt)) 

ith(od, 2)
# element 2's key is "c"
ith(od, 20) 
# element 20 not found

The advantage here is that the loop will break as soon as the desired element is found and returns a sensible result if not found...

The disadvantage is that relative slices are not supported.

dawg
  • 98,345
  • 23
  • 131
  • 206