Sort list of dictionaries

classic Classic list List threaded Threaded
16 messages Options
Reply | Threaded
Open this post in threaded view
|

Sort list of dictionaries

Charles Heizer
Hello,
I'm new to python and I'm trying to find the right way to solve this issue I have.

I'm trying to sort this list by name and then by version numbers. The problem I'm having is that I can not get the version numbers sorted with the highest at the top or sorted properly.

mylist = [{'name': u'com.google.earth', 'version': u'7.1.2.2041'},
{'name': u'com.google.earth', 'version': u'7.1.2.2019'},
{'name': u'com.google.Chrome', 'version': u'40.0.2214.93'},
{'name': u'com.google.Chrome', 'version': u'40.0.2214.91'},
{'name': u'com.google.Chrome', 'version': u'40.0.2214.111'},
{'name': u'com.google.Chrome', 'version': u'39.0.2171.99'},
{'name': u'com.google.Chrome', 'version': u'39.0.2171.95'},
{'name': u'com.google.Chrome', 'version': u'39.0.2171.71'},
{'name': u'com.google.Chrome', 'version': u'38.0.2125.122'},
{'name': u'com.google.Chrome', 'version': u'38.0.2125.111'},
{'name': u'com.google.Chrome', 'version': u'38.0.2125.104'},
{'name': u'com.google.Chrome', 'version': u'38.0.2125.101'},
{'name': u'com.google.Chrome', 'version': u'37.0.2062.94'},
{'name': u'com.google.Chrome', 'version': u'37.0.2062.120'},
{'name': u'com.google.Chrome', 'version': u'36.0.1985.143'},
{'name': u'com.google.Chrome', 'version': u'36.0.1985.125'},
{'name': u'com.google.Chrome', 'version': u'35.0.1916.153'},
{'name': u'com.google.Chrome', 'version': u'35.0.1916.114'},
{'name': u'com.google.Chrome', 'version': u'34.0.1847.137'},
{'name': u'com.google.Chrome', 'version': u'34.0.1847.131'},
{'name': u'com.google.Chrome', 'version': u'34.0.1847.116'},
{'name': u'com.google.Chrome', 'version': u'33.0.1750.152'},
{'name': u'com.google.Chrome', 'version': u'33.0.1750.149'},
{'name': u'com.google.Chrome', 'version': u'33.0.1750.146'},
{'name': u'com.google.Chrome', 'version': u'32.0.1700.107'},
{'name': u'com.google.Chrome', 'version': u'31.0.1650.63'},
{'name': u'com.google.Chrome', 'version': u'31.0.1650.57'}]

sortedlist = sorted(mylist , key=lambda x, y: x['name'] LooseVersion(elem['version'])), reverse=True)

Thanks,
Charlie


Reply | Threaded
Open this post in threaded view
|

Sort list of dictionaries

Emile van Sebille
On 3/2/2015 10:17 AM, Charles Heizer wrote:

