Reputation: 3874
Let's assume I'm creating a simple class to work similar to a C-style struct, to just hold data elements. I'm trying to figure out how to search a list of objects for objects with an attribute equaling a certain value. Below is a trivial example to illustrate what I'm trying to do.
For instance:
class Data:
pass
myList = []
for i in range(20):
data = Data()
data.n = i
data.n_squared = i * i
myList.append(data)
How would I go about searching the myList list to determine if it contains an element with n == 5?
I've been Googling and searching the Python docs, and I think I might be able to do this with a list comprehension, but I'm not sure. I might add that I'm having to use Python 2.4.3 by the way, so any new gee-whiz 2.6 or 3.x features aren't available to me.
Upvotes: 151
Views: 333844
Reputation: 3778
I'm surprised there's no answer that asserts that only one item matches.
Maybe this does that:
def one_matching(list, test):
filtered = [item for item in list if test(item)]
if len(filtered) != 1:
if len(filtered) == 0:
raise KeyError("No matching value found.")
else:
raise KeyError("Multiple matching values found.", *filtered)
return filtered[0]
# prints 2:
print(one_matching([1,2,3], lambda x: x**2 == 4))
Upvotes: 1
Reputation: 11
Use the following list comprehension in combination with the index
method:
data_n = 30
j = [data.n for data in mylist].index(data_n)
print(mylist[j].data.n == data_n)
Upvotes: 1
Reputation: 441
Another way you could do it is using the next() function.
matched_obj = next(x for x in list if x.n == 10)
Upvotes: 18
Reputation: 89221
[x for x in myList if x.n == 30] # list of all matches
[x.n_squared for x in myList if x.n == 30] # property of matches
any(x.n == 30 for x in myList) # if there is any matches
[i for i,x in enumerate(myList) if x.n == 30] # indices of all matches
def first(iterable, default=None):
for item in iterable:
return item
return default
first(x for x in myList if x.n == 30) # the first match, if any
Upvotes: 62
Reputation: 137492
Simple, Elegant, and Powerful:
A generator expression in conjuction with a builtin… (python 2.5+)
any(x for x in mylist if x.n == 10)
Uses the Python any()
builtin, which is defined as follows:
any(iterable)
->
Return True if any element of the iterable is true. Equivalent to:
def any(iterable):
for element in iterable:
if element:
return True
return False
Upvotes: 106
Reputation: 400542
You can get a list of all matching elements with a list comprehension:
[x for x in myList if x.n == 30] # list of all elements with .n==30
If you simply want to determine if the list contains any element that matches and do it (relatively) efficiently, you can do
def contains(list, filter):
for x in list:
if filter(x):
return True
return False
if contains(myList, lambda x: x.n == 3) # True if any element has .n==3
# do stuff
Upvotes: 224
Reputation: 5907
You can use in
to look for an item in a collection, and a list comprehension to extract the field you are interested in. This (works for lists, sets, tuples, and anything that defines __contains__
or __getitem__
).
if 5 in [data.n for data in myList]:
print "Found it"
See also:
Upvotes: 10
Reputation: 16909
Consider using a dictionary:
myDict = {}
for i in range(20):
myDict[i] = i * i
print(5 in myDict)
Upvotes: 3
Reputation: 54081
You should add a __eq__
and a __hash__
method to your Data
class, it could check if the __dict__
attributes are equal (same properties) and then if their values are equal, too.
If you did that, you can use
test = Data()
test.n = 5
found = test in myList
The in
keyword checks if test
is in myList
.
If you only want to a a n
property in Data
you could use:
class Data(object):
__slots__ = ['n']
def __init__(self, n):
self.n = n
def __eq__(self, other):
if not isinstance(other, Data):
return False
if self.n != other.n:
return False
return True
def __hash__(self):
return self.n
myList = [ Data(1), Data(2), Data(3) ]
Data(2) in myList #==> True
Data(5) in myList #==> False
Upvotes: 6
Reputation: 112404
Just for completeness, let's not forget the Simplest Thing That Could Possibly Work:
for i in list:
if i.n == 5:
# do something with it
print "YAY! Found one!"
Upvotes: 58