Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

max() of a list of tuples

2 views
Skip to first unread message

Mario Wehbrink

unread,
Jan 21, 2003, 4:58:14 AM1/21/03
to
Hi,

i have a list of tuples that look like:
[(1,3,5), (8,16,2), (2,56,4)]

what i am interested, in is the tuple with the greatest value in pos 3.
So in this case it would be (1,3,5). Is there a way to tell
max(mylistoftuples) to only look at the last position of the tuples?


Mario

--
http://www.porcupinetree.com

Max M

unread,
Jan 21, 2003, 6:25:58 AM1/21/03
to
Mario Wehbrink wrote:

> i have a list of tuples that look like:
> [(1,3,5), (8,16,2), (2,56,4)]
>
> what i am interested, in is the tuple with the greatest value in pos 3.
> So in this case it would be (1,3,5). Is there a way to tell
> max(mylistoftuples) to only look at the last position of the tuples?


l = [(1,3,5), (8,16,2), (2,56,4)]
print max([(i[-1],i) for i in l])[1]

>>> (1,3,5)

--

hilsen/regards Max M

http://www.futureport.dk/
Fremtiden, videnskab, skeptiscisme og transhumanisme

Terry Hancock

unread,
Jan 21, 2003, 6:35:58 AM1/21/03
to
Mario Wehbrink wrote:
> i have a list of tuples that look like:
> [(1,3,5), (8,16,2), (2,56,4)]
>
> what i am interested, in is the tuple with the greatest value in pos 3.
> So in this case it would be (1,3,5). Is there a way to tell
> max(mylistoftuples) to only look at the last position of the tuples?

Don't know if this is the most elegant solution, but it does work:

l = [(1, 3, 5), (8, 16, 2), (2, 56, 4)]
z = zip(*l)
z # prints [(1, 8, 2), (3, 16, 56), (5, 2, 4)]
l[list(z[2]).index(max(z[2]))] # prints (1, 3, 5)

Ugly, but:

* zip combines sequences into a list of tuples
* one of these contains the sequence you want to test
* we find the maximum and what its index in the sequence is
* we use that to look up the index in the original list

Maybe someone will have a more elegant solution.

Cheers,
Terry

--
Anansi Spaceworks
http://www.anansispaceworks.com

Mario Wehbrink

unread,
Jan 21, 2003, 5:41:03 AM1/21/03
to
Am Die, 21 Jan 2003 schrieb Max M in comp.lang.python:

> Mario Wehbrink wrote:
>
> > [maximum of list of tuples]


>
>
> l = [(1,3,5), (8,16,2), (2,56,4)]
> print max([(i[-1],i) for i in l])[1]
>
> >>> (1,3,5)

Nice and elegant! Thanks. Although i don't understand how it works :-/

Mario

Max M

unread,
Jan 21, 2003, 7:30:39 AM1/21/03
to

Here goes then!

# wont explain this one ;-)

l = [(1,3,5), (8,16,2), (2,56,4)]

# create a new list of 2 element tuples with last element of
# your tuples as the first value, and the actual tuple as the second.
# this is a standard idiom for sorting efficiently in Python

sList = [(i[-1],i) for i in l]
# sList = [(5,(1,3,5)), (2,(8,16,2)), (4,(2,56,4))]

# Because the first element is the most significant in a compare,
# python will get max value from that

maxElement = max(sList)
# maxElement = (5,(1,3,5))

# and to get the correct tuple I take the 2. element in maxElement

print maxElement[1]

>>> (1,3,5)

I hope that is more obvious


regards

Gerrit Holl

unread,
Jan 21, 2003, 10:17:51 AM1/21/03
to
Max M schreef op dinsdag 21 januari om 12:26:46 +0000:

> l = [(1,3,5), (8,16,2), (2,56,4)]

Yet Another possibility, Python 2.3 only:

max([i[::-1] for i in l])[::-1]

yours,
Gerrit.

--
Asperger Syndroom - een persoonlijke benadering:
http://people.nl.linux.org/~gerrit/
Het zijn tijden om je zelf met politiek te bemoeien:
http://www.sp.nl/

Jp Calderone

unread,
Jan 21, 2003, 11:09:16 AM1/21/03
to
On Tue, Jan 21, 2003 at 11:58:14AM +0200, Mario Wehbrink wrote:
> Hi,
>
> i have a list of tuples that look like:
> [(1,3,5), (8,16,2), (2,56,4)]
>
> what i am interested, in is the tuple with the greatest value in pos 3.
> So in this case it would be (1,3,5). Is there a way to tell
> max(mylistoftuples) to only look at the last position of the tuples?
>

