Python 3.xを使用して、自然なアルファベット順のソートを実行したい文字列のリストがあります。
Natural sort: Windowsのファイルがソートされる順序。
たとえば、次のリストは自然にソートされています(私が望むもの):
['Elm0', 'Elm1', 'Elm2', 'Elm9', 'Elm10', 'Elm11', 'Elm12', 'Elm13']
そして、これが上記リストの「ソート済み」バージョンです(私が持っているもの):
['Elm11', 'Elm12', 'Elm2', 'Elm0', 'Elm1', 'Elm10', 'Elm13', 'Elm9']
私は最初のもののように振る舞うソート関数を探しています。
natsort (完全な開示、私はパッケージの作成者です)と呼ばれるPyPIのサードパーティライブラリがあります。あなたの場合、次のいずれかを実行できます。
>>> 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']
natsort
は一般的なアルゴリズムを使用しているので、あなたが投げたほぼすべての入力に対して機能することに注意してください。独自の関数を展開するのではなく、ライブラリを選択する理由について詳しく知りたい場合は、natsort
ドキュメントの How It Works ページ、特に どこでも特別なケース! セクション。
並べ替え関数の代わりに並べ替えキーが必要な場合は、次のいずれかの式を使用します。
>>> 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']
これを試して:
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)
出力:
['Elm0', 'Elm1', 'Elm2', 'Elm9', 'Elm10', 'Elm11', 'Elm12', 'Elm13']
ここから適応されたコード: Sorting for Humans:Natural Sort Order 。
Mark Byerの答えのはるかにPython的なバージョンを次に示します。
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)]
これで、この関数は、list.sort
、sorted
、max
などのように、それを使用する関数のキーとして使用できます。
ラムダとして:
lambda s: [int(t) if t.isdigit() else t.lower() for t in re.split('(\d+)', s)]
http://www.codinghorror.com/blog/2007/12/sorting-for-humans-natural-sort-order.html に基づく関数を作成しました。独自の「キー」パラメータ。これは、文字列だけでなく、より複雑なオブジェクトを含む自然なリストを実行するために必要です。
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)
例えば:
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']
データを分析しましょう。すべての要素の桁数は2です。また、共通リテラル部分'Elm'
には3文字があります。
そのため、要素の最大長は5です。この値を増やして確認することができます(たとえば、8に)。
それを念頭に置いて、1行の解決策があります:
data.sort(key=lambda x: '{0:0>8}'.format(x).lower())
正規表現と外部ライブラリなし!
print(data)
>>> ['Elm0', 'Elm1', 'Elm2', 'Elm9', 'Elm10', 'Elm11', 'Elm13']
説明:
for Elm in data:
print('{0:0>8}'.format(Elm).lower())
>>>
0000Elm0
0000Elm1
0000Elm2
0000Elm9
000Elm10
000Elm11
000Elm13
与えられた:
data=['Elm11', 'Elm12', 'Elm2', 'Elm0', 'Elm1', 'Elm10', 'Elm13', 'Elm9']
SergOのソリューションと同様に、外部ライブラリのない1ライナー:
data.sort(key=lambda x : int(x[3:]))
または
sorted_data=sorted(data, key=lambda x : int(x[3:]))
説明:
このソリューションは、sortのkey機能を使用して、ソートに使用されます。すべてのデータエントリの前に「Elm」が付いていることがわかっているため、ソート関数は3番目の文字の後の文字列の部分を整数に変換します(つまり、int(x [3:]))。データの数値部分が別の場所にある場合、関数のこの部分を変更する必要があります。
乾杯
1つのオプションは、文字列をタプルに変換し、拡張形式を使用して数字を置き換えることです http://wiki.answers.com/Q/What_does_expanded_form_mean
そのようにすると、a90は( "a"、90,0)になり、a1は( "a"、1)になります。
以下はいくつかのサンプルコードです(これは数字から先行0を削除する方法のためにあまり効率的ではありません)
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)
出力:
('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']
ここでの回答に基づいて、組み込み関数sorted
のように動作するnatural_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)
ソースコードは、私のGitHubスニペットリポジトリでも入手できます。 https://github.com/bdrung/snippets/blob/master/natural_sorted.py
私のポイントは、一般的に適用できる非正規表現ソリューションを提供することです。
3つの関数を作成します。
find_first_digit
@ AnuragUniyal から借りたもの。文字列の最初の数字または数字以外の位置を見つけます。split_digits
は、文字列を数字と数字以外のチャンクに分離するジェネレーターです。数字の場合、yield
整数にもなります。natural_key
はsplit_digits
をTuple
にラップするだけです。これは、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))
複数桁のチャンクを持つことができるという点で一般的であることがわかります。
# Note that the key has lower case letters
natural_key('asl;dkfDFKJ:sdlkfjdf809lkasdjfa_543_hh')
('asl;dkfdfkj:sdlkfjdf', 809, 'lkasdjfa_', 543, '_hh')
または、大文字と小文字を区別して残す:
natural_key('asl;dkfDFKJ:sdlkfjdf809lkasdjfa_543_hh', True)
('asl;dkfDFKJ:sdlkfjdf', 809, 'lkasdjfa_', 543, '_hh')
OPのリストが適切な順序でソートされていることがわかります。
sorted(
['Elm0', 'Elm1', 'Elm2', 'Elm9', 'Elm10', 'Elm11', 'Elm12', 'Elm13'],
key=natural_key
)
['Elm0', 'Elm1', 'Elm2', 'Elm9', 'Elm10', 'Elm11', 'Elm12', 'Elm13']
ただし、より複雑なリストも処理できます。
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']
私の正規表現は
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))
最も可能性の高いfunctools.cmp_to_key()
は、Pythonのソートの基礎となる実装と密接に関係しています。また、cmpパラメーターはレガシーです。最新の方法は、入力項目を、目的のリッチ比較操作をサポートするオブジェクトに変換することです。
CPython 2.xでは、それぞれのリッチ比較演算子が実装されていない場合でも、異なるタイプのオブジェクトを注文できます。 CPython 3.xでは、異なるタイプのオブジェクトは比較を明示的にサポートする必要があります。 Pythonは文字列とintをどのように比較しますか公式ドキュメント へのリンクを参照してください。ほとんどの答えは、この暗黙的な順序に依存しています。 Python 3.xに切り替えるには、数値と文字列の比較を実装および統合するために新しい型が必要になります。
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()
3つの異なるアプローチがあります。 1つ目は、ネストされたクラスを使用して、PythonのIterable
比較アルゴリズムを利用します。 2番目は、このネストを単一のクラスに展開します。 3番目は、str
をサブクラス化せずにパフォーマンスに焦点を合わせます。すべてのタイミングです。 2番目は2倍、3番目はほぼ6倍高速です。 str
のサブクラス化は必須ではなく、そもそもおそらく悪い考えでしたが、ある程度の便利さがあります。
ソート文字は複製され、ケースごとの順序付けを強制し、小文字を最初にソートするようにケース交換されます。これは「自然なソート」の典型的な定義です。グループ化のタイプを決定できませんでした。一部の人は次のことを好むかもしれませんが、これはまたパフォーマンスの大きな利点をもたらします:
d = lambda s: s.lower()+s.swapcase()
利用される場合、比較演算子はobject
の比較演算子に設定されるため、 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
自然なソートはかなり複雑であり、漠然と問題として定義されています。事前にunicodedata.normalize(...)
を実行することを忘れずに、str.casefold()
ではなくstr.lower()
の使用を検討してください。おそらく、私が検討していない微妙なエンコードの問題があります。そのため、私は暫定的に natsort ライブラリをお勧めします。私はgithubリポジトリを一目見ました。コードのメンテナンスは素晴らしいものでした。
私が見たすべてのアルゴリズムは、文字の複製と下げ、大文字と小文字の交換などのトリックに依存しています。これにより実行時間が2倍になりますが、別の方法では、入力文字セットの完全な自然順序付けが必要になります。これはUnicode仕様の一部ではないと思います。また、[0-9]
よりも多くのUnicode桁があるため、このような並べ替えを作成することも同様に困難です。ロケールを意識した比較が必要な場合は、Pythonの _ Sorting HOW TO に従って locale.strxfrm
で文字列を準備します。
上記の回答は、示されたの特定の例には適していますが、自然なソートのより一般的な質問にはいくつかの有用なケースがありません。私はそれらのケースの1つに少し触れただけなので、より徹底的なソリューションを作成しました。
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
テストコードといくつかのリンク(StackOverflowのオンとオフ)は次のとおりです。 http://productarchitect.com/code/better-natural-sort.py
フィードバックを歓迎します。それは決定的な解決策ではありません。一歩前進。
@Mark Byersの回答に続いて、ここではkey
パラメーターを受け入れ、さらに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)
Gist も作成しました
key
のsorted
キーワード引数を使用して、目的のリストを作成することをお勧めします
例えば:
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
謝辞: