views:

1274

answers:

3

Ok, I've got a list like this (just a sample of data):

data = {"NAME": "James", "RANK": "3.0", "NUM": "27.5" ... }

Now, if I run something like this:

sortby = "NAME" //this gets passed to the function, hence why I am using a variable sortby instead
data.sort(key=itemgetter(sortby))

I get all the strings sorted properly - alphabetically.

However, when "sortby" is any of the floating values (RANK or NUM or any other), sort is done again, alphabetically, instead of numerically, so my sorted list looks something like this then:

0.441 101.404 107.558 107.558 108.48 108.945 11.195 12.143 12.801 131.73

which is obviously wrong.

Now, how can I do a sort like that (most efficiently in terms of speed and resources/computations taken) but have it cast the value somehow to float when it's a float, and leave it as a string when it's a string... possible? And no, removing quotes from float values in the list is not an option - I don't have control over the source list, unfortunately (and I know, that would've been an easy solution).

A: 

Slightly more verbose than just passing a field name, but this is an option:

sort_by_name = lambda x: x['name']
sort_by_rank = lambda x: float(x['RANK'])
# etc...

data.sort(key=sort_by_rank)

If the data is much more dense than what you've posted, you might want a separate dictionary mapping field names to data types, and then a factory function to produce sorters suitable for the key argument to list.sort()

Triptych
+4  A: 

if you cant save your data properly ( floats as floats ), something like this

sorters = { "NAME" : itemgetter("NAME"), 
            "RANK" : lambda x: float(x["RANK"]),
            "NUM" : lambda x: float(x["NUM"])
}

data.sort(key=sorters[sortby])
THC4k
I like that... never thought of a separate dictionary for sort types. Thanks! The only issue is that there are 50+ columns that I need to cast to float like this, but it is the smoothest approach so far...
Crazy Serb
Check out my answer, no need for a separate dictionary. This is a good answer, but you do have to set up that 50-entry dict...
Vinay Sajip
+1 — I can certainly see this getting cumbersome as your column count grows, but I really like both the clarity and flexibility.
Ben Blank
+6  A: 

If you want a general function which you can pass as a parameter to sort(key=XXX), then here's a candidate complete with test:

DATA = [
    { 'name' : 'A', 'value' : '10.0' },
    { 'name' : 'B', 'value' : '2.0' },
]

def get_attr(name):
    def inner_func(o):
        try:
            rv = float(o[name])
        except ValueError:
            rv = o[name]
        return rv
    return inner_func

for attrname in ('name', 'value'):
    DATA.sort(key=get_attr(attrname))
    print "%r-sorted: %s" % (attrname, DATA)

When you run the above script, you get:

'name'-sorted: [{'name': 'A', 'value': '10.0'}, {'name': 'B', 'value': '2.0'}]
'value'-sorted: [{'name': 'B', 'value': '2.0'}, {'name': 'A', 'value': '10.0'}]
Vinay Sajip
Slick.I like that even more... thank you.
Crazy Serb
You gotta love Python :-)
Vinay Sajip
+1 — Very DWIMish, and could probably be extended to other types fairly easily.
Ben Blank