Here's another way:

L = [(1,3,5), (8,16,2), (2,56,4)]
s = L[:]
s.sort(lambda x, y: cmp(x[2], y[2]))
print s[-1]

Jp

--
"There is no reason for any individual to have a computer in their
home."
-- Ken Olson, President of DEC, World Future Society
Convention, 1977
--
12:00am up 36 days, 9:48, 4 users, load average: 0.10, 0.14, 0.14

Erik Max Francis

unread,
Jan 21, 2003, 2:24:22 PM1/21/03
to
Mario Wehbrink wrote:

I'm suspecting if you don't understand how it works then it can't really
be too elegant :-).

The code sample constructs an enumeration of the tuples with their third
(last) element, relies on default sorting of lists (and tuples) to sort
by the first element first, picks the maximum element of that
enumeration, and then extracts the tuple associated with it.

It works, and it's arguably the right way to do it, but "elegant" isn't
the word I'd associate with it.

--
Erik Max Francis / m...@alcyone.com / http://www.alcyone.com/max/
__ San Jose, CA, USA / 37 20 N 121 53 W / &tSftDotIotE
/ \ The doors of Heaven and Hell are adjacent and identical.
\__/ Nikos Kazantzakis
Official Omega page / http://www.alcyone.com/max/projects/omega/
The official distribution page for the popular Roguelike, Omega.

Peter Abel

unread,
Jan 21, 2003, 3:02:00 PM1/21/03
to
Terry Hancock <han...@anansispaceworks.com> wrote in message news:<ygaX9.4400$bL4.4...@newsread2.prod.itd.earthlink.net>...

How about this?

>>> l = [(1, 3, 5), (8, 16, 2), (2, 56, 4)]

>>> reduce(lambda y,(u,v,x):y>x and y or x,l)
(1, 3, 5)

Regards
Peter

Peter Abel

unread,
Jan 21, 2003, 3:52:20 PM1/21/03
to
Terry Hancock <han...@anansispaceworks.com> wrote in message news:<ygaX9.4400$bL4.4...@newsread2.prod.itd.earthlink.net>...

Silly me!!!
The correct code is:


>>> l = [(1, 3, 5), (8, 16, 2), (2, 56, 4)]

>>> reduce(lambda y,x:y[2]>x[2] and y or x,l,(-1.e16,-1.e16,-1.e16))
(1, 3, 5)

And it works also with:
>>> l = [(1, 3, -5), (8, 16, -2), (2, 56, -4)]
>>> reduce(lambda y,x:y[2]>x[2] and y or x,l,(-1.e16,-1.e16,-1.e16))
(8, 16, -2)

Or with:
>> l = [(1, 3, -5), (8, 16, -200), (2, 56, -4)]
>>> reduce(lambda y,x:y[2]>x[2] and y or x,l,(-1.e16,-1.e16,-1.e16))
(2, 56, -4)
>>>

Sorry
Peter

Scott David Daniels

unread,
Jan 21, 2003, 5:06:14 PM1/21/03
to
I know this isn't clever enough, but:

source = [(1,3,5), (8,16,2), (2,56,4)] # or whatever

best = source[0]
for element in source:
if best[-1] < element[-1]:
best = element
Leaving the answer in best.

or perhaps you'd prefer:

score = min([element[-1] for element in source])
for best in source:
if score == best[-1]:
break

or even:

if source:
score = min([element[-1] for element in source])
bestlist = [element[-1] for element in source
if score == element[-1]]
else:
bestlist = []
# Which gives all elements with the highest score.

Variables named "i" always look like integers to me.
-Scott David Daniels
Scott....@Acm.Org

Lee Harr

unread,
Jan 21, 2003, 5:20:54 PM1/21/03
to
> Here's another way:
>
> L = [(1,3,5), (8,16,2), (2,56,4)]
> s = L[:]
> s.sort(lambda x, y: cmp(x[2], y[2]))
> print s[-1]
>
> Jp
>

Yup, that is the way I would go also.

Very simple & straightforward.

(tho I might not have used a lambda ;-)


Jeff Epler

