So sortieren Sie alphanumerische Sätze in Python


72

Ich habe einen Satz

set(['booklet', '4 sheets', '48 sheets', '12 sheets'])

Nach dem Sortieren soll es so aussehen

4 sheets,
12 sheets,
48 sheets,
booklet

Irgendeine Idee bitte

Antworten:


60

Kurz und bündig:

sorted(data, key=lambda item: (int(item.partition(' ')[0])
                               if item[0].isdigit() else float('inf'), item))

Diese Version:

  • Funktioniert in Python 2 und Python 3, weil:
    • Es wird nicht davon ausgegangen, dass Sie Zeichenfolgen und Ganzzahlen vergleichen (was in Python 3 nicht funktioniert).
    • Der cmpParameter to wird nicht verwendet sorted(was in Python 3 nicht vorhanden ist).
  • Sortiert nach dem String-Teil, wenn die Mengen gleich sind

Wenn Sie eine Druckausgabe genau wie in Ihrem Beispiel beschrieben wünschen, dann:

data = set(['booklet', '4 sheets', '48 sheets', '12 sheets'])
r = sorted(data, key=lambda item: (int(item.partition(' ')[0])
                                   if item[0].isdigit() else float('inf'), item))
print ',\n'.join(r)

würgt weiter, 4a sheetsaber wen interessiert das? Um dies zu beheben, benötigen Sie eine echte Funktion anstelle eines Lambda.
Jean-François Fabre

Das könnte für dieses triviale Beispiel funktionieren, aber nicht für eine Liste wie ["1. bla", "2. blub"]. Wahrscheinlich sollte der Split stattdessen ein regulärer Ausdruck sein und danach auch nach dem zweiten Teil sortiert werden, damit ["1 bcd", "2 abc", "1 xyz"] korrekt ausgegeben wird.
FrankyBoy

120

Jeff Atwood spricht über natürliche Sorte und gibt ein Beispiel für eine Möglichkeit, dies in Python zu tun. Hier ist meine Variation davon:

import re 

def sorted_nicely( l ): 
    """ Sort the given iterable in the way that humans expect.""" 
    convert = lambda text: int(text) if text.isdigit() else text 
    alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ] 
    return sorted(l, key = alphanum_key)

Verwenden Sie wie folgt:

s = set(['booklet', '4 sheets', '48 sheets', '12 sheets'])
for x in sorted_nicely(s):
    print(x)

Ausgabe:

4 sheets
12 sheets
48 sheets
booklet

Ein Vorteil dieser Methode ist, dass sie nicht nur funktioniert, wenn die Zeichenfolgen durch Leerzeichen getrennt sind. Es funktioniert auch für andere Trennzeichen, z. B. den Punkt in Versionsnummern (z. B. 1.9.1 steht vor 1.10.0).


Hallo Jeff, vielen Dank. Genau das habe ich gesucht. Viel Glück.
mmrs151

2
Ist es möglich, dies für eine Liste von Tupeln basierend auf dem ersten Wert im Tupel zu ändern? Beispiel: [('b', 0), ('0', 1), ('a', 2)]ist sortiert nach[('0', 1), ('a', 2), ('b', 0)]
paragbaxi

3
Diese Funktion unterscheidet zwischen Groß- und Kleinschreibung. Zeichenfolgen in Großbuchstaben haben Vorrang. Zur Behebung dieses Add .lower()zu keyin re.split.
Zamber

17

Sie sollten die Natsortierung der Drittanbieter-Bibliothek überprüfen . Sein Algorithmus ist allgemein, so dass er für die meisten Eingaben funktioniert.

>>> import natsort
>>> your_list = set(['booklet', '4 sheets', '48 sheets', '12 sheets'])
>>> print ',\n'.join(natsort.natsorted(your_list))
4 sheets,
12 sheets,
48 sheets,
booklet

9