> Hello,
> I'm new to python and I'm trying to find the right way to solve this issue I have.
>
> I'm trying to sort this list by name and then by version numbers. The problem I'm having is that I can not get the version numbers sorted with the highest at the top or sorted properly.
>
> mylist = [{'name': u'com.google.earth', 'version': u'7.1.2.2041'},
> {'name': u'com.google.earth', 'version': u'7.1.2.2019'},
> {'name': u'com.google.Chrome', 'version': u'40.0.2214.93'},
> {'name': u'com.google.Chrome', 'version': u'40.0.2214.91'},
> {'name': u'com.google.Chrome', 'version': u'40.0.2214.111'},
> {'name': u'com.google.Chrome', 'version': u'39.0.2171.99'},
> {'name': u'com.google.Chrome', 'version': u'39.0.2171.95'},
> {'name': u'com.google.Chrome', 'version': u'39.0.2171.71'},
> {'name': u'com.google.Chrome', 'version': u'38.0.2125.122'},
> {'name': u'com.google.Chrome', 'version': u'38.0.2125.111'},
> {'name': u'com.google.Chrome', 'version': u'38.0.2125.104'},
> {'name': u'com.google.Chrome', 'version': u'38.0.2125.101'},
> {'name': u'com.google.Chrome', 'version': u'37.0.2062.94'},
> {'name': u'com.google.Chrome', 'version': u'37.0.2062.120'},
> {'name': u'com.google.Chrome', 'version': u'36.0.1985.143'},
> {'name': u'com.google.Chrome', 'version': u'36.0.1985.125'},
> {'name': u'com.google.Chrome', 'version': u'35.0.1916.153'},
> {'name': u'com.google.Chrome', 'version': u'35.0.1916.114'},
> {'name': u'com.google.Chrome', 'version': u'34.0.1847.137'},
> {'name': u'com.google.Chrome', 'version': u'34.0.1847.131'},
> {'name': u'com.google.Chrome', 'version': u'34.0.1847.116'},
> {'name': u'com.google.Chrome', 'version': u'33.0.1750.152'},
> {'name': u'com.google.Chrome', 'version': u'33.0.1750.149'},
> {'name': u'com.google.Chrome', 'version': u'33.0.1750.146'},
> {'name': u'com.google.Chrome', 'version': u'32.0.1700.107'},
> {'name': u'com.google.Chrome', 'version': u'31.0.1650.63'},
> {'name': u'com.google.Chrome', 'version': u'31.0.1650.57'}]
>
> sortedlist = sorted(mylist , key=lambda x, y: x['name'] LooseVersion(elem['version'])), reverse=True)

You'll need to fix LooseVersion or elem or both -- or show them so we
can help.

Emile



>
> Thanks,
> Charlie
>




Reply | Threaded
Open this post in threaded view
|

Sort list of dictionaries

Charles Heizer
In reply to this post by Charles Heizer
Sorry,

sortedlist = sorted(mylist , key=lambda elem: "%s %s" % (elem['name'], LooseVersion(elem['version'])), reverse=True)

This is what I was trying but LooseVersion() was not sorting version numbers like I thought it would. You will notice that Chrome version "40.0.2214.111" is higher than "40.0.2214.91" but in the end result it's not sorting it that way.

Thanks,
Charlie

On Monday, March 2, 2015 at 10:32:40 AM UTC-8, Emile van Sebille wrote:

> On 3/2/2015 10:17 AM, Charles Heizer wrote:
> > Hello,
> > I'm new to python and I'm trying to find the right way to solve this issue I have.
> >
> > I'm trying to sort this list by name and then by version numbers. The problem I'm having is that I can not get the version numbers sorted with the highest at the top or sorted properly.
> >
> > mylist = [{'name': u'com.google.earth', 'version': u'7.1.2.2041'},
> > {'name': u'com.google.earth', 'version': u'7.1.2.2019'},
> > {'name': u'com.google.Chrome', 'version': u'40.0.2214.93'},
> > {'name': u'com.google.Chrome', 'version': u'40.0.2214.91'},
> > {'name': u'com.google.Chrome', 'version': u'40.0.2214.111'},
> > {'name': u'com.google.Chrome', 'version': u'39.0.2171.99'},
> > {'name': u'com.google.Chrome', 'version': u'39.0.2171.95'},
> > {'name': u'com.google.Chrome', 'version': u'39.0.2171.71'},
> > {'name': u'com.google.Chrome', 'version': u'38.0.2125.122'},
> > {'name': u'com.google.Chrome', 'version': u'38.0.2125.111'},
> > {'name': u'com.google.Chrome', 'version': u'38.0.2125.104'},
> > {'name': u'com.google.Chrome', 'version': u'38.0.2125.101'},
> > {'name': u'com.google.Chrome', 'version': u'37.0.2062.94'},
> > {'name': u'com.google.Chrome', 'version': u'37.0.2062.120'},
> > {'name': u'com.google.Chrome', 'version': u'36.0.1985.143'},
> > {'name': u'com.google.Chrome', 'version': u'36.0.1985.125'},
> > {'name': u'com.google.Chrome', 'version': u'35.0.1916.153'},
> > {'name': u'com.google.Chrome', 'version': u'35.0.1916.114'},
> > {'name': u'com.google.Chrome', 'version': u'34.0.1847.137'},
> > {'name': u'com.google.Chrome', 'version': u'34.0.1847.131'},
> > {'name': u'com.google.Chrome', 'version': u'34.0.1847.116'},
> > {'name': u'com.google.Chrome', 'version': u'33.0.1750.152'},
> > {'name': u'com.google.Chrome', 'version': u'33.0.1750.149'},
> > {'name': u'com.google.Chrome', 'version': u'33.0.1750.146'},
> > {'name': u'com.google.Chrome', 'version': u'32.0.1700.107'},
> > {'name': u'com.google.Chrome', 'version': u'31.0.1650.63'},
> > {'name': u'com.google.Chrome', 'version': u'31.0.1650.57'}]
> >
> > sortedlist = sorted(mylist , key=lambda x, y: x['name'] LooseVersion(elem['version'])), reverse=True)
>
> You'll need to fix LooseVersion or elem or both -- or show them so we
> can help.
>
> Emile
>
>
>
> >
> > Thanks,
> > Charlie
> >