unread,
Jan 21, 2003, 5:46:34 PM1/21/03
to
On Tue, Jan 21, 2003 at 10:20:54PM -0000, Lee Harr wrote:
> > Here's another way:
[using sort with a comparison function]

>
> Yup, that is the way I would go also.
>
> Very simple & straightforward.
>
> (tho I might not have used a lambda ;-)

However, you can find the maximum of a list in O(n) comparisons, but you
can only sort a list in O(n log n) comparisons. I'd endorse a variant of
DSU (decorate, sort, unsort) which would be decorate, max, undecorate.
I think you were calling the largest tuple the one whose final element was
greatest, in which case the following code seems likely:
def decorate(t): return (t[-1], t)
def undecorate(t): return t[1]

def decorated_max(l):
l = [decorate(t) for t in l]
return undecorate(max(l))

Jeff

Martin Maney

unread,
Jan 21, 2003, 11:03:57 PM1/21/03
to
Peter Abel <p-a...@t-online.de> wrote:
> Silly me!!!
> The correct code is:
>>>> l = [(1, 3, 5), (8, 16, 2), (2, 56, 4)]
>>>> reduce(lambda y,x:y[2]>x[2] and y or x,l,(-1.e16,-1.e16,-1.e16))
> (1, 3, 5)

Why not (None,None,None) as the initial value?

(no, really, I don't know. it just seemed natural in the same way that
thinking "reduce is the answer" seemed as it popped into my head when I
first saw the OP's question. None does seem to compare as less than
anything else (and, yeah, trying it seemed more natural than looking it
up - I think Python may be eating my mind, but I can't bring myself to
worry about it))

Erik Max Francis

unread,
Jan 21, 2003, 11:30:12 PM1/21/03
to
Martin Maney wrote:

> Why not (None,None,None) as the initial value?

You're guaranteed that in any given implementation, the ordering of None
and numerics will be consistent, but you've no guarantee which will be
bigger. If None < -1e16 on your implementation, that's just a lucky
conicidence; the interpreter would legitimately have None > -1e16.

It's an implementation defined area, and as such, your program shouldn't
rely on it if you want portability.

--
Erik Max Francis / m...@alcyone.com / http://www.alcyone.com/max/
__ San Jose, CA, USA / 37 20 N 121 53 W / &tSftDotIotE

/ \ I love Mickey Mouse more than any woman I've ever known.
\__/ Walt Disney
HardScience.info / http://www.hardscience.info/
The best hard science Web sites that the Web has to offer.

Delaney, Timothy

unread,
Jan 21, 2003, 11:25:54 PM1/21/03
to
> From: Martin Maney [mailto:ma...@pobox.com]

