【问题标题】:Sort List in Python by two other lists按其他两个列表对 Python 中的列表进行排序
【发布时间】:2023-09-29 13:25:01
【问题描述】:

我的问题与12这两个链接非常相​​似:

我有三个不同的列表。我想根据 List2 对 List1 进行排序(按升序排列)。但是,我在 List2 中有重复。然后我想按 List3 对这些重复进行排序(按降序排列)。够迷惑了吧?

我有什么:

List1 = ['a', 'b', 'c', 'd', 'e']
List2 = [4, 2, 3, 2, 4]
List3 = [0.1, 0.8, 0.3, 0.6, 0.4]

我想要什么:

new_List1 = ['b', 'd', 'c', 'e', 'a']

'b' 出现在 'd' 之前,因为 0.8 > 0.6。 'e' 出现在 'a' 之前,因为 0.4 > 0.1。

【问题讨论】:

    标签: python list sorting


    【解决方案1】:

    我认为您应该能够通过以下方式做到这一点:

    paired_sorted = sorted(zip(List2,List3,List1),key = lambda x: (x[0],-x[1]))
    l2,l3,l1 = zip(*paired_sorted)
    

    在行动:

    >>> List1 = ['a', 'b', 'c', 'd', 'e']
    >>> List2 = [4, 2, 3, 2, 4]
    >>> List3 = [0.1, 0.8, 0.3, 0.6, 0.4]
    >>> paired_sorted = sorted(zip(List2,List3,List1),key = lambda x: (x[0],-x[1]))
    >>> l2,l3,l1 = zip(*paired_sorted)
    >>> print l1
    ('b', 'd', 'c', 'e', 'a')
    

    这是它的工作原理。首先,我们使用zip 匹配您列表中的相应元素。然后,我们首先根据 List2 中的项目和(否定的)List3 中的项目对这些元素进行排序。然后我们只需要使用zip 和参数解包再次拉出 List1 元素——尽管如果您想确保在一天结束时有一个列表而不是元组。

    如果你不能轻易否定 List3 中的值,这会变得有点困难——例如如果它们是字符串。您需要分 2 次进行排序:

    paired = zip(List2,List3,List1)
    rev_sorted = sorted(paired,reverse=True,key=lambda x: x[1])  #"minor" sort first
    paired_sorted = sorted(rev_sorted,key=lambda x:x[0])         #"major" sort last
    l2,l3,l1 = zip(*paired_sorted)
    

    (如果您愿意,可以使用operator.itemgetter(1) 代替上面的lambda x:x[1])。这是有效的,因为 python 排序是“稳定的”。它不会重新排序“相等”的元素。

    【讨论】:

    • 不是您需要对 List2 和 List3 进行排序,而是您的解决方案可以做到这一点。
    • @MartijnPieters -- 你的也是 ;-) -- 你只是没有保留结果。
    【解决方案2】:
    >>> [v for i, v in sorted(enumerate(List1), key=lambda i_v: (List2[i_v[0]], -List3[i_v[0]]))]
    ['b', 'd', 'c', 'e', 'a']
    

    这通过使用索引对索引/值对进行排序,以从其他列表中获取相应的值,以用于sorted() 用于排序的键函数,然后使用列表推导仅提取值。

    这是一个较短的替代方案,它只对索引进行排序,然后使用这些索引从 List1 中获取值:

    >>> [List1[i] for i in sorted(range(len(List1)), key=lambda i: (List2[i], -List3[i]))]
    ['b', 'd', 'c', 'e', 'a']
    

    【讨论】:

      【解决方案3】:

      这需要一个装饰-排序-取消装饰步骤:

      decorated = zip(List1, List2, List3)
      decorated.sort(key=lambda v: (v[1], -v[2]))
      new_list1 = [v[0] for v in decorated]
      

      或者,合并为一行:

      new_list1 = [v[0] for v in sorted(zip(List1, List2, List3), key=lambda v: (v[1], -v[2]))]
      

      输出:

      >>> List1 = ['a', 'b', 'c', 'd', 'e']
      >>> List2 = [4, 2, 3, 2, 4]
      >>> List3 = [0.1, 0.8, 0.3, 0.6, 0.4]
      >>> new_list1 = [v[0] for v in sorted(zip(List1, List2, List3), key=lambda v: (v[1], -v[2]))]
      >>> new_list1
      ['b', 'd', 'c', 'e', 'a']
      

      【讨论】:

      • 如果我们能弄清楚如果否定 List3 中的元素不会导致它以相反的顺序排序,那么我们可以弄清楚如何使这项工作发挥作用:) 例如-- 如果它包含字符串
      • @mgilson:我认为你可以分两次使用reverse=True,不是吗?
      • @DSM -- 是的。我知道这就是它所需要的,但由于某种原因,在我看到你的评论之前,我无法弄清楚物流。我已将其添加到我的答案中。