Reply | Threaded
Open this post in threaded view
|

Sort list of dictionaries

Ian Kelly-2
On Mon, Mar 2, 2015 at 11:38 AM, Charles Heizer <ceh329 at gmail.com> wrote:
> Sorry,
>
> sortedlist = sorted(mylist , key=lambda elem: "%s %s" % (elem['name'], LooseVersion(elem['version'])), reverse=True)
>
> This is what I was trying but LooseVersion() was not sorting version numbers like I thought it would. You will notice that Chrome version "40.0.2214.111" is higher than "40.0.2214.91" but in the end result it's not sorting it that way.

Because it's a string they're sorted lexicographically, and in that
ordering "40.0.2214.111" is less than "40.0.2214.91". Instead of a
string you should probably use some sort of version info tuple. A
simple tuple of ints may suffice, although you may need to get a
little cleverer if there are ever any version strings that aren't
entirely dotted numeric.


Reply | Threaded
Open this post in threaded view
|

Sort list of dictionaries

Charles Heizer
In reply to this post by Charles Heizer
Never mind, the light bulb finally went off. :-\

sortedlist = sorted(mylist , key=lambda elem: "%s %s" % ( elem['name'], (".".join([i.zfill(5) for i in elem['version'].split(".")])) ), reverse=True)

On Monday, March 2, 2015 at 10:40:30 AM UTC-8, Charles Heizer wrote:

