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

mmrs151
quelle

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)
Daniel Stutzbach
quelle
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).

Mark Byers
quelle
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
SethMMorton
quelle
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)
Ameisen Aasma
quelle
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)]
John La Rooy
quelle
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']
SilentGhost
quelle
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

Le Droid
quelle
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']
draebek
quelle
0

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

Rakis
quelle
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.

Giacomo Lacava
quelle