Eine einfache Möglichkeit besteht darin, die Zeichenfolgen in numerische und nicht numerische Teile aufzuteilen und die Zeichenfolgen mithilfe der Python-Tupel-Sortierreihenfolge zu sortieren.

import re
tokenize = re.compile(r'(\d+)|(\D+)').findall
def natural_sortkey(string):          
    return tuple(int(num) if num else alpha for num, alpha in tokenize(string))

sorted(my_set, key=natural_sortkey)

5

Es wurde vorgeschlagen, diese Antwort hier erneut zu veröffentlichen, da sie auch in diesem Fall gut funktioniert

from itertools import groupby
def keyfunc(s):
    return [int(''.join(g)) if k else ''.join(g) for k, g in groupby(s, str.isdigit)]

sorted(my_list, key=keyfunc)

Demo:

>>> my_set = {'booklet', '4 sheets', '48 sheets', '12 sheets'}
>>> sorted(my_set, key=keyfunc)
['4 sheets', '12 sheets', '48 sheets', 'booklet']

Für Python3 muss es leicht geändert werden (diese Version funktioniert auch in Python2 einwandfrei).

def keyfunc(s):
    return [int(''.join(g)) if k else ''.join(g) for k, g in groupby('\0'+s, str.isdigit)]

2
>>> a = set(['booklet', '4 sheets', '48 sheets', '12 sheets'])
>>> def ke(s):
    i, sp, _ = s.partition(' ')
    if i.isnumeric():
        return int(i)
    return float('inf')

>>> sorted(a, key=ke)
['4 sheets', '12 sheets', '48 sheets', 'booklet']

2

Allgemeine Antwort zum Sortieren beliebiger Zahlen an einer beliebigen Position in einem Array von Zeichenfolgen. Funktioniert mit Python 2 & 3.

def alphaNumOrder(string):
   """ Returns all numbers on 5 digits to let sort the string with numeric order.
   Ex: alphaNumOrder("a6b12.125")  ==> "a00006b00012.00125"
   """
   return ''.join([format(int(x), '05d') if x.isdigit()
                   else x for x in re.split(r'(\d+)', string)])

Stichprobe:

s = ['a10b20','a10b1','a3','b1b1','a06b03','a6b2','a6b2c10','a6b2c5']
s.sort(key=alphaNumOrder)
s ===> ['a3', 'a6b2', 'a6b2c5', 'a6b2c10', 'a06b03', 'a10b1', 'a10b20', 'b1b1']

Ein Teil der Antwort kommt von dort


1

Basierend auf der Antwort von SilentGhost:

In [4]: a = set(['booklet', '4 sheets', '48 sheets', '12 sheets'])

In [5]: def f(x):
   ...:     num = x.split(None, 1)[0]
   ...:     if num.isdigit():
   ...:         return int(num)
   ...:     return x
   ...: 

In [6]: sorted(a, key=f)
Out[6]: ['4 sheets', '12 sheets', '48 sheets', 'booklet']

0

Sets sind von Natur aus ungeordnet. Sie müssen eine Liste mit demselben Inhalt erstellen und diese sortieren.


4
Nicht wahr - das integrierte sorted () nimmt eine beliebige Sequenz an und gibt eine sortierte Liste zurück.
PaulMcG

4
Anstatt eine Liste zu erstellen und zu sortieren, verwenden Sie stattdessen eine integrierte Liste, um eine sortierte Liste zu erstellen. Ja, ich war weit weg.
Rakis

Sets implementiert ein SortedSets (und nicht HashSets) sind von Natur aus geordnet
Axwell

0

Für Leute, die mit einer Python-Version vor 2.4 ohne die wunderbare sorted()Funktion stecken , ist eine schnelle Möglichkeit, Sets zu sortieren:

l = list(yourSet)
l.sort() 

Dies beantwortet nicht die oben genannte spezifische Frage ( 12 sheetswird vorher kommen 4 sheets), kann jedoch für Personen von Google nützlich sein.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.