> Sorry,
>
> sortedlist = sorted(mylist , key=lambda elem: "%s %s" % (elem['name'], LooseVersion(elem['version'])), reverse=True)
>
> This is what I was trying but LooseVersion() was not sorting version numbers like I thought it would. You will notice that Chrome version "40.0.2214.111" is higher than "40.0.2214.91" but in the end result it's not sorting it that way.
>
> Thanks,
> Charlie
>
> On Monday, March 2, 2015 at 10:32:40 AM UTC-8, Emile van Sebille wrote:
> > On 3/2/2015 10:17 AM, Charles Heizer wrote:
> > > Hello,
> > > I'm new to python and I'm trying to find the right way to solve this issue I have.
> > >
> > > I'm trying to sort this list by name and then by version numbers. The problem I'm having is that I can not get the version numbers sorted with the highest at the top or sorted properly.
> > >
> > > mylist = [{'name': u'com.google.earth', 'version': u'7.1.2.2041'},
> > > {'name': u'com.google.earth', 'version': u'7.1.2.2019'},
> > > {'name': u'com.google.Chrome', 'version': u'40.0.2214.93'},
> > > {'name': u'com.google.Chrome', 'version': u'40.0.2214.91'},
> > > {'name': u'com.google.Chrome', 'version': u'40.0.2214.111'},
> > > {'name': u'com.google.Chrome', 'version': u'39.0.2171.99'},
> > > {'name': u'com.google.Chrome', 'version': u'39.0.2171.95'},
> > > {'name': u'com.google.Chrome', 'version': u'39.0.2171.71'},
> > > {'name': u'com.google.Chrome', 'version': u'38.0.2125.122'},
> > > {'name': u'com.google.Chrome', 'version': u'38.0.2125.111'},
> > > {'name': u'com.google.Chrome', 'version': u'38.0.2125.104'},
> > > {'name': u'com.google.Chrome', 'version': u'38.0.2125.101'},
> > > {'name': u'com.google.Chrome', 'version': u'37.0.2062.94'},
> > > {'name': u'com.google.Chrome', 'version': u'37.0.2062.120'},
> > > {'name': u'com.google.Chrome', 'version': u'36.0.1985.143'},
> > > {'name': u'com.google.Chrome', 'version': u'36.0.1985.125'},
> > > {'name': u'com.google.Chrome', 'version': u'35.0.1916.153'},
> > > {'name': u'com.google.Chrome', 'version': u'35.0.1916.114'},
> > > {'name': u'com.google.Chrome', 'version': u'34.0.1847.137'},
> > > {'name': u'com.google.Chrome', 'version': u'34.0.1847.131'},
> > > {'name': u'com.google.Chrome', 'version': u'34.0.1847.116'},
> > > {'name': u'com.google.Chrome', 'version': u'33.0.1750.152'},
> > > {'name': u'com.google.Chrome', 'version': u'33.0.1750.149'},
> > > {'name': u'com.google.Chrome', 'version': u'33.0.1750.146'},
> > > {'name': u'com.google.Chrome', 'version': u'32.0.1700.107'},
> > > {'name': u'com.google.Chrome', 'version': u'31.0.1650.63'},
> > > {'name': u'com.google.Chrome', 'version': u'31.0.1650.57'}]
> > >
> > > sortedlist = sorted(mylist , key=lambda x, y: x['name'] LooseVersion(elem['version'])), reverse=True)
> >
> > You'll need to fix LooseVersion or elem or both -- or show them so we
> > can help.
> >
> > Emile
> >
> >
> >
> > >
> > > Thanks,
> > > Charlie
> > >


Reply | Threaded
Open this post in threaded view
|

Sort list of dictionaries

Dave Angel-4
In reply to this post by Charles Heizer
On 03/02/2015 01:38 PM, Charles Heizer wrote:
> Sorry,
>
> sortedlist = sorted(mylist , key=lambda elem: "%s %s" % (elem['name'], LooseVersion(elem['version'])), reverse=True)
>
> This is what I was trying but LooseVersion() was not sorting version numbers like I thought it would. You will notice that Chrome version "40.0.2214.111" is higher than "40.0.2214.91" but in the end result it's not sorting it that way.
>

Please don't top-post.  put your remarks after whatever quoting you do.

You still haven't posted your LooseVersion() function source.  I agree
with Emile that it's likely to be the problem.


--
DaveA


Reply | Threaded
Open this post in threaded view
|

Sort list of dictionaries

Peter Otten
In reply to this post by Charles Heizer
Charles Heizer wrote:

> Never mind, the light bulb finally went off. :-\
>
> sortedlist = sorted(mylist , key=lambda elem: "%s %s" % ( elem['name'],
> (".".join([i.zfill(5) for i in elem['version'].split(".")])) ),
> reverse=True)

This lightbulb will break with version numbers > 99999 ;)

Here are two alternatives:

result = sorted(
    mylist,
    key=lambda elem: (elem['name'], LooseVersion(elem['version'])),
    reverse=True)

result = sorted(
    mylist,
    key=lambda e: (e["name"], tuple(map(int, e["version"].split(".")))),
    reverse=True)


Personally, I prefer to not use a lambda:

