Getting the max value of attributes from a list of objects

Question:

I have this list of objects which have a x and a y parameter (and some other stuff).

path.nodes = (
    <GSNode x=535.0 y=0.0 GSLINE GSSHARP>,
    <GSNode x=634.0 y=0.0 GSLINE GSSHARP>,
    <GSNode x=377.0 y=706.0 GSLINE GSSHARP>,
    <GSNode x=279.0 y=706.0 GSLINE GSSHARP>,
    <GSNode x=10.0 y=0.0 GSLINE GSSHARP>,
    <GSNode x=110.0 y=0.0 GSLINE GSSHARP>,
    <GSNode x=189.0 y=216.0 GSLINE GSSHARP>,
    <GSNode x=458.0 y=216.0 GSLINE GSSHARP>
)

I need to have the max y of this list. Though, I tried this:

print(max(path.nodes, key=y))

And I get this error:

NameError: name 'y' is not defined

I am kinda new to Python and the docs give me no clue. I think I am doing wrong with the keyword because if iterate through nodes like this:

for node in path.nodes:
    print(node.y)

I’ll get the values of y.
Could somebody provide me an explanation?

Asked By: PDXIII

||

Answers:

from operator import attrgetter
print(max(path.nodes, key=attrgetter("y")))
Answered By: Alexey Kachayev

There’s a built-in to help with this case.

import operator

print(max(path.nodes, key=operator.attrgetter('y')))

Alternatively:

print(max(path.nodes, key=lambda item: item.y))

Edit: But Mark Byers’ answer is most Pythonic.

print(max(node.y for node in path.nodes))
Answered By: FogleBird

To get just the maximum value and not the entire object you can use a generator expression:

print(max(node.y for node in path.nodes))
Answered By: Mark Byers

y isn’t defined as a variable; it’s an attribute of individual GSNode objects; you can’t use it as a name on its own.

To access the individual attributes you can use something like key=lambda x: x.y or attrgetter() from the operator module.

Answered By: Wooble

There is an important difference for when to use the "Pythonic" style #1 versus lambda style #2:

max(node.y for node in path.nodes)  # (style #1)

versus

max(path.nodes, key=lambda item: item.y)  # (style #2)

If you look carefully you can see that style #1 returns the maximum value for the attribute y while style #2 returns the node that has maximum attribute y. These two are not the same and code usage is important in case you want to iterate over the attribute values or iterate over the objects that holds that attribute.

Example:

class node():
    def __init__(self,x):
        self.x = x
        self.y = self.x + 10

node_lst = [node(1), node(2), node(3), node(4), node(5)]
print([(e.x,e.y) for e in node_lst])

>>> [(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]

Now:

maxy = max(node.y for node in node_lst)
print(maxy)
>>> 15

max_node = max(node_lst, key=lambda node: node.y)
print(max_node.y)
>>> 15
Answered By: user-asterix

If y is a property attribute then you don’t even need to import operator.attrgetter. You can use fget method instead:

my_node = max(path.nodes, key=Node.y.fget)

This will return the Node instance from where to get the max y value is just my_node.y

Answered By: Georgy

It’s also possible to implement the __gt__ comparison operator for an object, and than use max without a key function:

class node:
    def __init__(self, y):
        self.y = y
    def __gt__(self, other):
        return self.y > other.y

and than something like:

ls = [node(3), node(5), node(11), node(0)]
print(max(ls).y)

is supposed to output 11.

Answered By: baz

they already answered you, but if you want to get the object who has the max value:

max_val_object = lambda x: max(ob.value for ob in x)
Answered By: new card
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.