0

I know how to sort by an attribute from this question where the top answer suggests doing:

someList.sort(key=lambda x: x.someAttr, reverse=True)

and I have seen suggestions for sorting a sublist which suggest to do something like:

someList[i:j] = sorted(somelist[i:j])

but how can I sort a sublist by an attribute? I tried:

someList[i:j].sort(key=lambda x: x.someAttr, reverse=True)

but this did not work. Any suggestions?

user3494047
  • 1,643
  • 4
  • 31
  • 61
  • 2
    Why are doing an in-place sort of the slice (a new, different list) in the second case and using slice assignment to update the list in the first case? – Moses Koledoye Jul 03 '17 at 19:58
  • 5
    `someList[i:j] = sorted(somelist[i:j], key=lambda x: x.attr, reverse=True)` Why wouldn't this work? – cs95 Jul 03 '17 at 20:00
  • 1
    @cᴏʟᴅsᴘᴇᴇᴅ I Didn't know that for the sorted() method key is an argument. If you make it an answer i'll accept. Or just delete if it is too obvious. – user3494047 Jul 03 '17 at 20:03
  • `sorted()` takes the same arguments as `list.sort()`, just use your `key` and `reverse` attributes in that call. – Martijn Pieters Jul 03 '17 at 20:05
  • @cᴏʟᴅsᴘᴇᴇᴅ: so? That doesn't make the `sorted()` not take the same arguments.. – Martijn Pieters Jul 03 '17 at 20:07
  • I thought you were talking about `list[i:j].sort()`. Sorry. – cs95 Jul 03 '17 at 20:07

2 Answers2

4

You're on the right track. You can sort a sublist with sorted() and assign it to the same splice. Here's an example:

>>> class Foo:
...    def __init__(self, val):
...        self.val = val
...    def __repr__(self): 
...        return 'Foo(%d)' %self.val
...
>>>
>>> x = [Foo(5), Foo(3), Foo(2), Foo(7)]
>>> x
[Foo(5), Foo(3), Foo(2), Foo(7)]
>>> x[1:3] = sorted(x[1:3], key=lambda x: x.val)
>>> x
[Foo(5), Foo(2), Foo(3), Foo(7)]

This has sorted the middle two elements. For your use case, don't forget the reverse=True keyword argument in the call.


On a related note, someList[i:j].sort(key=lambda x: x.someAttr, reverse=True) doesn't work as you expected. Well, it does sort a sublist, however, when you splice the original list, you end up creating an anonymous copy and sorting that in-place, and then losing that sorted copy (it is not assigned to anything, it is garbage collected). The original list is not affected.

cs95
  • 379,657
  • 97
  • 704
  • 746
2

The reason your last method didn't work is because you're trying to sort the sublist in-place. When you do:

L[1:-1].sort(...)

You're basically creating a copy of the sublist L[1:-1]. But because you sorted the copy in place, no reference was ever made to copy, and the new sorted list was lost and later garbage collected.

Instead, you need to reassign the new sorted value of the sublist, to the old sublist. eg:

>>> l = [1, 3, 2, 4, 5]
>>> l[1:-1].sort() # Reference to sorted sublist is never saved
>>> l # List unchanged
[1, 3, 2, 4, 5]
>>> l[1:-1] = sorted(l[1:-1]) # Reassign the sorted sublist to the old one
>>> l # List is modfied
[1, 2, 3, 4, 5]

Here's an example pertaining more to your specific case:

>>> class UserId:
...     def __init__(self, id_number):
...         self.id_number = id_number
...     def __repr__(self):
...         return 'UserId(id_number={})'.format(self.id_number)
... 
>>> user_ids = [UserId(1), UserId(3), UserId(4), UserId(2), UserId(5)]
>>> user_ids[1:-1] = sorted(user_ids[1:-1], key=lambda u: u.id_number, reverse=True)
>>> user_ids
[UserId(id_number=1), UserId(id_number=4), UserId(id_number=3), UserId(id_number=2), UserId(id_number=5)]
cs95
  • 379,657
  • 97
  • 704
  • 746
Christian Dean
  • 22,138
  • 7
  • 54
  • 87