def name_version(elem):
    return elem['name'], LooseVersion(elem['version'])
   
result = sorted(mylist, key=name_version, reverse=True)




Reply | Threaded
Open this post in threaded view
|

Sort list of dictionaries

Jason Friedman
In reply to this post by Ian Kelly-2
>> This is what I was trying but LooseVersion() was not sorting version numbers like I thought it would. You will notice that Chrome version "40.0.2214.111" is higher than "40.0.2214.91" but in the end result it's not sorting it that way.
>
> Because it's a string they're sorted lexicographically, and in that
> ordering "40.0.2214.111" is less than "40.0.2214.91". Instead of a
> string you should probably use some sort of version info tuple. A
> simple tuple of ints may suffice, although you may need to get a
> little cleverer if there are ever any version strings that aren't
> entirely dotted numeric.

Also, Python 3.4 comes with an ipaddress module.

>>> import ipaddress
>>> address_string_1 = "2.2.3.4"
>>> address_string_2 = "10.2.3.4"
>>> address_string_2 > address_string_1
False
>>> ip_address_1 = ipaddress.ip_address(address_string_1)
>>> ip_address_2 = ipaddress.ip_address(address_string_2)
>>> ip_address_2 > ip_address_1
True


Reply | Threaded
Open this post in threaded view
|

Sort list of dictionaries

Chris Angelico
On Tue, Mar 3, 2015 at 4:33 PM, Jason Friedman <jsf80238 at gmail.com> wrote:

>>> This is what I was trying but LooseVersion() was not sorting version numbers like I thought it would. You will notice that Chrome version "40.0.2214.111" is higher than "40.0.2214.91" but in the end result it's not sorting it that way.
>>
>> Because it's a string they're sorted lexicographically, and in that
>> ordering "40.0.2214.111" is less than "40.0.2214.91". Instead of a
>> string you should probably use some sort of version info tuple. A
>> simple tuple of ints may suffice, although you may need to get a
>> little cleverer if there are ever any version strings that aren't
>> entirely dotted numeric.
>
> Also, Python 3.4 comes with an ipaddress module.

Heh, I think that miiiiiiiight be a bit abusive :) I'm not sure that
you want to depend on the version numbers fitting inside the rules for
IP addresses, especially given that the example has a component of
"2214".

The right way to compare version numbers is usually to split them on
dots, then treat each part as a separate number. I say "usually"
because there are complications like "alpha", "RC", "-git", "+deb7u1",
and so on, but if the entire version number consists of digits and
dots, then tuple(int(x) for x in version_number.split(".")) will give
you a properly-sortable key.

BTW, Jason: It's usually courteous to acknowledge who you're quoting.
If you look at the top of my post here, you'll see that there's a line
saying your name and email address, and the date/time that you made
your post; but underneath that is just straight text, because your
post didn't extend the same courtesy to the previous posters. When you
trim quoted text, do please try to keep at least the first
acknowledgement line - the one showing who you're actually quoting.
Whether or not you keep additional headers is up to you (sometimes
it's easy, but other times it's fiddly), but the first one is your
responsibility. Thanks!

ChrisA


Reply | Threaded
Open this post in threaded view
|

Sort list of dictionaries

Steven D'Aprano-11
In reply to this post by Charles Heizer
Charles Heizer wrote:

> Hello,
> I'm new to python and I'm trying to find the right way to solve this issue
> I have.
>
> I'm trying to sort this list by name and then by version numbers. The
> problem I'm having is that I can not get the version numbers sorted with
> the highest at the top or sorted properly.
>
> mylist = [{'name': u'com.google.earth', 'version': u'7.1.2.2041'},
> {'name': u'com.google.earth', 'version': u'7.1.2.2019'},
> {'name': u'com.google.Chrome', 'version': u'40.0.2214.93'},
> {'name': u'com.google.Chrome', 'version': u'40.0.2214.91'},
[...]
> ]
>
> sortedlist = sorted(mylist , key=lambda x, y: x['name']
> LooseVersion(elem['version'])), reverse=True)

