43

I have list of objects with attribute id and I want to find index of object with specific id. I wrote something like this:

index = -1
for i in range(len(my_list)):
    if my_list[i].id == 'specific_id'
        index = i
        break

but it doesn't look very well. Are there any better options?

1
  • Looping by index is a really huge anti-pattern in Python - loop directly over a list. Commented Oct 3, 2013 at 14:53

5 Answers 5

75

Use enumerate when you want both the values and indices in a for loop:

for index, item in enumerate(my_list):
    if item.id == 'specific_id':
        break
else:
    index = -1

Or, as a generator expression:

index = next((i for i, item in enumerate(my_list) if item.id == 'specific_id'), -1)
2
  • 6
    But "-1" is a valid index value. If your code doesn't find a match, it will return an index of -1, and lead someone to take the final element of the list as though it matched.
    – CPBL
    Commented Apr 17, 2016 at 17:03
  • @CPBL That's true. It would certainly be more pythonic to raise a ValueError in the else clause (to mirror the behavior of list.index). Commented May 8, 2017 at 19:28
13

Here's an alternative that doesn't use an (explicit) loop, with two different approaches to generating the list of 'id' values from the original list.

try:
    # index = map(operator.attrgetter('id'), my_list).index('specific_id')
    index = [ x.id for x in my_list ].index('specific_id')
except ValueError:
    index = -1
2
  • Is this in python 3 only? Using python 2.7.4 'find' doesn't exist - the method that finds the index of an element in a list is called 'index'.
    – Ian Durkan
    Commented Mar 27, 2014 at 20:47
  • 2
    No, this just appears to be a disaster of an answer. Not only did I appear to assume there was a list.find by analogy to the pair of str.find and str.index methods, but also mixed up which one raises a ValueError (it's str.index, not str.find) and which returns -1. Corrected to list.index now.
    – chepner
    Commented Mar 27, 2014 at 20:57
10

You can use enumerate:

for index, item in enumerate(my_list):
    if item.id == 'specific_id':
        break
4

Implement the __eq__ method for your class

class MyCls:
   def __init__(self, id):
       self.id = id

   def __eq__(self, other):
       # comparing with str since you want to compare
       # your object with str

       if not isinstance(other, str):
           raise TypeError("MyCls can be compared only with str")
       if other == self.id:
           return True
       return False

now you can do something like

my_list = [MyCls(i) for i in 'abcdef']
print(my_list.index('c'))

This would return the index 2. It would behave like normal list.index methods behaves. i.e If it doesn't find the index, it will raise a ValueError

-1

Assuming

a = [1,2,3,4]
val = 3

Do

a.index(val) if val in a else -1

For multiple occurrence, as per Azam's comment below:

[i if val == x else -1 for i,x in enumerate(a)] 

Edit1: For everyone commenting that its List of object, all you need is, access the id

[i if val == x.id else -1 for i,x in enumerate(a)] 
3
  • what if the val number that we are searching for is repeating such as a=[1,2,3,4,3,7,9]???
    – Azam
    Commented May 13, 2018 at 6:41
  • 3
    is a = [1,2,3,4] a list of objects ? Please read the op's question before answering
    – spartanz51
    Commented Nov 1, 2018 at 9:52
  • 2
    this is not a list of objects Commented Mar 17, 2019 at 21:24

Not the answer you're looking for? Browse other questions tagged or ask your own question.