使用另一个列表对 Python 列表中的字符串进行排序
Sort strings in Python list using another list
假设我有以下列表:
List1=['Name1','Name3','Color1','Size2','Color3','Color2','Name2','Size1', 'ID']
List2=['ID','Color1','Color2','Size1','Size2','Name1','Name2']
每个列表都有名为 "ID" 变量的元素,然后是其他 3 个类别(名称、颜色和大小),其中每个类别中的元素数量不确定。
我想在不知道每个类别中有多少变量的情况下对这些变量进行排序 'sort list':
SortList=['ID','Name','Size','Color']
我可以获得所需的输出(见下文),尽管我想有更好/更 pythonic 的方法。
>>> def SortMyList(MyList,SortList):
... SortedList=[]
... for SortItem in SortList:
... SortItemList=[]
... for Item in MyList:
... ItemWithoutNum="".join([char for char in Item if char.isalpha()])
... if SortItem==ItemWithoutNum:
... SortItemList.append(Item)
... if len(SortItemList)>1:
... SortItemList=[SortItem+str(I) for I in range(1,len(SortItemList)+1)]
... for SortedItem in SortItemList:
... SortedList.append(SortedItem)
... return SortedList
...
>>>
>>> SortMyList(List1, SortList)
['ID', 'Name1', 'Name2', 'Name3', 'Size1', 'Size2', 'Color1', 'Color2', 'Color3']
>>> SortMyList(List2, SortList)
['ID', 'Name1', 'Name2', 'Size1', 'Size2', 'Color1', 'Color2']
>>>
关于如何改进我的方法或我的代码有什么建议吗?
您可以使用自定义键函数对列表进行排序,returns 一个 2 元组,用于主要排序和次要排序。
主要排序是按照您 "tags" 的顺序(首先是 ID,然后是姓名,依此类推)。
二次排序是按照它后面的数值进行的。
tags = ['ID','Name','Size','Color']
sort_order = { tag : i for i,tag in enumerate(tags) }
def elem_key(x):
for tag in tags:
if x.startswith(tag):
suffix = x[len(tag) : ]
return ( sort_order[tag],
int(suffix) if suffix else None )
raise ValueError("element %s is not prefixed by a known tag. order is not defined" % x)
list1.sort(key = elem_key)
您只需提供足够的密钥即可:
List1.sort( key = lambda x : ('INSC'.index(x[0]),x[-1]))
# ['ID', 'Name1', 'Name2', 'Name3', 'Size1', 'Size2', 'Color1', 'Color2', 'Color3']
元素将按第一个字母排序,然后是最后一个数字(如果存在)。它在这里有效,因为所有首字母都不同,如果数字最多只有一个数字。
编辑
对于很多数字,更混淆的解决方案:
List1.sort( key =lambda x : ('INSC'.index(x[0]),int("0"+"".join(re.findall('\d+',x)))))
# ['ID', 'Name1', 'Name2', 'Name10', 'Size1', 'Size2', 'Color1', 'Color2', 'Color3']
是否有(在这种情况下)比简单的正则表达式更容易从字符串中提取数据的方法?
import re
def keygen(sort_list):
return lambda elem: (
sort_list.index(re.findall(r'^[a-zA-Z]+', elem)[0]),
re.findall(r'\d+$', elem)
)
用法:
SortList = ['ID', 'Name', 'Size', 'Color']
List1 = ['Name1', 'Name3', 'Color1', 'Size2', 'Color3', 'Color2','Name2', 'Size1', 'ID']
List2 = ['ID', 'Color1', 'Color2', 'Size1', 'Size2', 'Name1', 'Name2']
sorted(List1, key=keygen(SortList))
=> ['ID', 'Name1', 'Name2', 'Name3', 'Size1', 'Size2', 'Color1', 'Color2', 'Color3']
sorted(List2, key=keygen(SortList))
=> ['ID', 'Name1', 'Name2', 'Size1', 'Size2', 'Color1', 'Color2']
解释:
^[a-zA-Z]+
匹配开头的字母部分,\d$
– 字符串末尾的数字部分。
keygen
returns lambda
接受一个字符串,并且 returns two-item 元组:
第一项是列表中字母部分的位置(列表中没有这样的项目 = ValueError
),
第二个是 one-item 列表,最后包含数字部分,如果字符串不以数字结尾,则为空列表。
一些可能的改进:
sort_list.index
调用是O(n)
,对list中的每个元素都会调用;可以替换为 O(1)
字典查找以加快排序(为了简单起见,我没有这样做),
- 数字部分可以转换成实际的整数
(
1 < 2 < 10
, 但 '1' < '10' < '2'
)
应用这些之后:
import re
def keygen(sort_list):
index = {(word, index) for index, word in enumerate(sort_slist)}
return lambda elem: (
index[re.findall(r'^[a-zA-Z]+', elem)[0]],
[int(s) for s in re.findall(r'\d+$', elem)]
)
只要您知道 List2 只包含以 sortList
中的事物开头的字符串,这就有效
List2=['ID','Color4','Color2','Size1','Size2','Name2','Name1']
sortList=['ID','Name','Size','Color']
def sort_fun(x):
for i, thing in enumerate(sortList):
if x.startswith(thing):
return (i, x[len(thing):])
print sorted(List2, key=sort_fun)
假设我有以下列表:
List1=['Name1','Name3','Color1','Size2','Color3','Color2','Name2','Size1', 'ID']
List2=['ID','Color1','Color2','Size1','Size2','Name1','Name2']
每个列表都有名为 "ID" 变量的元素,然后是其他 3 个类别(名称、颜色和大小),其中每个类别中的元素数量不确定。
我想在不知道每个类别中有多少变量的情况下对这些变量进行排序 'sort list':
SortList=['ID','Name','Size','Color']
我可以获得所需的输出(见下文),尽管我想有更好/更 pythonic 的方法。
>>> def SortMyList(MyList,SortList):
... SortedList=[]
... for SortItem in SortList:
... SortItemList=[]
... for Item in MyList:
... ItemWithoutNum="".join([char for char in Item if char.isalpha()])
... if SortItem==ItemWithoutNum:
... SortItemList.append(Item)
... if len(SortItemList)>1:
... SortItemList=[SortItem+str(I) for I in range(1,len(SortItemList)+1)]
... for SortedItem in SortItemList:
... SortedList.append(SortedItem)
... return SortedList
...
>>>
>>> SortMyList(List1, SortList)
['ID', 'Name1', 'Name2', 'Name3', 'Size1', 'Size2', 'Color1', 'Color2', 'Color3']
>>> SortMyList(List2, SortList)
['ID', 'Name1', 'Name2', 'Size1', 'Size2', 'Color1', 'Color2']
>>>
关于如何改进我的方法或我的代码有什么建议吗?
您可以使用自定义键函数对列表进行排序,returns 一个 2 元组,用于主要排序和次要排序。
主要排序是按照您 "tags" 的顺序(首先是 ID,然后是姓名,依此类推)。 二次排序是按照它后面的数值进行的。
tags = ['ID','Name','Size','Color']
sort_order = { tag : i for i,tag in enumerate(tags) }
def elem_key(x):
for tag in tags:
if x.startswith(tag):
suffix = x[len(tag) : ]
return ( sort_order[tag],
int(suffix) if suffix else None )
raise ValueError("element %s is not prefixed by a known tag. order is not defined" % x)
list1.sort(key = elem_key)
您只需提供足够的密钥即可:
List1.sort( key = lambda x : ('INSC'.index(x[0]),x[-1]))
# ['ID', 'Name1', 'Name2', 'Name3', 'Size1', 'Size2', 'Color1', 'Color2', 'Color3']
元素将按第一个字母排序,然后是最后一个数字(如果存在)。它在这里有效,因为所有首字母都不同,如果数字最多只有一个数字。
编辑
对于很多数字,更混淆的解决方案:
List1.sort( key =lambda x : ('INSC'.index(x[0]),int("0"+"".join(re.findall('\d+',x)))))
# ['ID', 'Name1', 'Name2', 'Name10', 'Size1', 'Size2', 'Color1', 'Color2', 'Color3']
是否有(在这种情况下)比简单的正则表达式更容易从字符串中提取数据的方法?
import re
def keygen(sort_list):
return lambda elem: (
sort_list.index(re.findall(r'^[a-zA-Z]+', elem)[0]),
re.findall(r'\d+$', elem)
)
用法:
SortList = ['ID', 'Name', 'Size', 'Color']
List1 = ['Name1', 'Name3', 'Color1', 'Size2', 'Color3', 'Color2','Name2', 'Size1', 'ID']
List2 = ['ID', 'Color1', 'Color2', 'Size1', 'Size2', 'Name1', 'Name2']
sorted(List1, key=keygen(SortList))
=> ['ID', 'Name1', 'Name2', 'Name3', 'Size1', 'Size2', 'Color1', 'Color2', 'Color3']
sorted(List2, key=keygen(SortList))
=> ['ID', 'Name1', 'Name2', 'Size1', 'Size2', 'Color1', 'Color2']
解释:
^[a-zA-Z]+
匹配开头的字母部分,\d$
– 字符串末尾的数字部分。
keygen
returns lambda
接受一个字符串,并且 returns two-item 元组:
第一项是列表中字母部分的位置(列表中没有这样的项目 = ValueError
),
第二个是 one-item 列表,最后包含数字部分,如果字符串不以数字结尾,则为空列表。
一些可能的改进:
sort_list.index
调用是O(n)
,对list中的每个元素都会调用;可以替换为O(1)
字典查找以加快排序(为了简单起见,我没有这样做),- 数字部分可以转换成实际的整数
(
1 < 2 < 10
, 但'1' < '10' < '2'
)
应用这些之后:
import re
def keygen(sort_list):
index = {(word, index) for index, word in enumerate(sort_slist)}
return lambda elem: (
index[re.findall(r'^[a-zA-Z]+', elem)[0]],
[int(s) for s in re.findall(r'\d+$', elem)]
)
只要您知道 List2 只包含以 sortList
中的事物开头的字符串,这就有效List2=['ID','Color4','Color2','Size1','Size2','Name2','Name1']
sortList=['ID','Name','Size','Color']
def sort_fun(x):
for i, thing in enumerate(sortList):
if x.startswith(thing):
return (i, x[len(thing):])
print sorted(List2, key=sort_fun)