There's a syntax error there. Is there supposed to be a comma between the
x['name'] and the LooseVersion function call?

Once you've fixed the syntax error, you can then fix the mysterious elem,
and the fact that your key function takes too many arguments.

The key function should take *one* argument, which in your case is a dict.
So:

def sort_by_version(d):
    name = d['name']
    version = d['version']
    return (name, [int(substr) for substr in version.split('.')])


sortedlist = sorted(mylist, key=sort_by_version)


should get you started. It may not quiet do what you want, but you can
adjust the sort_by_version function as needed.



--
Steve



Reply | Threaded
Open this post in threaded view
|

Sort list of dictionaries

Jason Friedman
In reply to this post by Chris Angelico
On Tue, Mar 3, 2015 at 12:07 AM, Chris Angelico <rosuav at gmail.com> wrote:

> Heh, I think that miiiiiiiight be a bit abusive :) I'm not sure that
> you want to depend on the version numbers fitting inside the rules for
> IP addresses, especially given that the example has a component of
> "2214".
>

Indeed, I was mistaken

>>> ipaddress.ip_address("1000.1.2.3")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/python/lib/python3.4/ipaddress.py", line 54, in ip_address
    address)
ValueError: '1000.1.2.3' does not appear to be an IPv4 or IPv6 address

I was overcome by how neat I think the ipaddress module is.
I have not used it yet, but I expect someday I will, and I appreciate
how Python makes my job easier by doing much of my work for me.  A
colleague yesterday asked how I guaranteed my temporary file names
would not collide with another file, and my answer was I don't have to
worry about it, someone else figured it out with the tempfile module.


Reply | Threaded
Open this post in threaded view
|

Sort list of dictionaries

Chris Angelico
On Wed, Mar 4, 2015 at 1:45 AM, Jason Friedman <jsf80238 at gmail.com> wrote:
> I appreciate
> how Python makes my job easier by doing much of my work for me.  A
> colleague yesterday asked how I guaranteed my temporary file names
> would not collide with another file, and my answer was I don't have to
> worry about it, someone else figured it out with the tempfile module.

Though in that case, Python isn't actually the one doing most of the
work - the tempfile module handballs responsibility to the lower-level
functions. But yes, there's HEAPS that you don't have to worry about,
because it's Someone Else's Problem.

ChrisA


Reply | Threaded
Open this post in threaded view
|

Sort list of dictionaries

Charles Heizer
In reply to this post by Charles Heizer
On Monday, March 2, 2015 at 11:23:37 AM UTC-8, Peter Otten wrote:

> Charles Heizer wrote:
>
> > Never mind, the light bulb finally went off. :-\
> >
> > sortedlist = sorted(mylist , key=lambda elem: "%s %s" % ( elem['name'],
> > (".".join([i.zfill(5) for i in elem['version'].split(".")])) ),
> > reverse=True)
>
> This lightbulb will break with version numbers > 99999 ;)
>
> Here are two alternatives:
>
> result = sorted(
>     mylist,
>     key=lambda elem: (elem['name'], LooseVersion(elem['version'])),
>     reverse=True)
>
> result = sorted(
>     mylist,
>     key=lambda e: (e["name"], tuple(map(int, e["version"].split(".")))),
>     reverse=True)
>
>
> Personally, I prefer to not use a lambda:
>
> def name_version(elem):
>     return elem['name'], LooseVersion(elem['version'])
>    
> result = sorted(mylist, key=name_version, reverse=True)

Peter, thank you. Me being new to Python why don't you prefer to use a lambda?


Reply | Threaded
Open this post in threaded view
|

Sort list of dictionaries

Chris Angelico
On Wed, Mar 4, 2015 at 2:56 AM, Charles Heizer <ceh329 at gmail.com> wrote:
>> Personally, I prefer to not use a lambda:
>>
>> def name_version(elem):
>>     return elem['name'], LooseVersion(elem['version'])
>>
>> result = sorted(mylist, key=name_version, reverse=True)
>
> Peter, thank you. Me being new to Python why don't you prefer to use a lambda?

