I have a custom data type, say: mytime
, which represent hours and minutes, such as 29:45, it is 29 hours and 45 minutes.
I want to use max
built-in function to find the item in a list of lists, whose sum of its elements is the greatest, where all lists contain values of mytime
type.
x = [[a, b], [c, d]]
a,b,c,d are of mytime
type.
max(x, key=sum)
won't work here, because a,b,c,d, are not integers.
If I type a + b
at python command line, I get the sum of these two time values, result is of mytime
type, without any errors.
How do I use max
function here?
Let's say your class looks like this:
class mytime(object):
def __init__(self, h, m):
self.h = h
self.m = m
def __add__(self, other):
return mytime(self.h + other.h, self.m + other.m)
def __repr__(self):
return '%i:%i' % (self.h, self.m)
and you use it like this:
a = mytime(10, 10)
b = mytime(2, 22)
print a + b
and it will work as expect:
12:32
Problem:
What you want to do is:
l = [a, b]
print sum(l)
but it will fail:
TypeError: unsupported operand type(s) for +: 'int' and 'mytime'
The problem is that the sum
function will start with 0
and will add up all values of the list. It will try to evaluate
0 + mytime(10, 10)
which will fail.
Solution:
The solution to your problem is implementing the __radd__
function, which represents "reverse add" and is called when the arguments can't be resolved in the "forward" direction. For example, x + y
is evaluated as x.__add__(y)
if possible, but if that doesn't exist then Python tries y.__radd__(x)
.
So you can add the following method to your class:
def __radd__(self, other):
return mytime(self.h, self.m)
and the sum
function will work for you (in this implementation ignoring the other
value, which is probably fine in your case).
You can write your own sum function:
def my_sum(item):
return sum(60 * e[0] + e[1] for e in item)
x = [[(2,0), (3,0)], [(9, 0), (4, 0)]]
print max(x, key=my_sum)
I have represented your mytime
data structure as tuples (with hours and minutes) so you may need to adjust my_sum
to your data structure. The only requirement is that the hours and minutes of a mytime
can be filled in for e[0]
and e[1]
respectively.
The above code returns the greatest element (in this case [(9, 0), (4, 0)]
).
Are you sure using a + b
works? All sum
does is repeatedly apply +
to adjacent elements (it's the same as reduce(operator.add, sequence)
with a special case to break on strings)... So if it does work - then max(x, key=sum)
should just work -- as long as mydate
supports comparison operators - eg __gt__
, __eq__
, __lt__
Example
You need to have __gt__
defined for max to work...
class mydate(object):
def __init__(self, num):
self.num = num
def __add__(self, other): # make sure sum works
return self.num + other.num
def __gt__(self, other): # make sure max can do > comparison
return self.num > other.num
def __repr__(self):
return 'date: {}'.format(self.num)
x = mydate(3)
y = mydate(5)
z = mydate(2)
print max([x,y,z], key=sum)