PDXIII
PDXIII

Reputation: 1146

Getting the max value of attributes from a list of objects

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?

Upvotes: 65

Views: 79296

Answers (8)

new card
new card

Reputation: 23

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)

Upvotes: 1

Oleksii Kachaiev
Oleksii Kachaiev

Reputation: 6234

from operator import attrgetter
print(max(path.nodes, key=attrgetter("y")))

Upvotes: 5

user-asterix
user-asterix

Reputation: 936

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

Upvotes: 44

FogleBird
FogleBird

Reputation: 76762

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))

Upvotes: 37

Mark Byers
Mark Byers

Reputation: 838116

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))

Upvotes: 115

baz
baz

Reputation: 1587

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.

Upvotes: 4

Georgy
Georgy

Reputation: 13697

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

Upvotes: 0

Wooble
Wooble

Reputation: 89877

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.

Upvotes: 0

Related Questions