>
> Why not (None,None,None) as the initial value?
>
> first saw the OP's question. None does seem to compare as less than
> anything else (and, yeah, trying it seemed more natural than

The ordering of comparison of None with anything else is arbitrary and has
changed over Python versions.

In fact, except for well-defined cases (such as comparing int with float)
the comparison of an instance of any type with an instance of any other type
is undefined.

Tim Delaney

David Eppstein

unread,
Jan 22, 2003, 12:22:10 AM1/22/03
to
In article <b0l57d$12j$1...@wheel2.two14.net>,
Martin Maney <ma...@pobox.com> wrote:

> Peter Abel <p-a...@t-online.de> wrote:
> > Silly me!!!
> > The correct code is:
> >>>> l = [(1, 3, 5), (8, 16, 2), (2, 56, 4)]
> >>>> reduce(lambda y,x:y[2]>x[2] and y or x,l,(-1.e16,-1.e16,-1.e16))
> > (1, 3, 5)
>
> Why not (None,None,None) as the initial value?

What I wonder is, why supply the final argument to reduce at all?
Without the argument, it will work with non-numeric tuple values.
With the argument, the values have to be comparable to floats.

--
David Eppstein UC Irvine Dept. of Information & Computer Science
epps...@ics.uci.edu http://www.ics.uci.edu/~eppstein/

Erik Max Francis

unread,
Jan 22, 2003, 12:48:16 AM1/22/03
to
David Eppstein wrote:

> What I wonder is, why supply the final argument to reduce at all?
> Without the argument, it will work with non-numeric tuple values.
> With the argument, the values have to be comparable to floats.

The final argument would only be necessary in the edge case where the
list of tuples is empty:

>>> reduce(max, [3, 4, 5, 2])
5
>>> reduce(max, [3])
3
>>> reduce(max, [])
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: reduce() of empty sequence with no initial value

Certainly that can be handled as a special case and reduce would simply
not be needed to be called in the first place.

Peter Abel

unread,
Jan 22, 2003, 7:47:14 AM1/22/03
to
Martin Maney <ma...@pobox.com> wrote in message news:<b0l57d$12j$1...@wheel2.two14.net>...

Believe me, I'm not at all happy with (-1.e16,-1.e16,-1.e16).
It's only because nothing else came into my head.
I tried ('','','') and it worked too ?? But
as I remarked later, only in this special case.
So the solution is to start with the first element of l.
And the the tuples can be what they want: integer, floats etc.

reduce(lambda y,x:y[2]>x[2] and y or x,l,l[0])

I think it's the best initial value one can choose.
Peter

Jp Calderone

unread,
Jan 22, 2003, 11:56:00 AM1/22/03
to
On Wed, Jan 22, 2003 at 04:47:14AM -0800, Peter Abel wrote:
> [snip]

>
> reduce(lambda y,x:y[2]>x[2] and y or x,l,l[0])

reduce(lambda y,x:y[2]>x[2] and y or x,l[1:],l[0])

Jp

--
12:00am up 37 days, 9:48, 2 users, load average: 0.36, 0.32, 0.27

Erik Max Francis

unread,
Jan 22, 2003, 7:30:44 PM1/22/03
to
Peter Abel wrote:

> Believe me, I'm not at all happy with (-1.e16,-1.e16,-1.e16).
> It's only because nothing else came into my head.
> I tried ('','','') and it worked too ?? But
> as I remarked later, only in this special case.

It's implementation defined; all strings could test greater than all
numerics in a particular Python implementation, for instance.

> So the solution is to start with the first element of l.
> And the the tuples can be what they want: integer, floats etc.
>
> reduce(lambda y,x:y[2]>x[2] and y or x,l,l[0])
>
> I think it's the best initial value one can choose.

It's not clear to me why you think an initial value here is necessary in
the first place. Since you're only doing the equivalent a reduce on a
max function, the only case you'd need to specify that third argument is
if there were nothing to reduce (i.e., the list were empty). But you
can simply handle that case separately before you call reduce, so it's a
non-issue.

--
Erik Max Francis / m...@alcyone.com / http://www.alcyone.com/max/
__ San Jose, CA, USA / 37 20 N 121 53 W / &tSftDotIotE

/ \ I'm rolling like thunder / I'm feeling my power
\__/ Chante Moore
CatCam / http://www.catcam.com/
What do your pets do all day while you're at work? Find out.

Martin Maney

unread,
Jan 22, 2003, 10:54:00 PM1/22/03
to
Erik Max Francis <m...@alcyone.com> wrote:
> Martin Maney wrote:
>> Why not (None,None,None) as the initial value?

> It's an implementation defined area, and as such, your program shouldn't


> rely on it if you want portability.

Bother. Okay, back to Looking It Up, then. :-)

(I'm sure there are half a dozen others who replied likewise; Erik just
happened to come up first in thread order.)

Alan Daniels

unread,
Jan 23, 2003, 3:37:49 PM1/23/03
to
Max M <ma...@mxm.dk> wrote in message news:<3E2D2E46...@mxm.dk>...

> l = [(1,3,5), (8,16,2), (2,56,4)]
> print max([(i[-1],i) for i in l])[1]

Another possible solution, using a slightly different route:

best = max([x[-1] for x in l])
print [x for x in l if x[-1] == best][0]

Line one: Find the max value for the last item in each of the tuples.
Line two: Get the tuple that has that max value.

Slightly less efficient, since it uses two list comprehensions, and thus
traverses the lists twice, but easier to understand at first glance, imho.

Peter Abel

unread,
Jan 23, 2003, 6:03:59 PM1/23/03
to
Erik Max Francis <m...@alcyone.com> wrote in message news:<3E2F37B4...@alcyone.com>...

> Peter Abel wrote:
>
> > Believe me, I'm not at all happy with (-1.e16,-1.e16,-1.e16).
> > It's only because nothing else came into my head.
> > I tried ('','','') and it worked too ?? But
> > as I remarked later, only in this special case.
>
> It's implementation defined; all strings could test greater than all
> numerics in a particular Python implementation, for instance.
>
> > So the solution is to start with the first element of l.
> > And the the tuples can be what they want: integer, floats etc.
> >
> > reduce(lambda y,x:y[2]>x[2] and y or x,l,l[0])
> >
> > I think it's the best initial value one can choose.
>
> It's not clear to me why you think an initial value here is necessary in
> the first place.
When I played around with some examples, it seemed not to work without
an initial value.

