Avec Python 3.x, j’ai une liste de chaînes pour lesquelles je voudrais effectuer un tri alphabétique naturel.
Natural sort: L'ordre dans lequel les fichiers de Windows sont triés.
Par exemple, la liste suivante est naturellement triée (ce que je veux):
['Elm0', 'Elm1', 'Elm2', 'Elm9', 'Elm10', 'Elm11', 'Elm12', 'Elm13']
Et voici la version "triée" de la liste ci-dessus (ce que j'ai):
['Elm11', 'Elm12', 'Elm2', 'Elm0', 'Elm1', 'Elm10', 'Elm13', 'Elm9']
Je cherche une fonction de tri qui se comporte comme la première.
Il existe une bibliothèque tierce pour cela sur PyPI appelée natsort (divulgation complète, je suis l’auteur du paquet). Dans votre cas, vous pouvez effectuer l’une des opérations suivantes:
>>> from natsort import natsorted, ns
>>> x = ['Elm11', 'Elm12', 'Elm2', 'Elm0', 'Elm1', 'Elm10', 'Elm13', 'Elm9']
>>> natsorted(x, key=lambda y: y.lower())
['Elm0', 'Elm1', 'Elm2', 'Elm9', 'Elm10', 'Elm11', 'Elm12', 'Elm13']
>>> natsorted(x, alg=ns.IGNORECASE) # or alg=ns.IC
['Elm0', 'Elm1', 'Elm2', 'Elm9', 'Elm10', 'Elm11', 'Elm12', 'Elm13']
Vous devriez noter que natsort
utilise un algorithme général, il devrait donc fonctionner pour pratiquement toutes les entrées que vous lui envoyez. Si vous voulez plus de détails sur les raisons pour lesquelles vous pourriez choisir une bibliothèque plutôt que de lancer votre propre fonction, consultez la page de documentation de natsort
Comment ça marche , en particulier le Cas spéciaux partout! section.
Si vous avez besoin d'une clé de tri au lieu d'une fonction de tri, utilisez l'une des formules ci-dessous.
>>> from natsort import natsort_keygen, ns
>>> l1 = ['Elm0', 'Elm1', 'Elm2', 'Elm9', 'Elm10', 'Elm11', 'Elm12', 'Elm13']
>>> l2 = l1[:]
>>> natsort_key1 = natsort_keygen(key=lambda y: y.lower())
>>> l1.sort(key=natsort_key1)
>>> l1
['Elm0', 'Elm1', 'Elm2', 'Elm9', 'Elm10', 'Elm11', 'Elm12', 'Elm13']
>>> natsort_key2 = natsort_keygen(alg=ns.IGNORECASE)
>>> l2.sort(key=natsort_key2)
>>> l2
['Elm0', 'Elm1', 'Elm2', 'Elm9', 'Elm10', 'Elm11', 'Elm12', 'Elm13']
Essaye ça:
import re
def natural_sort(l):
convert = lambda text: int(text) if text.isdigit() else text.lower()
alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ]
return sorted(l, key = alphanum_key)
Sortie:
['Elm0', 'Elm1', 'Elm2', 'Elm9', 'Elm10', 'Elm11', 'Elm12', 'Elm13']
Code adapté à partir d'ici: Tri pour les humains: ordre de tri naturel .
Voici une version beaucoup plus pythonique de la réponse de Mark Byer:
import re
def natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
return [int(text) if text.isdigit() else text.lower()
for text in _nsre.split(s)]
Maintenant, cette fonction peut être utilisée comme touche dans n'importe quelle fonction qui l'utilise, comme list.sort
, sorted
, max
, etc.
En tant que lambda:
lambda s: [int(t) if t.isdigit() else t.lower() for t in re.split('(\d+)', s)]
J'ai écrit une fonction basée sur http://www.codinghorror.com/blog/2007/12/sorting-for-humans-natural-sort-order.html qui ajoute la possibilité de passer encore votre propre paramètre 'clé'. J'ai besoin de cela pour effectuer une sorte de liste naturelle qui contient des objets plus complexes (pas seulement des chaînes).
import re
def natural_sort(list, key=lambda s:s):
"""
Sort the list into natural alphanumeric order.
"""
def get_alphanum_key_func(key):
convert = lambda text: int(text) if text.isdigit() else text
return lambda s: [convert(c) for c in re.split('([0-9]+)', key(s))]
sort_key = get_alphanum_key_func(key)
list.sort(key=sort_key)
Par exemple:
my_list = [{'name':'b'}, {'name':'10'}, {'name':'a'}, {'name':'1'}, {'name':'9'}]
natural_sort(my_list, key=lambda x: x['name'])
print my_list
[{'name': '1'}, {'name': '9'}, {'name': '10'}, {'name': 'a'}, {'name': 'b'}]
data = ['Elm13', 'Elm9', 'Elm0', 'Elm1', 'Elm11', 'Elm2', 'Elm10']
Analysons les données. La capacité numérique de tous les éléments est 2. Et il y a 3 lettres dans la partie littérale commune 'Elm'
.
Donc, la longueur maximale de l'élément est 5. Nous pouvons augmenter cette valeur pour nous en assurer (par exemple, à 8).
Gardant cela à l'esprit, nous avons une solution sur une ligne:
data.sort(key=lambda x: '{0:0>8}'.format(x).lower())
sans expressions régulières et bibliothèques externes!
print(data)
>>> ['Elm0', 'Elm1', 'Elm2', 'Elm9', 'Elm10', 'Elm11', 'Elm13']
Explication:
for Elm in data:
print('{0:0>8}'.format(Elm).lower())
>>>
0000Elm0
0000Elm1
0000Elm2
0000Elm9
000Elm10
000Elm11
000Elm13
Donné:
data=['Elm11', 'Elm12', 'Elm2', 'Elm0', 'Elm1', 'Elm10', 'Elm13', 'Elm9']
Semblable à la solution de SergO, un 1-liner sans bibliothèques externes serait:
data.sort(key=lambda x : int(x[3:]))
ou
sorted_data=sorted(data, key=lambda x : int(x[3:]))
Explication:
Cette solution utilise la clé de sort pour définir une fonction qui être employé pour le tri. Comme nous savons que chaque entrée de données est précédée de "Elm", la fonction de tri convertit en entier la partie de la chaîne située après le 3ème caractère (c'est-à-dire int (x [3:])). Si la partie numérique des données se trouve à un emplacement différent, cette partie de la fonction devrait être modifiée.
À votre santé
Une option consiste à transformer la chaîne en un tuple et à remplacer les chiffres à l'aide d'une forme développée http://wiki.answers.com/Q/What_does_expanded_form_mean
de cette façon, a90 deviendrait ("a", 90,0) et a1 deviendrait ("a", 1)
ci-dessous quelques exemples de code (ce qui n'est pas très efficace en raison de la façon dont il supprime les 0 premiers numéros des nombres)
alist=["something1",
"something12",
"something17",
"something2",
"something25and_then_33",
"something25and_then_34",
"something29",
"beta1.1",
"beta2.3.0",
"beta2.33.1",
"a001",
"a2",
"z002",
"z1"]
def key(k):
nums=set(list("0123456789"))
chars=set(list(k))
chars=chars-nums
for i in range(len(k)):
for c in chars:
k=k.replace(c+"0",c)
l=list(k)
base=10
j=0
for i in range(len(l)-1,-1,-1):
try:
l[i]=int(l[i])*base**j
j+=1
except:
j=0
l=Tuple(l)
print l
return l
print sorted(alist,key=key)
sortie:
('s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', 1)
('s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', 10, 2)
('s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', 10, 7)
('s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', 2)
('s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', 20, 5, 'a', 'n', 'd', '_', 't', 'h', 'e', 'n', '_', 30, 3)
('s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', 20, 5, 'a', 'n', 'd', '_', 't', 'h', 'e', 'n', '_', 30, 4)
('s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', 20, 9)
('b', 'e', 't', 'a', 1, '.', 1)
('b', 'e', 't', 'a', 2, '.', 3, '.')
('b', 'e', 't', 'a', 2, '.', 30, 3, '.', 1)
('a', 1)
('a', 2)
('z', 2)
('z', 1)
['a001', 'a2', 'beta1.1', 'beta2.3.0', 'beta2.33.1', 'something1', 'something2', 'something12', 'something17', 'something25and_then_33', 'something25and_then_34', 'something29', 'z1', 'z002']
Sur la base des réponses ici, j'ai écrit un natural_sorted
fonction qui se comporte comme la fonction intégrée sorted
:
# Copyright (C) 2018, Benjamin Drung <[email protected]>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import re
def natural_sorted(iterable, key=None, reverse=False):
"""Return a new naturally sorted list from the items in *iterable*.
The returned list is in natural sort order. The string is ordered
lexicographically (using the Unicode code point number to order individual
characters), except that multi-digit numbers are ordered as a single
character.
Has two optional arguments which must be specified as keyword arguments.
*key* specifies a function of one argument that is used to extract a
comparison key from each list element: ``key=str.lower``. The default value
is ``None`` (compare the elements directly).
*reverse* is a boolean value. If set to ``True``, then the list elements are
sorted as if each comparison were reversed.
The :func:`natural_sorted` function is guaranteed to be stable. A sort is
stable if it guarantees not to change the relative order of elements that
compare equal --- this is helpful for sorting in multiple passes (for
example, sort by department, then by salary grade).
"""
prog = re.compile(r"(\d+)")
def alphanum_key(element):
"""Split given key in list of strings and digits"""
return [int(c) if c.isdigit() else c for c in prog.split(key(element)
if key else element)]
return sorted(iterable, key=alphanum_key, reverse=reverse)
Le code source est également disponible dans mon référentiel d'extraits de code GitHub: https://github.com/bdrung/snippets/blob/master/natural_sorted.py
Mon but est d’offrir une solution non regex qui puisse s’appliquer de manière générale.
Je créerai trois fonctions:
find_first_digit
que j'ai emprunté à @ AnuragUniyal . Il trouvera la position du premier chiffre ou non dans une chaîne.split_digits
_ qui est un générateur qui sépare une chaîne en morceaux numériques et non numériques. Il s'agira également de yield
nombres entiers s’il s’agit d’un chiffre.natural_key
_ enveloppe simplement split_digits
dans un Tuple
. C'est ce que nous utilisons comme clé pour sorted
, max
, min
.def find_first_digit(s, non=False):
for i, x in enumerate(s):
if x.isdigit() ^ non:
return i
return -1
def split_digits(s, case=False):
non = True
while s:
i = find_first_digit(s, non)
if i == 0:
non = not non
Elif i == -1:
yield int(s) if s.isdigit() else s if case else s.lower()
s = ''
else:
x, s = s[:i], s[i:]
yield int(x) if x.isdigit() else x if case else x.lower()
def natural_key(s, *args, **kwargs):
return Tuple(split_digits(s, *args, **kwargs))
Nous pouvons voir que c'est général en ce que nous pouvons avoir plusieurs morceaux de chiffres:
# Note that the key has lower case letters
natural_key('asl;dkfDFKJ:sdlkfjdf809lkasdjfa_543_hh')
('asl;dkfdfkj:sdlkfjdf', 809, 'lkasdjfa_', 543, '_hh')
Ou laissez comme sensible à la casse:
natural_key('asl;dkfDFKJ:sdlkfjdf809lkasdjfa_543_hh', True)
('asl;dkfDFKJ:sdlkfjdf', 809, 'lkasdjfa_', 543, '_hh')
Nous pouvons voir qu'il trie la liste du PO dans l'ordre approprié
sorted(
['Elm0', 'Elm1', 'Elm2', 'Elm9', 'Elm10', 'Elm11', 'Elm12', 'Elm13'],
key=natural_key
)
['Elm0', 'Elm1', 'Elm2', 'Elm9', 'Elm10', 'Elm11', 'Elm12', 'Elm13']
Mais il peut aussi gérer des listes plus compliquées:
sorted(
['f_1', 'e_1', 'a_2', 'g_0', 'd_0_12:2', 'd_0_1_:2'],
key=natural_key
)
['a_2', 'd_0_1_:2', 'd_0_12:2', 'e_1', 'f_1', 'g_0']
Mon équivalent regex serait
def int_maybe(x):
return int(x) if str(x).isdigit() else x
def split_digits_re(s, case=False):
parts = re.findall('\d+|\D+', s)
if not case:
return map(int_maybe, (x.lower() for x in parts))
else:
return map(int_maybe, parts)
def natural_key_re(s, *args, **kwargs):
return Tuple(split_digits_re(s, *args, **kwargs))
Très probablement, functools.cmp_to_key()
est étroitement lié à l'implémentation sous-jacente du type de python. En outre, le paramètre cmp est hérité. La méthode moderne consiste à transformer les éléments d’entrée en objets prenant en charge les opérations de comparaison enrichies souhaitées.
Sous CPython 2.x, des objets de types différents peuvent être commandés même si les opérateurs de comparaison riches respectifs n'ont pas été implémentés. Sous CPython 3.x, les objets de types différents doivent explicitement prendre en charge la comparaison. Voir Comment Python compare chaîne et int? qui relie à la documentation officielle . La plupart Le passage à Python 3.x nécessitera un nouveau type pour implémenter et unifier les comparaisons entre les nombres et les chaînes.
Python 2.7.12 (default, Sep 29 2016, 13:30:34)
>>> (0,"foo") < ("foo",0)
True
Python 3.5.2 (default, Oct 14 2016, 12:54:53)
>>> (0,"foo") < ("foo",0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: int() < str()
Il y a trois approches différentes. La première utilise des classes imbriquées pour tirer parti de l'algorithme de comparaison Iterable
de Python. La seconde déroule cette imbrication en une seule classe. Le troisième évoque le sous-classement str
pour se concentrer sur les performances. Tous sont chronométrés; la seconde est deux fois plus rapide et la troisième presque six fois plus vite. Le sous-classement str
n'est pas obligatoire, et était probablement une mauvaise idée en premier lieu, mais il présente certaines commodités.
Les caractères de tri sont dupliqués pour forcer le classement par cas et échangés pour forcer les lettres minuscules à trier en premier; Telle est la définition typique de "nature naturelle". Je ne pouvais pas décider du type de groupement; Certains pourraient préférer les solutions suivantes, qui apportent également des avantages significatifs en termes de performances:
d = lambda s: s.lower()+s.swapcase()
Lorsqu'ils sont utilisés, les opérateurs de comparaison sont définis sur celui de object
afin qu'ils ne soient pas sera ignoré par functools.total_ordering
.
import functools
import itertools
@functools.total_ordering
class NaturalStringA(str):
def __repr__(self):
return "{}({})".format\
( type(self).__name__
, super().__repr__()
)
d = lambda c, s: [ c.NaturalStringPart("".join(v))
for k,v in
itertools.groupby(s, c.isdigit)
]
d = classmethod(d)
@functools.total_ordering
class NaturalStringPart(str):
d = lambda s: "".join(c.lower()+c.swapcase() for c in s)
d = staticmethod(d)
def __lt__(self, other):
if not isinstance(self, type(other)):
return NotImplemented
try:
return int(self) < int(other)
except ValueError:
if self.isdigit():
return True
Elif other.isdigit():
return False
else:
return self.d(self) < self.d(other)
def __eq__(self, other):
if not isinstance(self, type(other)):
return NotImplemented
try:
return int(self) == int(other)
except ValueError:
if self.isdigit() or other.isdigit():
return False
else:
return self.d(self) == self.d(other)
__le__ = object.__le__
__ne__ = object.__ne__
__gt__ = object.__gt__
__ge__ = object.__ge__
def __lt__(self, other):
return self.d(self) < self.d(other)
def __eq__(self, other):
return self.d(self) == self.d(other)
__le__ = object.__le__
__ne__ = object.__ne__
__gt__ = object.__gt__
__ge__ = object.__ge__
import functools
import itertools
@functools.total_ordering
class NaturalStringB(str):
def __repr__(self):
return "{}({})".format\
( type(self).__name__
, super().__repr__()
)
d = lambda s: "".join(c.lower()+c.swapcase() for c in s)
d = staticmethod(d)
def __lt__(self, other):
if not isinstance(self, type(other)):
return NotImplemented
groups = map(lambda i: itertools.groupby(i, type(self).isdigit), (self, other))
zipped = itertools.Zip_longest(*groups)
for s,o in zipped:
if s is None:
return True
if o is None:
return False
s_k, s_v = s[0], "".join(s[1])
o_k, o_v = o[0], "".join(o[1])
if s_k and o_k:
s_v, o_v = int(s_v), int(o_v)
if s_v == o_v:
continue
return s_v < o_v
Elif s_k:
return True
Elif o_k:
return False
else:
s_v, o_v = self.d(s_v), self.d(o_v)
if s_v == o_v:
continue
return s_v < o_v
return False
def __eq__(self, other):
if not isinstance(self, type(other)):
return NotImplemented
groups = map(lambda i: itertools.groupby(i, type(self).isdigit), (self, other))
zipped = itertools.Zip_longest(*groups)
for s,o in zipped:
if s is None or o is None:
return False
s_k, s_v = s[0], "".join(s[1])
o_k, o_v = o[0], "".join(o[1])
if s_k and o_k:
s_v, o_v = int(s_v), int(o_v)
if s_v == o_v:
continue
return False
Elif s_k or o_k:
return False
else:
s_v, o_v = self.d(s_v), self.d(o_v)
if s_v == o_v:
continue
return False
return True
__le__ = object.__le__
__ne__ = object.__ne__
__gt__ = object.__gt__
__ge__ = object.__ge__
import functools
import itertools
import enum
class OrderingType(enum.Enum):
PerWordSwapCase = lambda s: s.lower()+s.swapcase()
PerCharacterSwapCase = lambda s: "".join(c.lower()+c.swapcase() for c in s)
class NaturalOrdering:
@classmethod
def by(cls, ordering):
def wrapper(string):
return cls(string, ordering)
return wrapper
def __init__(self, string, ordering=OrderingType.PerCharacterSwapCase):
self.string = string
self.groups = [ (k,int("".join(v)))
if k else
(k,ordering("".join(v)))
for k,v in
itertools.groupby(string, str.isdigit)
]
def __repr__(self):
return "{}({})".format\
( type(self).__name__
, self.string
)
def __lesser(self, other, default):
if not isinstance(self, type(other)):
return NotImplemented
for s,o in itertools.Zip_longest(self.groups, other.groups):
if s is None:
return True
if o is None:
return False
s_k, s_v = s
o_k, o_v = o
if s_k and o_k:
if s_v == o_v:
continue
return s_v < o_v
Elif s_k:
return True
Elif o_k:
return False
else:
if s_v == o_v:
continue
return s_v < o_v
return default
def __lt__(self, other):
return self.__lesser(other, default=False)
def __le__(self, other):
return self.__lesser(other, default=True)
def __eq__(self, other):
if not isinstance(self, type(other)):
return NotImplemented
for s,o in itertools.Zip_longest(self.groups, other.groups):
if s is None or o is None:
return False
s_k, s_v = s
o_k, o_v = o
if s_k and o_k:
if s_v == o_v:
continue
return False
Elif s_k or o_k:
return False
else:
if s_v == o_v:
continue
return False
return True
# functools.total_ordering doesn't create single-call wrappers if both
# __le__ and __lt__ exist, so do it manually.
def __gt__(self, other):
op_result = self.__le__(other)
if op_result is NotImplemented:
return op_result
return not op_result
def __ge__(self, other):
op_result = self.__lt__(other)
if op_result is NotImplemented:
return op_result
return not op_result
# __ne__ is the only implied ordering relationship, it automatically
# delegates to __eq__
>>> import natsort
>>> import timeit
>>> l1 = ['Apple', 'corn', 'Apple', 'arbour', 'Corn', 'Banana', 'Apple', 'banana']
>>> l2 = list(map(str, range(30)))
>>> l3 = ["{} {}".format(x,y) for x in l1 for y in l2]
>>> print(timeit.timeit('sorted(l3+["0"], key=NaturalStringA)', number=10000, globals=globals()))
362.4729259099986
>>> print(timeit.timeit('sorted(l3+["0"], key=NaturalStringB)', number=10000, globals=globals()))
189.7340817489967
>>> print(timeit.timeit('sorted(l3+["0"], key=NaturalOrdering.by(OrderingType.PerCharacterSwapCase))', number=10000, globals=globals()))
69.34636392899847
>>> print(timeit.timeit('natsort.natsorted(l3+["0"], alg=natsort.ns.GROUPLETTERS | natsort.ns.LOWERCASEFIRST)', number=10000, globals=globals()))
98.2531585780016
Le tri naturel est à la fois assez compliqué et défini vaguement comme un problème. N'oubliez pas de lancer unicodedata.normalize(...)
à l'avance et envisagez d'utiliser str.casefold()
plutôt que str.lower()
. Il y a probablement des problèmes de codage subtils que je n'ai pas pris en compte. Je recommande donc provisoirement la bibliothèque natsort . J'ai jeté un coup d'œil au dépôt github; la maintenance du code a été stellaire.
Tous les algorithmes que j'ai vus dépendent d'astuces telles que la duplication et l'abaissement de caractères, ainsi que l'échange de cas. Bien que cela double la durée d'exécution, une alternative nécessiterait un ordre naturel total sur le jeu de caractères saisi. Je ne pense pas que cela fasse partie de la spécification unicode, et comme il y a beaucoup plus de chiffres unicode que [0-9]
, La création d'un tel tri serait également décourageante. Si vous souhaitez effectuer des comparaisons tenant compte des paramètres régionaux, préparez vos chaînes avec locale.strxfrm
pour Python Sorting HOW TO .
Les réponses ci-dessus sont bonnes pour l'exemple spécifique qui a été montré, mais il manque plusieurs cas utiles pour la question plus générale du genre naturel. Je viens juste de connaître un cas, alors j'ai créé une solution plus complète:
def natural_sort_key(string_or_number):
"""
by Scott S. Lawton <[email protected]> 2014-12-11; public domain and/or CC0 license
handles cases where simple 'int' approach fails, e.g.
['0.501', '0.55'] floating point with different number of significant digits
[0.01, 0.1, 1] already numeric so regex and other string functions won't work (and aren't required)
['Elm1', 'Elm2'] ASCII vs. letters (not case sensitive)
"""
def try_float(astring):
try:
return float(astring)
except:
return astring
if isinstance(string_or_number, basestring):
string_or_number = string_or_number.lower()
if len(re.findall('[.]\d', string_or_number)) <= 1:
# assume a floating point value, e.g. to correctly sort ['0.501', '0.55']
# '.' for decimal is locale-specific, e.g. correct for the Anglosphere and Asia but not continental Europe
return [try_float(s) for s in re.split(r'([\d.]+)', string_or_number)]
else:
# assume distinct fields, e.g. IP address, phone number with '.', etc.
# caveat: might want to first split by whitespace
# TBD: for unicode, replace isdigit with isdecimal
return [int(s) if s.isdigit() else s for s in re.split(r'(\d+)', string_or_number)]
else:
# consider: add code to recurse for lists/tuples and perhaps other iterables
return string_or_number
Le code de test et plusieurs liens (activés et désactivés de StackOverflow) se trouvent ici: http://productarchitect.com/code/better-natural-sort.py
Commentaires bienvenus. Ce n'est pas censé être une solution définitive; juste un pas en avant.
Après la réponse de @Mark Byers, voici une adaptation qui accepte le paramètre key
et qui est davantage compatible avec PEP8.
def natsorted(seq, key=None):
def convert(text):
return int(text) if text.isdigit() else text
def alphanum(obj):
if key is not None:
return [convert(c) for c in re.split(r'([0-9]+)', key(obj))]
return [convert(c) for c in re.split(r'([0-9]+)', obj)]
return sorted(seq, key=alphanum)
J'ai aussi fait un Gist
Je vous suggère simplement d'utiliser l'argument de mot-clé key
de sorted
pour obtenir la liste souhaitée.
Par exemple:
to_order= [e2,E1,e5,E4,e3]
ordered= sorted(to_order, key= lambda x: x.lower())
# ordered should be [E1,e2,e3,E4,e5]
a = ['H1', 'H100', 'H10', 'H3', 'H2', 'H6', 'H11', 'H50', 'H5', 'H99', 'H8']
b = ''
c = []
def bubble(bad_list):#bubble sort method
length = len(bad_list) - 1
sorted = False
while not sorted:
sorted = True
for i in range(length):
if bad_list[i] > bad_list[i+1]:
sorted = False
bad_list[i], bad_list[i+1] = bad_list[i+1], bad_list[i] #sort the integer list
a[i], a[i+1] = a[i+1], a[i] #sort the main list based on the integer list index value
for a_string in a: #extract the number in the string character by character
for letter in a_string:
if letter.isdigit():
#print letter
b += letter
c.append(b)
b = ''
print 'Before sorting....'
print a
c = map(int, c) #converting string list into number list
print c
bubble(c)
print 'After sorting....'
print c
print a
Remerciements :