【问题标题】:Custom 2D list sort with lambda expression (in 1 line) in Python在 Python 中使用 lambda 表达式(在 1 行中)自定义二维列表排序
【发布时间】:2020-11-07 23:01:09
【问题描述】:

我有一个像这样的 python 2D 列表-

[[3,4],[1,2],[2,1],[6,5]]

我喜欢让它在两个方向上进行排序,行和列。所以,我想要的输出是这样的-

[[1, 2], [1, 2], [3, 4], [5, 6]]

我所做的是-

list.sort(key = lambda x: (x[1], x[0]))

而我得到的是-

[[2, 1], [1, 2], [3, 4], [6, 5]]

谁能帮忙用lambda表达式对in-place进行排序?

【问题讨论】:

  • 您不能对列表以及在键中使用单个 lamda 表达式的元素进行排序。您可以使用 listcomprehension 对每个元素进行排序,然后在新列表上使用 sort() 对整个列表进行排序。当然也可以是一行
  • 为什么排序“获取密钥”功能会更改条目?它不会 - 也不应该因为如果这实际上是使用排序函数完成的,事情可能会中断。您可能希望在包含集合中将每对作为其自己的集合进行排序(请参阅列表理解),然后对(内部排序的)对的集合进行排序。跨度>
  • key = lambda x: (x[1], x[0])的目的是什么?

标签: python arrays list algorithm lambda


【解决方案1】:

如果你真的想在一条线上就地完成所有这些,你可以用这样可怕的东西来实现它:

vals = [[3, 4], [1, 2], [2, 1], [6, 5]]

vals.sort(key=lambda x: (lambda x, dummy: (x[1], x[0]))(x, x.sort()))

print(vals)

输出:[[1, 2], [1, 2], [3, 4], [5, 6]]

虽然这确实符合您的要求,但效率低下且无法阅读。我强烈建议按照其他答案中的说明正确执行。

【讨论】:

  • 双 lambda 表达式的意义何在?无论如何,实际工作是由x.sort() 完成的。要使外部列表仅在内部列表排序之后进行排序,您只需在x 之前调用它。因此,您可以大大简化代码并使用单个函数调用。
【解决方案2】:

key 参数(和lambda)并不意味着修改list 的内容。相反,它用于根据每个元素在应用key 函数时的评估方式进行排序。 但是,您可以通过在 key 的函数参数上调用 .sort() 来使用 key 的副作用来实现您的目标。由于.sort() 结果只是None,因此您还需要提供参数本身以用于实际排序:

l = [[3,4],[1,2],[2,1],[6,5]]

l.sort(key=lambda x: (x.sort(), x))

print(l)
# [[1, 2], [1, 2], [3, 4], [5, 6]]

不过,这并不是一种好的编程习惯。

一种更清洁、更高效但显然不是单行的方法是:

l = [[3,4],[1,2],[2,1],[6,5]]
for x in l:
    x.sort()
l.sort()

print(l)
# [[1, 2], [1, 2], [3, 4], [5, 6]]

基于key 的方法效率也显着降低,因为它尝试在每个key 调用中对内部列表进行排序,我们应该期望在n log n 次调用中对内部列表进行排序,而不是严格要求的n 次调用,因此一些内部列表不可避免地被排序超过必要。相反,遍历外部列表显式地对每个内部列表进行一次排序。

只是给出一些时间的想法:

import random
import copy
import collections


def double_sort_loop(seq):
    for x in seq:
        x.sort()
    seq.sort()


def double_sort_deque(seq):
    collections.deque(map(lambda x: x.sort(), seq), maxlen=0)
    seq.sort()


def double_sort_key(seq):
    seq.sort(key=lambda x: (x.sort(), x))


def gen_input(n, m, v_min=None, v_max=None):
    if v_min is None:
        v_min = 0
    if v_max is None:
        v_max = v_min + (2 * m * n)
    return [[random.randint(v_min, v_max) for _ in range(m)] for _ in range(n)]


random.seed(0)

base = gen_input(100000, 10)

%timeit seq = copy.deepcopy(base); double_sort_loop(seq)
# 1 loop, best of 3: 1.03 s per loop
%timeit seq = copy.deepcopy(base); double_sort_deque(seq)
# 1 loop, best of 3: 1.02 s per loop
%timeit seq = copy.deepcopy(base); double_sort_key(seq)
# 1 loop, best of 3: 1.19 s per loop

已编辑

【讨论】:

    【解决方案3】:

    您无法就地排序,因为您需要先创建修改后的内部元素列表。你能做的最好的就是

    [sorted(x) for x in my_list].sort(key=lambda k: (k[0], k[1]))
    

    【讨论】:

    • 这不是 OP 所要求的。并且过于复杂。 sorted_list = sorted([sorted(item) for item in my_list]) 也可以这样做
    • 嗨,我喜欢in-pace 没有多余空间的排序(stackoverflow.com/a/16585637/2193439),这可能吗?
    • @EhteshamSiddiqui,我的代码将生成 [[1, 2], [1, 2], [3, 4], [5, 6]] - 正是 OP 想要的。
    • @EhteshamSiddiqui,你完全糊涂了。输入[[2, 1], [1, 1]],结果将是[[1, 1], [1, 2]] - 正如预期的那样。
    猜你喜欢
    • 2022-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-21
    • 2015-02-06
    • 2013-08-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多