I wish to sort the below list first by the number, then by the text.
我想先把下面的列表按数字排序,然后再按文本排序。
lst = ['b-3', 'a-2', 'c-4', 'd-2']
# result:
# ['a-2', 'd-2', 'b-3', 'c-4']
Attempt 1
尝试1
res = sorted(lst, key=lambda x: (int(x.split('-')[1]), x.split('-')[0]))
I was not happy with this since it required splitting a string twice, to extract the relevant components.
我对此并不满意,因为它需要分割两个字符串来提取相关的组件。
Attempt 2
尝试2
I came up with the below solution. But I am hoping there is a more succinct solution via Pythonic lambda
statements.
我想到了下面的解决方案。但是我希望通过python的lambda表达式有一个更简洁的解决方案。
def sorter_func(x):
text, num = x.split('-')
return int(num), text
res = sorted(lst, key=sorter_func)
I looked at Understanding nested lambda function behaviour in python but couldn't adapt this solution directly. Is there a more succinct way to rewrite the above code?
我研究了理解python中的嵌套lambda函数行为,但无法直接采用这种解决方案。有没有更简洁的方法重写上面的代码?
7 个解决方案
#1
15
There are 2 points to note:
有两点需要注意:
- One-line answers are not necessarily better. Using a named function is likely to make your code easier to read.
- 简单的答案不一定更好。使用命名函数可能会使您的代码更易于阅读。
- You are likely not looking for a nested
lambda
statement, as function composition is not part of the standard library (see Note #1). What you can do easily is have onelambda
function return the result of anotherlambda
function. - 您可能不会寻找嵌套的lambda语句,因为函数组合不是标准库的一部分(参见注释#1)。你能做的就是让一个函数返回另一个函数的结果。
Therefore, the correct answer can found in Lambda inside lambda.
因此,正确的答案可以在里面找到。
For your specific problem, you can use:
对于您的具体问题,您可以使用:
res = sorted(lst, key=lambda x: (lambda y: (int(y[1]), y[0]))(x.split('-')))
Remember that lambda
is just a function. You can call it immediately after defining it, even on the same line.
记住,只是一个函数。你可以在定义它之后立即调用它,即使是在同一条线上。
Note #1: The 3rd party toolz
library does allow composition:
注1:第三方工具库允许合成:
from toolz import compose
res = sorted(lst, key=compose(lambda x: (int(x[1]), x[0]), lambda x: x.split('-')))
Note #2: As @chepner points out, the deficiency of this solution (repeated function calls) is one of the reasons why PEP-572 is being considered.
注意#2:正如@chepner指出的,这个解决方案(重复的函数调用)的不足是PEP-572被考虑的原因之一。
#2
6
We can wrap the list returned by split('-')
under another list and then we can use a loop to handle it:
我们可以将split('-')返回的列表封装到另一个列表中,然后使用循环来处理:
# Using list-comprehension
>>> sorted(lst, key=lambda x: [(int(num), text) for text, num in [x.split('-')]])
['a-2', 'd-2', 'b-3', 'c-4']
# Using next()
>>> sorted(lst, key=lambda x: next((int(num), text) for text, num in [x.split('-')]))
['a-2', 'd-2', 'b-3', 'c-4']
#3
2
lst = ['b-3', 'a-2', 'c-4', 'd-2']
res = sorted(lst, key=lambda x: tuple(f(a) for f, a in zip((int, str), reversed(x.split('-')))))
print(res)
['a-2', 'd-2', 'b-3', 'c-4']
#4
1
you could convert to integer only if the index of the item is 0 (when reversing the splitted list). The only object (besides the result of split
) which is created is the 2-element list used for comparison. The rest are just iterators.
只有当项的索引为0时(在反转分割列表时),才能将其转换为整数。惟一的对象(除了拆分的结果)是用于比较的2元素列表。剩下的只是迭代器。
sorted(lst,key = lambda s : [x if i else int(x) for i,x in enumerate(reversed(s.split("-")))])
As an aside, the -
token isn't particularly great when numbers are involved, because it complicates the use of negative numbers (but can be solved with s.split("-",1)
顺便说一下,当涉及到数字时,令牌并不特别好,因为它使负数的使用变得复杂(但可以用s.split(“-”,1)来解决)。
#5
0
lst = ['b-3', 'a-2', 'c-4', 'd-2']
def xform(l):
return list(map(lambda x: x[1] + '-' + x[0], list(map(lambda x: x.split('-'), lst))))
lst = sorted(xform(lst))
print(xform(lst))
See it here I think @jpp has a better solution, but a fun little brainteaser :-)
我认为@jpp有一个更好的解决方案,但是一个有趣的小脑筋急事:-)
#6
0
In general with FOP ( functional oriented programming ) you can put it all in one liner and nest lambdas within one-liners but that is in general bad etiquette, since after 2 nesting function it all becomes quite unreadable.
一般来说,在FOP(面向函数的编程)中,您可以将所有内容放在一行中,并且在一行中使用nest lambdas,但这在一般情况下是很糟糕的礼仪,因为在两个嵌套函数之后,所有内容都变得非常不可读。
The best way to approach this kind of issue is to split it up in several stages:
解决这类问题的最好办法是把它分成几个阶段:
1: splitting string into tuple
:
1:将字符串分割成元组:
lst = ['b-3', 'a-2', 'c-4', 'd-2']
res = map( lambda str_x: tuple( str_x.split('-') ) , lst)
2: sorting elements like you wished :
2:按照您的要求对元素进行排序:
lst = ['b-3', 'a-2', 'c-4', 'd-2']
res = map( lambda str_x: tuple( str_x.split('-') ) , lst)
res = sorted( res, key=lambda x: ( int(x[1]), x[0] ) )
Since we split the string into tuple it will return an map object that will be represented as list of tuples. So now the 3rd step is optional:
由于我们将字符串分割为tuple,它将返回一个映射对象,该对象将表示为元组列表。所以第三步是可选的
3: representing data as you inquired:
3:按照您的要求表示数据:
lst = ['b-3', 'a-2', 'c-4', 'd-2']
res = map( lambda str_x: tuple( str_x.split('-') ) , lst)
res = sorted( res, key=lambda x: ( int(x[1]), x[0] ) )
res = map( '-'.join, res )
Now have in mind that lambda nesting
could produce a more one-liner solution and that you can actually embed a non discrete nesting type of lambda like follows:
现在请记住,lambda嵌套可以生成一个更简单的线性解决方案,并且您实际上可以嵌入一个非离散的嵌套类型,例如:
a = ['b-3', 'a-2', 'c-4', 'd-2']
resa = map( lambda x: x.split('-'), a)
resa = map( lambda x: ( int(x[1]),x[0]) , a)
# resa can be written as this, but you must be sure about type you are passing to lambda
resa = map( lambda x: tuple( map( lambda y: int(y) is y.isdigit() else y , x.split('-') ) , a)
But as you can see if contents of list a
arent anything but 2 string types separated by '-'
, lambda
function will raise an error and you will have a bad time figuring what the hell is happening.
但是你可以看到,如果列表a的内容除了两个字符串类型用'-'分隔之外没有其他内容,lambda函数会产生一个错误,你将很难搞清楚到底发生了什么。
So in the end, i would like to show you several ways the 3rd step program could be written:
最后,我想向大家展示第三步程序的几种写法:
1:
1:
lst = ['b-3', 'a-2', 'c-4', 'd-2']
res = map( '-'.join,\
sorted(\
map( lambda str_x: tuple( str_x.split('-') ) , lst),\
key=lambda x: ( int(x[1]), x[0] )\
)\
)
2:
2:
lst = ['b-3', 'a-2', 'c-4', 'd-2']
res = map( '-'.join,\
sorted( map( lambda str_x: tuple( str_x.split('-') ) , lst),\
key=lambda x: tuple( reversed( tuple(\
map( lambda y: int(y) if y.isdigit() else y ,x )\
)))\
)\
) # map isn't reversible
3:
3:
res = sorted( lst,\
key=lambda x:\
tuple(reversed(\
tuple( \
map( lambda y: int(y) if y.isdigit() else y , x.split('-') )\
)\
))\
)
So you can see how this all can get very complicated and incomprehensible. When reading my own or someone else's code i often love to see this version:
所以你可以看到这一切是如何变得非常复杂和难以理解的。当我读我自己或别人的代码时,我经常喜欢看这个版本:
res = map( lambda str_x: tuple( str_x.split('-') ) , lst) # splitting string
res = sorted( res, key=lambda x: ( int(x[1]), x[0] ) ) # sorting for each element of splitted string
res = map( '-'.join, res ) # rejoining string
That is all from me. Have fun. I've tested all code in py 3.6
.
这都是我说的。玩得开心。我在py 3.6中测试了所有代码。
PS. In general, you have 2 ways to approach lambda functions
:
一般来说,有两种方法可以接近lambda函数:
mult = lambda x: x*2
mu_add= lambda x: mult(x)+x #calling lambda from lambda
This way is useful for typical FOP,where you have constant data , and you need to manipulate each element of that data. But if you need to resolve list,tuple,string,dict
in lambda
these kind of operations aren't very useful, since if any of those container/wrapper
types is present the data type of elements inside containers becomes questionable. So we would need to go up a level of abstraction and determine how to manipulate data per its type.
这种方法对于典型的FOP非常有用,在FOP中,您有常量数据,并且需要操作数据的每个元素。但是,如果您需要解析列表、元组、字符串、lambda中的dict类型,这些操作不是非常有用,因为如果存在任何这些容器/包装器类型,容器中的元素的数据类型就会变得有问题。因此,我们需要提升抽象级别,并确定如何根据其类型操作数据。
mult_i = lambda x: x*2 if isinstance(x,int) else 2 # some ternary operator to make our life easier by putting if statement in lambda
Now you can use another type of lambda
function:
现在你可以使用另一种类型的lambda函数:
int_str = lambda x: ( lambda y: str(y) )(x)*x # a bit of complex, right?
# let me break it down.
#all this could be written as:
str_i = lambda x: str(x)
int_str = lambda x: str_i(x)*x
## we can separate another function inside function with ()
##because they can exclude interpreter to look at it first, then do the multiplication
# ( lambda x: str(x)) with this we've separated it as new definition of function
# ( lambda x: str(x) )(i) we called it and passed it i as argument.
Some people call this type of syntax as nested lambdas, i call it indiscreet since you can see all.
有些人把这种类型的语法称为嵌套的lambdas,我把它称为轻率的,因为您可以看到所有内容。
And you can use recursive lambda assignment:
你可以使用递归分配
def rec_lambda( data, *arg_lambda ):
# filtering all parts of lambda functions parsed as arguments
arg_lambda = [ x for x in arg_lambda if type(x).__name__ == 'function' ]
# implementing first function in line
data = arg_lambda[0](data)
if arg_lambda[1:]: # if there are still elements in arg_lambda
return rec_lambda( data, *arg_lambda[1:] ) #call rec_lambda
else: # if arg_lambda is empty or []
return data # returns data
#where you can use it like this
a = rec_lambda( 'a', lambda x: x*2, str.upper, lambda x: (x,x), '-'.join)
>>> 'AA-AA'
#7
-3
I think* if you are certain the format is consistently "[0]alphabet [1]dash" following indexes beyond [2:] will always be number, then you can replace split with slice, or you can use str.index('-')
我认为,如果你确定这种格式始终是“[0]字母[1]dash”后面的索引[2:]将永远是数字,然后你可以用slice代替split,或者你可以使用str.index('-')
sorted(lst, key=lambda x:(int(x[2:]),x[0]))
# str.index('-')
sorted(lst, key=lambda x:(int(x[x.index('-')+1 :]),x[0]))
#1
15
There are 2 points to note:
有两点需要注意:
- One-line answers are not necessarily better. Using a named function is likely to make your code easier to read.
- 简单的答案不一定更好。使用命名函数可能会使您的代码更易于阅读。
- You are likely not looking for a nested
lambda
statement, as function composition is not part of the standard library (see Note #1). What you can do easily is have onelambda
function return the result of anotherlambda
function. - 您可能不会寻找嵌套的lambda语句,因为函数组合不是标准库的一部分(参见注释#1)。你能做的就是让一个函数返回另一个函数的结果。
Therefore, the correct answer can found in Lambda inside lambda.
因此,正确的答案可以在里面找到。
For your specific problem, you can use:
对于您的具体问题,您可以使用:
res = sorted(lst, key=lambda x: (lambda y: (int(y[1]), y[0]))(x.split('-')))
Remember that lambda
is just a function. You can call it immediately after defining it, even on the same line.
记住,只是一个函数。你可以在定义它之后立即调用它,即使是在同一条线上。
Note #1: The 3rd party toolz
library does allow composition:
注1:第三方工具库允许合成:
from toolz import compose
res = sorted(lst, key=compose(lambda x: (int(x[1]), x[0]), lambda x: x.split('-')))
Note #2: As @chepner points out, the deficiency of this solution (repeated function calls) is one of the reasons why PEP-572 is being considered.
注意#2:正如@chepner指出的,这个解决方案(重复的函数调用)的不足是PEP-572被考虑的原因之一。
#2
6
We can wrap the list returned by split('-')
under another list and then we can use a loop to handle it:
我们可以将split('-')返回的列表封装到另一个列表中,然后使用循环来处理:
# Using list-comprehension
>>> sorted(lst, key=lambda x: [(int(num), text) for text, num in [x.split('-')]])
['a-2', 'd-2', 'b-3', 'c-4']
# Using next()
>>> sorted(lst, key=lambda x: next((int(num), text) for text, num in [x.split('-')]))
['a-2', 'd-2', 'b-3', 'c-4']
#3
2
lst = ['b-3', 'a-2', 'c-4', 'd-2']
res = sorted(lst, key=lambda x: tuple(f(a) for f, a in zip((int, str), reversed(x.split('-')))))
print(res)
['a-2', 'd-2', 'b-3', 'c-4']
#4
1
you could convert to integer only if the index of the item is 0 (when reversing the splitted list). The only object (besides the result of split
) which is created is the 2-element list used for comparison. The rest are just iterators.
只有当项的索引为0时(在反转分割列表时),才能将其转换为整数。惟一的对象(除了拆分的结果)是用于比较的2元素列表。剩下的只是迭代器。
sorted(lst,key = lambda s : [x if i else int(x) for i,x in enumerate(reversed(s.split("-")))])
As an aside, the -
token isn't particularly great when numbers are involved, because it complicates the use of negative numbers (but can be solved with s.split("-",1)
顺便说一下,当涉及到数字时,令牌并不特别好,因为它使负数的使用变得复杂(但可以用s.split(“-”,1)来解决)。
#5
0
lst = ['b-3', 'a-2', 'c-4', 'd-2']
def xform(l):
return list(map(lambda x: x[1] + '-' + x[0], list(map(lambda x: x.split('-'), lst))))
lst = sorted(xform(lst))
print(xform(lst))
See it here I think @jpp has a better solution, but a fun little brainteaser :-)
我认为@jpp有一个更好的解决方案,但是一个有趣的小脑筋急事:-)
#6
0
In general with FOP ( functional oriented programming ) you can put it all in one liner and nest lambdas within one-liners but that is in general bad etiquette, since after 2 nesting function it all becomes quite unreadable.
一般来说,在FOP(面向函数的编程)中,您可以将所有内容放在一行中,并且在一行中使用nest lambdas,但这在一般情况下是很糟糕的礼仪,因为在两个嵌套函数之后,所有内容都变得非常不可读。
The best way to approach this kind of issue is to split it up in several stages:
解决这类问题的最好办法是把它分成几个阶段:
1: splitting string into tuple
:
1:将字符串分割成元组:
lst = ['b-3', 'a-2', 'c-4', 'd-2']
res = map( lambda str_x: tuple( str_x.split('-') ) , lst)
2: sorting elements like you wished :
2:按照您的要求对元素进行排序:
lst = ['b-3', 'a-2', 'c-4', 'd-2']
res = map( lambda str_x: tuple( str_x.split('-') ) , lst)
res = sorted( res, key=lambda x: ( int(x[1]), x[0] ) )
Since we split the string into tuple it will return an map object that will be represented as list of tuples. So now the 3rd step is optional:
由于我们将字符串分割为tuple,它将返回一个映射对象,该对象将表示为元组列表。所以第三步是可选的
3: representing data as you inquired:
3:按照您的要求表示数据:
lst = ['b-3', 'a-2', 'c-4', 'd-2']
res = map( lambda str_x: tuple( str_x.split('-') ) , lst)
res = sorted( res, key=lambda x: ( int(x[1]), x[0] ) )
res = map( '-'.join, res )
Now have in mind that lambda nesting
could produce a more one-liner solution and that you can actually embed a non discrete nesting type of lambda like follows:
现在请记住,lambda嵌套可以生成一个更简单的线性解决方案,并且您实际上可以嵌入一个非离散的嵌套类型,例如:
a = ['b-3', 'a-2', 'c-4', 'd-2']
resa = map( lambda x: x.split('-'), a)
resa = map( lambda x: ( int(x[1]),x[0]) , a)
# resa can be written as this, but you must be sure about type you are passing to lambda
resa = map( lambda x: tuple( map( lambda y: int(y) is y.isdigit() else y , x.split('-') ) , a)
But as you can see if contents of list a
arent anything but 2 string types separated by '-'
, lambda
function will raise an error and you will have a bad time figuring what the hell is happening.
但是你可以看到,如果列表a的内容除了两个字符串类型用'-'分隔之外没有其他内容,lambda函数会产生一个错误,你将很难搞清楚到底发生了什么。
So in the end, i would like to show you several ways the 3rd step program could be written:
最后,我想向大家展示第三步程序的几种写法:
1:
1:
lst = ['b-3', 'a-2', 'c-4', 'd-2']
res = map( '-'.join,\
sorted(\
map( lambda str_x: tuple( str_x.split('-') ) , lst),\
key=lambda x: ( int(x[1]), x[0] )\
)\
)
2:
2:
lst = ['b-3', 'a-2', 'c-4', 'd-2']
res = map( '-'.join,\
sorted( map( lambda str_x: tuple( str_x.split('-') ) , lst),\
key=lambda x: tuple( reversed( tuple(\
map( lambda y: int(y) if y.isdigit() else y ,x )\
)))\
)\
) # map isn't reversible
3:
3:
res = sorted( lst,\
key=lambda x:\
tuple(reversed(\
tuple( \
map( lambda y: int(y) if y.isdigit() else y , x.split('-') )\
)\
))\
)
So you can see how this all can get very complicated and incomprehensible. When reading my own or someone else's code i often love to see this version:
所以你可以看到这一切是如何变得非常复杂和难以理解的。当我读我自己或别人的代码时,我经常喜欢看这个版本:
res = map( lambda str_x: tuple( str_x.split('-') ) , lst) # splitting string
res = sorted( res, key=lambda x: ( int(x[1]), x[0] ) ) # sorting for each element of splitted string
res = map( '-'.join, res ) # rejoining string
That is all from me. Have fun. I've tested all code in py 3.6
.
这都是我说的。玩得开心。我在py 3.6中测试了所有代码。
PS. In general, you have 2 ways to approach lambda functions
:
一般来说,有两种方法可以接近lambda函数:
mult = lambda x: x*2
mu_add= lambda x: mult(x)+x #calling lambda from lambda
This way is useful for typical FOP,where you have constant data , and you need to manipulate each element of that data. But if you need to resolve list,tuple,string,dict
in lambda
these kind of operations aren't very useful, since if any of those container/wrapper
types is present the data type of elements inside containers becomes questionable. So we would need to go up a level of abstraction and determine how to manipulate data per its type.
这种方法对于典型的FOP非常有用,在FOP中,您有常量数据,并且需要操作数据的每个元素。但是,如果您需要解析列表、元组、字符串、lambda中的dict类型,这些操作不是非常有用,因为如果存在任何这些容器/包装器类型,容器中的元素的数据类型就会变得有问题。因此,我们需要提升抽象级别,并确定如何根据其类型操作数据。
mult_i = lambda x: x*2 if isinstance(x,int) else 2 # some ternary operator to make our life easier by putting if statement in lambda
Now you can use another type of lambda
function:
现在你可以使用另一种类型的lambda函数:
int_str = lambda x: ( lambda y: str(y) )(x)*x # a bit of complex, right?
# let me break it down.
#all this could be written as:
str_i = lambda x: str(x)
int_str = lambda x: str_i(x)*x
## we can separate another function inside function with ()
##because they can exclude interpreter to look at it first, then do the multiplication
# ( lambda x: str(x)) with this we've separated it as new definition of function
# ( lambda x: str(x) )(i) we called it and passed it i as argument.
Some people call this type of syntax as nested lambdas, i call it indiscreet since you can see all.
有些人把这种类型的语法称为嵌套的lambdas,我把它称为轻率的,因为您可以看到所有内容。
And you can use recursive lambda assignment:
你可以使用递归分配
def rec_lambda( data, *arg_lambda ):
# filtering all parts of lambda functions parsed as arguments
arg_lambda = [ x for x in arg_lambda if type(x).__name__ == 'function' ]
# implementing first function in line
data = arg_lambda[0](data)
if arg_lambda[1:]: # if there are still elements in arg_lambda
return rec_lambda( data, *arg_lambda[1:] ) #call rec_lambda
else: # if arg_lambda is empty or []
return data # returns data
#where you can use it like this
a = rec_lambda( 'a', lambda x: x*2, str.upper, lambda x: (x,x), '-'.join)
>>> 'AA-AA'
#7
-3
I think* if you are certain the format is consistently "[0]alphabet [1]dash" following indexes beyond [2:] will always be number, then you can replace split with slice, or you can use str.index('-')
我认为,如果你确定这种格式始终是“[0]字母[1]dash”后面的索引[2:]将永远是数字,然后你可以用slice代替split,或者你可以使用str.index('-')
sorted(lst, key=lambda x:(int(x[2:]),x[0]))
# str.index('-')
sorted(lst, key=lambda x:(int(x[x.index('-')+1 :]),x[0]))