0

I am trying to sort a list:

[
    '[fc] EDW Ratio (10 degrees)', 
    ' [fc] EDW Ratio (45 degrees)', 
    ' [fc] EDW Ratio (60 degrees)', 
    ' [fc] EDW Ratio (25 degrees)', 
    ' [fc] EDW Ratio (20 degrees)', 
    ' [fc] EDW Ratio (30 degrees)', 
    ' [fc] EDW Ratio (15 degrees)', 
    ' [fc] EDW output factor (60 degrees)', 
    ' [fc] Quality index'
]

using the first part of the accepted answer here:

But the list is ending up like this:

[
    ' [fc] EDW Ratio (15 degrees)', 
    ' [fc] EDW Ratio (20 degrees)', 
    ' [fc] EDW Ratio (25 degrees)', 
    ' [fc] EDW Ratio (30 degrees)', 
    ' [fc] EDW Ratio (45 degrees)', 
    ' [fc] EDW Ratio (60 degrees)', 
    ' [fc] EDW output factor (60 degrees)', 
    ' [fc] Quality index', 
    '[fc] EDW Ratio (10 degrees)'
]

whereas I want EDW Ratio (10 degrees) to end up at the start of the list after sorting (index position 0).

How can this be done?

My code includes the following:

#
# Method to define natural sorting used to sort lists
#
def atoi(text):
    return int(text) if text.isdigit() else text

def natural_keys(text):
    '''
    alist.sort(key=natural_keys) sorts in human order
    http://nedbatchelder.com/blog/200712/human_sorting.html
    (See Toothy's implementation in the comments)
    '''
    return [ atoi(c) for c in re.split(r'(\d+)', text) ]

    .
    .
    .


    tname_list = test_names.split(",") # this outputs the exact first (unsorted) list shown above

    tname_list.sort(key=natural_keys) # use human sorting defined above. This outputs the second list shown above.
2one
  • 1,035
  • 3
  • 17
  • 36

5 Answers5

3

Your code is correct, but your data look incorrect: all the entries have a leading whitespace, which implies they are "before" the one you identify as least, that actually have no leading whitespace.

If the data is fine as they are I suggest you to revise the code to ignore leading whitespaces (check this: How do I remove leading whitespace in Python?).

Fabio Veronese
  • 7,726
  • 2
  • 18
  • 27
  • Some other good answers to make the code more robust but this was the issue for now. I added in line of code as indicated below and that had the intended effect. Thanks. – 2one Nov 15 '19 at 09:44
  • `tname_list = [x.lstrip() for x in tname_list] # remove white space from start of list entries before sort` – 2one Nov 15 '19 at 09:45
0

You need to modify natural_keys to only return the numerical part of the string as an int. You should use int() for the conversion instead of atoi() which returns the ascii code of a character.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
0

You're going to run into trouble if any of your strings contain more than one number, or put the numbers at the beginning or end of the string. That's because Python can't compare an int and str to each other. Your key function should return both as a tuple or list.

def atoi(text):
    return (int(text), '') if text.isdigit() else (math.nan, text)

math.nan is special, because it will never compare less than an actual number.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
0

I recommend using natsort (full disclosure, I am the author). Your data is also a bit messy, you need to remove the leading whitespace to normalize all the entries.

from natsort import natsorted
data = [
    '[fc] EDW Ratio (10 degrees)', 
    ' [fc] EDW Ratio (45 degrees)', 
    ' [fc] EDW Ratio (60 degrees)', 
    ' [fc] EDW Ratio (25 degrees)', 
    ' [fc] EDW Ratio (20 degrees)', 
    ' [fc] EDW Ratio (30 degrees)', 
    ' [fc] EDW Ratio (15 degrees)', 
    ' [fc] EDW output factor (60 degrees)', 
    ' [fc] Quality index'
]
data_sorted = natsorted(data, key=lambda x: x.lstrip())

Outputs

[
 '[fc] EDW Ratio (10 degrees)',
 ' [fc] EDW Ratio (15 degrees)',
 ' [fc] EDW Ratio (20 degrees)',
 ' [fc] EDW Ratio (25 degrees)',
 ' [fc] EDW Ratio (30 degrees)',
 ' [fc] EDW Ratio (45 degrees)',
 ' [fc] EDW Ratio (60 degrees)',
 ' [fc] EDW output factor (60 degrees)',
 ' [fc] Quality index',
]
SethMMorton
  • 45,752
  • 12
  • 65
  • 86
0
import re

def get_numbers(texto):
    return int(re.findall(r'[0-9]+', texto)[0])
        
def sort_list(l):
    dicto = {}
    for i in l:
        dicto[get_numbers(i)] = i
    lista = []
    for i in sorted(list(dicto.keys())):
        lista.append(dicto[i])
    return lista

sort_list(frames)

NOTE that it will only work for the first serie of numbers ... "peter123jjj111" will only take 123 into account

Javi
  • 913
  • 2
  • 13
  • 15