> Since you're only doing the equivalent a reduce on a
> max function, the only case you'd need to specify that third argument is
> if there were nothing to reduce (i.e., the list were empty).
I think you're right:

>>> n=0
>>> def my_max(y,x):
... global n
... print 'y(%d)=%s'%(n,str(y))
... n+=1
... return y[2]>x[2] and y or x
...
>>> l= [(-52, -28, -987), (-63, -16, 295), (-54, -16, 809), (-13, -3,
-14), (-97, -36, 552)]
>>> print 'Result:%s'%str(reduce(my_max,l))
y(0)=(-52, -28, -987)
y(1)=(-63, -16, 295)
y(2)=(-54, -16, 809)
y(3)=(-54, -16, 809)
Result:(-54, -16, 809)
>>> n=0
>>> l= [(-52, -28, -987), (-63, -16, -295), (-54, -16, -809), (-13,
-3, -14), (-97, -36, -552)]
>>> print 'Result:%s'%str(reduce(my_max,l))
y(0)=(-52, -28, -987)
y(1)=(-63, -16, -295)
y(2)=(-63, -16, -295)
y(3)=(-13, -3, -14)
Result:(-13, -3, -14)
>>>
And if there's no initial value, it seems the
reduce-function starts with the first element of l.
(As I said l[0] <wink>)

> But you
> can simply handle that case separately before you call reduce, so it's a
> non-issue.

I like it compact:
>>> l=[]
>>> reduce(lambda y,x:y[2]>x[2] and y or x,len(l) and l or
[('this','is an','empty list!!')])
('this', 'is an', 'empty list!!')
>>> l= [(-52, -28, -987), (-63, -16, -295), (-54, -16, -809), (-13,
-3, -14), (-97, -36, -552)]
>>> reduce(lambda y,x:y[2]>x[2] and y or x,len(l) and l or
[('this','is an','empty list!!')])
(-13, -3, -14)
>>>

Peter

Erik Max Francis

unread,
Jan 23, 2003, 7:54:26 PM1/23/03
to
Peter Abel wrote:

> And if there's no initial value, it seems the
> reduce-function starts with the first element of l.
> (As I said l[0] <wink>)

Well, of course. That's what reduce _does_. It repeatedly applies a
binary function to the elements of the sequence, accumulating the
result. Specifying the third argument -- the initial value -- is only
necessary if 1. the sequence is empty or 2. you want the initial value
to be something other than what it would normally be.

The vagueness here is, of course, because reduce works on _any_ function
that accumulates values over any types of elements, not just numeric
ones.

Specifying

reduce(f, S, i)

is really precisely equivalent to

reduce(f, [i] + S),

provided of course that the sequence is compatible with being added to a
list, etc.

--
Erik Max Francis / m...@alcyone.com / http://www.alcyone.com/max/
__ San Jose, CA, USA / 37 20 N 121 53 W / &tSftDotIotE

/ \ An undevout astronomer is mad.
\__/ Edward Young
Bosskey.net: Quake III Arena / http://www.bosskey.net/q3a/
A personal guide to Quake III Arena.

Anton Vredegoor

unread,
Jan 24, 2003, 5:43:16 AM1/24/03
to
On Thu, 23 Jan 2003 16:54:26 -0800, Erik Max Francis <m...@alcyone.com>
wrote:

>The vagueness here is, of course, because reduce works on _any_ function
>that accumulates values over any types of elements, not just numeric
>ones.
>
>Specifying
>
> reduce(f, S, i)
>
>is really precisely equivalent to
>
> reduce(f, [i] + S),

This provides me with a rationale to reuse some code that I did not
post because shorter solutions were already found. It can be used for
lists that are nested deeper than one level.

Regards,
Anton.

from operator import getitem

def maxat(L,x):
def getat(indices):
return reduce(getitem,indices,L)
def imax(i,j):
if getat([i]+x)<getat([j]+x): return j
return i
return L[reduce(imax,range(len(L)))]

def test():
L = [(1,3,5), (8,16,2), (2,56,4)]
print maxat(L,[2])

if __name__=='__main__':
test()

0 new messages