Using lambda is fine if it's really clear what's going on (usually, if
it's an extremely simple function), but if your expression goes across
multiple lines because of the function parameter, it's usually simpler
to break the function out into a separate def statement and then use
that. There's ultimately no difference[1] between a lambda function
and a def function, apart from the fact that a function created with
lambda always has the name "<lambda>".

ChrisA

[1] Modulo bugs, eg a weird edge case with lambda and yield; certainly
no intentional difference.


Reply | Threaded
Open this post in threaded view
|

Sort list of dictionaries

Paul Moore
In reply to this post by Charles Heizer
On Tuesday, 3 March 2015 16:09:31 UTC, Chris Angelico  wrote:

> On Wed, Mar 4, 2015 at 2:56 AM, Charles Heizer wrote:
> >> Personally, I prefer to not use a lambda:
> >>
> >> def name_version(elem):
> >>     return elem['name'], LooseVersion(elem['version'])
> >>
> >> result = sorted(mylist, key=name_version, reverse=True)
> >
> > Peter, thank you. Me being new to Python why don't you prefer to use a lambda?
>
> Using lambda is fine if it's really clear what's going on (usually, if
> it's an extremely simple function), but if your expression goes across
> multiple lines because of the function parameter, it's usually simpler
> to break the function out into a separate def statement and then use
> that. There's ultimately no difference[1] between a lambda function
> and a def function, apart from the fact that a function created with
> lambda always has the name "<lambda>".
>
> ChrisA
>
> [1] Modulo bugs, eg a weird edge case with lambda and yield; certainly
> no intentional difference.

The main point is that a def gives your function a name, whereas lambda is unnamed. It sometimes feels harder to have to think of a name for something that seems simple, like your key function. But when you come back to it in a few months, the name is incredibly useful documentation as to what's going on.

Paul


Reply | Threaded
Open this post in threaded view
|

Sort list of dictionaries

Peter Otten
In reply to this post by Charles Heizer
Charles Heizer wrote:

> On Monday, March 2, 2015 at 11:23:37 AM UTC-8, Peter Otten wrote:
>> Charles Heizer wrote:
>>
>> > Never mind, the light bulb finally went off. :-\
>> >
>> > sortedlist = sorted(mylist , key=lambda elem: "%s %s" % ( elem['name'],
>> > (".".join([i.zfill(5) for i in elem['version'].split(".")])) ),
>> > reverse=True)
>>
>> This lightbulb will break with version numbers > 99999 ;)
>>
>> Here are two alternatives:
>>
>> result = sorted(
>>     mylist,
>>     key=lambda elem: (elem['name'], LooseVersion(elem['version'])),
>>     reverse=True)
>>
>> result = sorted(
>>     mylist,
>>     key=lambda e: (e["name"], tuple(map(int, e["version"].split(".")))),
>>     reverse=True)
>>
>>
>> Personally, I prefer to not use a lambda:
>>
>> def name_version(elem):
>>     return elem['name'], LooseVersion(elem['version'])
>>    
>> result = sorted(mylist, key=name_version, reverse=True)
>
> Peter, thank you. Me being new to Python why don't you prefer to use a
> lambda?

I find

def name_version(elem):
    return elem['name'], LooseVersion(elem['version'])

more readable than

lambda elem: (elem['name'], LooseVersion(elem['version']))

and I can understand what

>> result = sorted(mylist, key=name_version, reverse=True)

is supposed to do without grokking the implementation of name_version()
first. I can spot a potential bug -- are the names really supposed to occur
in reverse order, not just the versions? -- again without a look at the
implementation.

If I intend to use the script more than once or if I want to rely on the
result I can write unit tests for name_version() to increase confidence that
it does what I expect.

Finally I can add a docstring to make it more discoverable in the
interactive interpreter.