views:

104

answers:

5

I'm new to Python, so please forgive me when using wrong terms :)

I'd like to have a list of several "objects", each of them having the same numeric attributes (A, B, C). This list should then be sorted by the value of attribute A.

In Java I'd define a Class with my attributes as members, implement Sortable to compare A, put them all in some sort of List and let Collections.sort sort my list.

The result should "look" like that:

A   B   C
1   2   3
1   2   4
2   5   1
3   1   1

What is the best way to do something like that in Python?

+4  A: 

Sorry, If I took your question wrong way. I don't get it very well.

So, I will assume like you want to sort by column

Lets say x is 2 dimensional array

>>> x=[[1, 2, 3], [1, 2, 4], [2, 5, 1], [3, 1, 1]]
>>> x
[[1, 2, 3], [1, 2, 4], [2, 5, 1], [3, 1, 1]]

here is one way to do sorting by each column, using itemgetter

from operator import itemgetter

>>> sorted(x,key=itemgetter(0))
[[1, 2, 4], [1, 2, 3], [2, 5, 1], [3, 1, 1]]
>>> sorted(x,key=itemgetter(1))
[[3, 1, 1], [1, 2, 4], [1, 2, 3], [2, 5, 1]]
>>> sorted(x,key=itemgetter(2))
[[3, 1, 1], [2, 5, 1], [1, 2, 3], [1, 2, 4]]

If you want in-place sorting, please do like x.sort(key=itemgetter(0))

S.Mark
-1 for not answering the question. The OP specifically requested a way to sort **objects** having attributes A, B and C; not lists of 3 items.
Don O'Donnell
+2  A: 
List = [(3,1,1),(1,2,4),(2,5,1),(1,2,3)]
sorted(List)

Update: The heart of the answer is actually the sorted built-in. I just put it on two lines to allow for incremental inspection that (a) the data structure chosen is a list of tuples, and (b) the sorting is done by sorted. As a commenter pointed out sorted() will in one go sort by A, then B, then C (whether this is required or not). Other answers make a good point about adding a key-picking function to the call to sorted(), to nail down which element will be used in the comparison. Again, whether this is advantageous or not is for the OP to judge. I wanted to offer a minimal solution.

Update1: I shuffled the elements of the list around, so it appeals more to sorting :).

ThomasH
This sorts by A then B then C, whereas S. Mark sorts just by A and really does ignore B and C for the sorting. The OP isn't clear (to me) if this simpler solution is acceptable or not.
gnibbler
I read the OP's "ignore" as "I don't care", so not actually ignoring them isn't a problem for me. What is a problem is the result looks identical to the input, so it's impossible to tell sorted() did something if you don't already know it, and ignores (the strongly implied, IMHO) question of "how to sort on B or C instead of A, too?" I'm a big fan to only using code to answer a question when possible, but this answer just doesn't hold up and needs explanation.
Roger Pate
-1 for not answering the question. The OP specifically requested a way to sort objects having attributes A, B and C; not lists of 3 items.
Don O'Donnell
@Don I took those letters to be meta-variables he just picked to explain the problem and to have something he could refer to, particularly as he quoted the term "object". So I guessed he wanted *some* container for three numbers, and the tuple seemed to be the best pythonic fit here.
ThomasH
@Roger Indeed, my presentation wasn't particularly pedagogic. I always expect that a poster would try out an answer and play with it, to better grasp it, especially when a potential solution is so short. As for potentially sorting by B or C instead of A, I though about that and didn't find any implication in that direction, so I went for The Simplest Thing that Could Possibly work.
ThomasH
+5  A: 
class myclass(object):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    def __repr__(self):
        return "(a=%s, b=%s, c=%s)" % (self.a, self.b, self.c)

>>> obj1 = myclass(1, 2, 3)
>>> obj2 = myclass(1, 2, 4)
>>> obj3 = myclass(2, 5, 1)
>>> obj4 = myclass(3, 1, 1)

>>> print sorted([obj1, obj2, obj3, obj4], key=lambda o: o.a)
[(a=1, b=2, c=3), (a=1, b=2, c=4), (a=2, b=5, c=1), (a=3, b=1, c=1)]
jellybean
Would help the example to use `"(a=%s, b=%s, c=%s)"` in `__repr__` and put the initial list in a non-sorted order to show that sorted() actually does something.
Roger Pate
@Roger Pate: Good point. Edited.
jellybean
+2  A: 

You can give a comparison function as first argument of the sort of a list See the code below:

class Foo:
    def __init__(self, a, b=0, c=0):
        self.a = a
        self.b = b
        self.c = c

    def __repr__(self):
        return "%d %d  %d" % (self.a, self.b, self.c)


foos = [Foo(2, 5, 1), Foo(1, 2, 4), Foo(3, 1, 1), Foo(1, 2, 3)]

def cmp_a(f1, f2): 
    if f1.a == f2.a:
        return 0
    elif f1.a < f2.a:
        return -1
    else:
        return 1

foos.sort(cmp_a)

for f in foos:
    print f
luc
While true and works, it seems 'cmp' functions are getting phased out in preference to 'key' functions, at least in Python.
Roger Pate
+4  A: 

Consider using a namedtuple to create your objects. (Have a look at is-there-a-tuple-data-structure-in-python.)

collections.namedtuple(typename, field_names[, verbose])

Returns a new tuple subclass named typename. The new subclass is used to create tuple-like objects that have fields accessible by attribute lookup as well as being indexable and iterable. Instances of the subclass also have a helpful docstring (with typename and field_names) and a helpful repr() method which lists the tuple contents in a name=value format.

A simple interactive session with A B C field names. Sorting is straightforward with key=lambda o:o.A:

>>> import collections
>>> mob=collections.namedtuple('myobj',('A','B','C'))
>>> mlist = [mob(3,1,1), mob(1,2,3), mob(1,2,4), mob(2,5,1)]
>>> mlist
[myobj(A=3, B=1, C=1), myobj(A=1, B=2, C=3), myobj(A=1, B=2, C=4), myobj(A=2, B=5, C=1)]
>>> for x in sorted(mlist,key=lambda o:o.A):
...     print x
...     
myobj(A=1, B=2, C=3)
myobj(A=1, B=2, C=4)
myobj(A=2, B=5, C=1)
myobj(A=3, B=1, C=1)
>>>
gimel