0

I have the following list of tuples already sorted, with "sorted" in python:

L = [("1","blaabal"),
     ("1.2","bbalab"),
     ("10","ejej"),
     ("11.1","aaua"),
     ("12.1","ehjej"),
     ("12.2 (c)", "ekeke"), 
     ("12.2 (d)", "qwerty"), 
     ("2.1","baala"),
     ("3","yuio"),
     ("4","poku"),
     ("5.2","qsdfg")]

My problem is as you can notice, at first it is good, though after "12.2 (d)" the list restart at "2.1",I don't how to solve this problem.

Thanks

7
  • What were you expecting? The numbers were sorted as strings so '12...' comes before '2' Commented Oct 4, 2016 at 12:59
  • It's because they're sorted as strings lexicographically. Commented Oct 4, 2016 at 12:59
  • That happens because your tuples contain strings and strings are sorted lexicographically. The answer by Suever will work, but ask yourself why they are strings to begin with. Commented Oct 4, 2016 at 12:59
  • 1
    Maybe this is even better duplicate: Does Python have a built in function for string natural sort? Commented Oct 4, 2016 at 13:00
  • Actually my main problem is dealing with "12.2 (d)", adding a third layer that cannot be turned into a string is complicated Commented Oct 4, 2016 at 13:01

3 Answers 3

6

There's a package made specifically for your case called natsort:

>>> from natsort import natsorted
>>> L = [('1', 'blaabal'), ('4', 'poku'), ('12.2 (c)', 'ekeke'), ('12.1', 'ehjej')]
>>> natsorted(L)
[('1', 'blaabal'), ('4', 'poku'), ('12.1', 'ehjej'), ('12.2 (c)', 'ekeke')]
Sign up to request clarification or add additional context in comments.

Comments

4

Since the first element in each tuple is a string, Python is performing lexographic sorting in which all strings that start with '1' come before strings that start with a '2'.

To get the sorting you desire, you'll want to treat the first entry as a float instead of a string.

We can use sorted along with a custom sorting function which converts the first entry to a float prior to sorting. It also keeps the second tuple element to handle the case when you may have non-unique first entries.

result = sorted(L, key = lambda x: (float(x[0].split()[0]), x[1]))

# [('1', 'blaabal'), ('1.2', 'bbalab'), ('2.1', 'baala'), ('3', 'yuio'), ('4', 'poku'), ('5.2', 'qsdfg'), ('10', 'ejej'), ('11.1', 'aaua'), ('12.1', 'ehjej'), ('12.2 (c)', 'ekeke'), ('12.2 (d)', 'qwerty')]

I had to add in a x[0].split()[0] so that we split the first tuple element at the space and only grab the first pieces since some have values such as '12.2 (d)' and we only want the '12.2'.

If the second part of that first element that we've discarded matters, then you could use a sorting function similar to the following which breaks that first element into pieces and converts just the first piece to a float and leaves the rest as strings.

def sorter(value):
    parts = value[0].split()

    # Convert the first part to a number and leave all other parts as strings
    parts[0] = float(parts[0]);

    return (parts, value[1])

result = sorted(L, key = sorter)

9 Comments

This won't work for some of the items. E.g "12.2 (d)"
@Farhan.K Thanks updated.
Wouldn't it be better to just type: sorted(L, key= lambda tup: float(tup[0].split()[0]) ? You should care only about the number as an identifier value.
@Nf4r In this case it would work since all of the first entries are unique. If they were not unique though, you'd likely want to use the second tuple element for sorting.
Well yea, it depends on the case, wheter u care about the 2nd item order or not. But seeing now "12.2 (c)" and "12.2 (d)" i think he do care about it, so your solution is fine.
|
-3

The first value of your tuples are strings, and are being sorted in lexicographic order. If you want them to remain strings, sort with

sorted(l, key = lambda x: float(x[0]))

1 Comment

x[0] might be a string like 12.2 (c), which is not a valid float literal.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.