whatspop - Kunal Anand

List methods that handle predicates in Python

12/18/2007

In continuation of a previous post of using predicates/lambdas in Python, I prepared some helpful list methods that you can drop into your current and future Python scripts. What follows in an introduction/walk-through of the five methods. Note, when I say predicate, I am referring to a literal value or a function pointer.

Exists(predicate)
* Returns true if the predicate is satisfied by at least one list member.

FirstIndex(predicate)
* Returns the first integer position in the list that satisfies the predicate.

FirstValue(predicate)
* Returns the first value in the list that satisfies the predicate.
* Note, the returned value may be a complex object.

LastIndex(predicate)
* Returns the last integer position in the list that satisfies the predicate.

LastValue(predicate)
* Returns the last value in the list that satisfies the predicate.
* Like FirstValue, the returned value may be complex.

Bored? Here is the overloaded list, which I am dubbing 'PredicateList':

from types import FunctionType
from UserList import UserList

class PredicateList(UserList):
def Exists(self, predicate):
"""Returns true if the predicate is satisfied by at least one list member."""
if type(predicate) is FunctionType:
for item in self.data:
if predicate(item):
return True
return False
else:
return predicate in self.data

def GetIndex(self, predicate, reversed):
"""Common functionality shared by FirstIndex and LastIndex."""
xr = xrange(len(self.data))
if reversed:
xr = xr.__reversed__()
for i in xr:
try:
if type(predicate) == FunctionType:
if predicate(self.data[i]):
return i
else:
if self.data[i] == predicate:
return i
# Function predicates that check object properties will fail on simple
# types like strings. This is rather unelegant, but works.
except AttributeError:
pass
return None

def FirstIndex(self, predicate):
"""Returns the first integer position in the list that satisfies the predicate."""
return self.GetIndex(predicate, False)

def FirstValue(self, predicate):
"""Returns the first value in the list that satisfies the predicate."""
firstObjectIndex = self.FirstIndex(predicate)
if not firstObjectIndex is None:
return self.data[firstObjectIndex]
else:
return None

def LastIndex(self, predicate):
"""Returns the last integer position in the list that satisfies the predicate."""
return self.GetIndex(predicate, True)

def LastValue(self, predicate):
"""Returns the last value in the list that satisfies the predicate."""
lastObjectIndex = self.LastIndex(predicate)
if not lastObjectIndex is None:
return self.data[lastObjectIndex]
else:
return None

Note, PredicateList extends UserList (this is found under your Python installation's lib/), which happens to be a Python wrapper for the list type. If you are curious, there is also a UserDict class that acts congruently for dictionaries. As you can see, everything above is straight-forward. I am however a bit embarrassed by catching the AttributeError exception; there is certainly a better way of accomplishing that - one way that comes to mind is to do a type check prior to grabbing the property value.

Alright, with that out of the way here is an example use-case. Suppose I had a Person class that looked like the following:

class Person():
def __init__(self, firstName, lastName):
self.firstName = firstName
self.lastName = lastName
def __str__(self):
return '%s %s' % (self.firstName, self.lastName)

Now let's initialize a PredicateList and append some members. Note, I would have preferred to have simply used the [] notation, but this will have to suffice:

sampleList = PredicateList()

sampleList.append('3')
sampleList.append('2')
sampleList.append('1')
sampleList.append(Person('Joe', 'Shmoe'))
sampleList.append('5')
sampleList.append('2')

print sampleList

Now, we can use the PredicateList like so:

# Existence checks
print sampleList.Exists('2')
print sampleList.Exists(lambda x: x == '2')

# First index checks
print sampleList.FirstIndex('2')
print sampleList.FirstIndex(lambda x: x == '2')

# First object checks
print sampleList.FirstValue('2')
print sampleList.FirstValue(lambda person: person.firstName == 'Joe')

# Last index checks
print sampleList.LastIndex('2')
print sampleList.LastIndex(lambda x: x == '2')

# Last object checks
print sampleList.LastValue('2')
print sampleList.LastValue(lambda person: person.lastName == 'Shmoe')

There were things that I obviously omitted from this class. For instance, map() and filter() already take lambdas as function arguments, making operations like value casting and spawning new lists seem relatively simple.

There may be an oversight in the code above as this was a really fun 10 minute exercise with the REPL. I always appreciate feedback, so feel free to drop me a line.

About I am currently a Senior Engineer at MySpace. Feel free to check out my personal collective.

Archives
April 2005
May 2005
June 2005
July 2005
August 2005
September 2005
October 2005
November 2005
December 2005
January 2006
March 2006
April 2006
May 2006
June 2006
July 2006
August 2006
September 2006
October 2006
November 2006
January 2007
February 2007
April 2007
November 2007
December 2007
January 2008
March 2008
April 2008
May 2008
June 2008

Subscribe to my feed