6

In python 2 when using an OrderedDict I was able to get the keys in the order they were inserted by simply using the keys method that returned a list. In python 3 however:

rows = OrderedDict()
rows[0]=[1,2,3]
rows[1]=[1,2,3]
image = [rows[k] for k in rows.keys()[:2]]

I get:

Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: 'odict_keys' object is not subscriptable

I certainly can do list(rows)[:2] as advised for instance here - but is this guaranteed to get me the keys in order ? Is this the one right way ?

UPDATE: the python 2 code would be better as:

image = [v for v in rows.values()[:2]]

which of course still blows with the same error on python 3

Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361
  • 4
    Yes, doing `list(rows)` is the correct way to get the keys in order since that uses `__iter__` and `__next__` which are guaranteed to be ordered. If you only need the first 2 keys, you might want to consider something like `itertools.islice(rows, 2)` instead of `list(rows)[:2]` ... – mgilson May 24 '17 at 00:27
  • Thanks @mgilson - not sure what would be faster - copying a small list or using the itertools module - maybe try an answer ? :) – Mr_and_Mrs_D May 24 '17 at 00:32

1 Answers1

3

@mglison is right. Because OrderedDict follows the iterator protocol, it is guaranteed to yield the keys in the correct order to list():

However, like @mglison also mentioned,itertools.islice() would be better and more efficent than simply using list and subscripting. After timing both methods multiple times using the timeit module, the itertools method was roughly 2x faster. Here are the exact numbers:

After getting the average time of running both list(itertools.islice(rows, 2)) and list(rows.keys())[:2](notice I'm casting the result of islice() to a list) multiple times, it appears that using the latter method is actual slightly faster:

---------------------------------------------------------------
|  list(itertools.islice(rows, 2))   |    1.5023668839901838  |      
---------------------------------------------------------------
|  list(rows.keys())[:2]             |    1.495460570215706   |      
---------------------------------------------------------------

However, the difference of the two numbers is so small, I really would not make much of difference which method you use. Just choose the method which is the most readable and understandable for your specific case.

Christian Dean
  • 22,138
  • 7
  • 54
  • 87
  • 1
    If that's exactly what you did, I don't think it's a fair comparison: `itertools.islice(rows, 2)` by itself doesn't make a list. You'd need to compare it to `list(itertools.islice(rows, 2))`, and I suspect the two approaches would look a lot closer at that point. – DSM May 24 '17 at 01:24
  • @DSM Ah, your right good catch. I don't where my brain was. I'll recalculate the code snippets and update my answer. – Christian Dean May 24 '17 at 01:24
  • zzzz please add the timeit code zzzz :) - @DSM - however I am interested in iterating over the keys in order so that may be a fair comparison – Mr_and_Mrs_D May 24 '17 at 01:33
  • Thanks - I am off to bed - by the way now that I look carefully I probably need `itertools.islice(rows.values(), 2)` – Mr_and_Mrs_D May 24 '17 at 01:36
  • I think that whether or not `itertools` will win out is highly dependent on how big the dictionary is. If the dict is really large, I suspect that itertools will be faster. If the dict is small, then the performance difference is probably negligible either way. – mgilson May 24 '17 at 02:56
  • @mgilson Yes, your right. My timings were based off of the OP's comment - _"Thanks mgilson - not sure what would be faster - **copying a small list** or using the itertools module - maybe try an answer ? :)"_. – Christian Dean May 24 '17 at 03:38
  • The behavior on generator being slower than the list constructor is expected - see for instance: https://stackoverflow.com/questions/35138661/my-list-comprehension-is-faster-than-equivalent-code-with-generator-expression#comment58013879_35138661. Of course if the dict was enormous iterators would win. However timing this is off topic - what we are interested in is _iterating over the list of keys_ so your previous timings were more relevant (please also post the timit code). Also see the update to the question - does what hold for `keys()` hold for `values()` too - I guess yes. – Mr_and_Mrs_D May 24 '17 at 10:03
  • Btw my comment on _copying a small list_ referred to the `rows.keys()[:2]` copy – Mr_and_Mrs_D May 24 '17 at 12:56