218

I would like to index a list with another list like this

L = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
Idx = [0, 3, 7]
T = L[ Idx ]

and T should end up being a list containing ['a', 'd', 'h'].

Is there a better way than

T = []
for i in Idx:
    T.append(L[i])

print T
# Gives result ['a', 'd', 'h']
4
  • 14
    It is really bizarre that L[idx] doesn't just work in base Python. Zen of python and all that. In numpy, things like this work just fine. Commented Oct 31, 2020 at 15:19
  • @eric A numpy array is vastly different from a CPython list object Commented Nov 2, 2021 at 15:16
  • @eric on the contrary: it would be really bizarre if L[idx] did do this in base Python. In fact, I can quote the Zen of Python to support that: "Special cases aren't special enough to break the rules." L[idx] does "work" - it means that the tuple (0, 3, 7) will be supplied as an index, which will subsequently cause a TypeError. It would work fine with, say, a dict using tuples for its keys. (Slices are different, in that - in prehistoric times - they were a special syntax before there was a slice type.) Commented Oct 9, 2022 at 6:39
  • 2
    “Although practicality beats purity” Commented Oct 10, 2022 at 9:49

8 Answers 8

350
T = [L[i] for i in Idx]
Sign up to request clarification or add additional context in comments.

6 Comments

Is this faster than a for-loop or only shorter?
@daniel: both + recommended
A quick timing test (no pysco or anything, so make of it what you will) showed the list comprehension 2.5x faster than the loop (1000 elements, repeated 10000 times).
(using map and a lambda is even slower - to be expected, since it calls a function for each iteration)
+1 If the indexing list is arbitrary, then a list comrpehension is the way. I think though that, when possible, which seems not to be the case here, slices are even faster.
|
57

If you are using numpy, you can perform extended slicing like that:

>>> import numpy
>>> a=numpy.array(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])
>>> Idx = [0, 3, 7]
>>> a[Idx]
array(['a', 'd', 'h'], 
      dtype='|S1')

...and is probably much faster (if performance is enough of a concern to to bother with the numpy import)

3 Comments

My quick timeit test showed that using np.array is actually almost 3 times slower (including the conversion to array).
It works better if you need to convert it for array operations anyways. Too time-consuming for regular list operations.
I tried this approach, i.e. replace list with np.array, but np.append did not work correctly (i.e. the same results as for lists) when the elements themselves were jagged arrays.
16
T = map(lambda i: L[i], Idx)

1 Comment

needed to be converted to list in py3k
14

I wasn't happy with any of these approaches, so I came up with a Flexlist class that allows for flexible indexing, either by integer, slice or index-list:

class Flexlist(list):
    def __getitem__(self, keys):
        if isinstance(keys, (int, slice)): return list.__getitem__(self, keys)
        return [self[k] for k in keys]

Which, for your example, you would use as:

L = Flexlist(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])
Idx = [0, 3, 7]
T = L[ Idx ]

print(T)  # ['a', 'd', 'h']

4 Comments

which also demonstrates the power and flexibility of Python!
It's so easy to extend this as well for existing code. Simply call existing_list = Flexlist(existing_list) and we have the required functionality without breaking any code
I did self[int(k)] instead, so that I could use np.arrays as indices too. Otherwise, you will get an np.int64 is not iterable error.
This is really useful. Two improvements are: return Flexlist([self[k] for k in keys]) and isinstance(keys, (int, np.integer, slice)) so you can work with numpy integer types for indexing.
12

A functional approach:

a = [1,"A", 34, -123, "Hello", 12]
b = [0, 2, 5]

from operator import itemgetter

print(list(itemgetter(*b)(a)))
[1, 34, 12]

1 Comment

This won't work if b happens to contain just one item.
7

You could also use the __getitem__ method combined with map like the following:

L = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
Idx = [0, 3, 7]
res = list(map(L.__getitem__, Idx))
print(res)
# ['a', 'd', 'h']

Comments

1
L= {'a':'a','d':'d', 'h':'h'}
index= ['a','d','h'] 
for keys in index:
    print(L[keys])

I would use a Dict add desired keys to index

Comments

1

My problem: Find indexes of list.

L = makelist() # Returns a list of different objects
La = np.array(L, dtype = object) # add dtype!
for c in chunks:
    L_ = La[c] # Since La is array, this works.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.