簡體   English   中英

如何根據另一個列表列表的值對列表列表進行排序?

[英]How to sort a list of lists based on values of another list of lists?

我的兩個列表是:

lst_1 = [[1, 'John'], [2, 'Mcquin'], [4, 'Paul'], [7, 'Jimmy'], [9, 'Coco'], [11, 'Coco']]
lst_2 = [[3, 'Mcquin', 1], [6, 'Paul', 6], [5, 'John', 15], [12, 'Coco', 18], [8, 'Jimmy', 24], [10, 'Coco', 24]]

根據lst_2lst_2[i][1] )子列表中的第二個值對lst_1進行排序的最有效方法是什么? 首選輸出:

[[2, 'Mcquin'], [4, 'Paul'], [1, 'John'], [9, 'Coco'], [7, 'Jimmy'], [11, 'Coco']]

如果有相同名稱的重復(在這種情況下為Coco)並不重要。 此外,列表將始終包含與此處相同的名稱。

如果兩個列表的名稱數量相同,則可以將每個元素的索引存儲在collections.defaultdict ,然后彈出每個索引,並在排序期間找到項目時將其用作排序鍵。

演示:

from collections import defaultdict, deque

lst_1 = [[1, 'John'], [2, 'Mcquin'], [4, 'Paul'], [7, 'Jimmy'], [9, 'Coco'], [11, 'Coco']]
lst_2 = [[3, 'Mcquin', 1], [6, 'Paul', 6], [5, 'John', 15], [12, 'Coco', 18], [8, 'Jimmy', 24], [10, 'Coco', 24]]

sort_map = defaultdict(deque)
for i, x in enumerate(lst_2):
    sort_map[x[1]].append(i)

result = sorted(lst_1, key=lambda x: sort_map[x[1]].popleft())

print(result)

輸出:

[[2, 'Mcquin'], [4, 'Paul'], [1, 'John'], [9, 'Coco'], [7, 'Jimmy'], [11, 'Coco']]. 

注意:您可以使用collections.deque在常量時間從頭開始彈出元素,如上所示。 這種微小的改進允許上述解決方案保持在整體O(NlogN),這是分揀的成本。

編輯:我想我有一個O(n)解決方案!


最初,我認為我們可以根據lst_2創建一個dictionary ,列出它們應該出現在最終列表中的名稱和索引。 然后我們可以通過對lst_1進行排序來創建最終列表 - 給出一個O(n log(n))解決方案。

但是,該方法的問題在於lst_2中存在重復的名稱! 此外,這種新方法甚至具有更好的時間復雜度!


首先,我們基於lst_1創建一個字典其中每個key都是一個名稱,每個值都是一個 list collections.deque (感謝RoadRunner),其中包含與該名稱對應的數字。

通過使用deque ,我們使用相同的名稱維護lst_1中這些元素的順序。 此外,我們有能力在O(1)時間.popleft deque上調用.popleft

然后,這允許我們迭代lst_2 (不再需要任何排序,因為它已經按順序排列)並將新名稱附加到名稱后跟我們創建的字典中的第一個值條目。

如果我們用.popleft()來獲得的第一個元素,我們也將其刪除這意味着當這個名字在明年出現lst_2 ,我們得到的下一個值lst_1

那么,這是代碼:

import collections
vals = {}
for v, n in lst_1:
    vals.setdefault(n, collections.deque()).append(v)

#vals == {'Paul': [4], 'Coco': [9, 11], 'John': [1], 'Mcquin': [2], 'Jimmy': [7]}
#        (each key here ^^ is actually a deque but it's easier to see with lists)
r = []
for _,n,_ in lst_2:
    r.append([n, vals[n].popleft()])

r (結果)如下:

[['Mcquin', 2], ['Paul', 4], ['John', 1], ['Coco', 9], ['Jimmy', 7], ['Coco', 11]]

非常不詭異,但仍然易於理解和工作:

lst_new = []
for item in lst_2:
    name = item[1]
    for item2 in lst_1:
        if name == item2[1]:
            lst_new.append(list.copy(item2))
            lst_1.remove(item2)
            #item2[1] = "" is also an option but it's worse for long inputs
            break

輸出:

>>> lst_new
[[2, 'Mcquin'], [4, 'Paul'], [1, 'John'], [9, 'Coco'], [7, 'Jimmy'], [11, 'Coco']]

給出兩個列表:

xs = [[4, 'a'], [3, 'b'], [7, 'c'], [10, 'd']]
ys = [ 7, 3, 4, 10]

以下行按照ys的項目順序對列表xs進行排序:

[x for y in ys for x in xs if x[0] == y]

結果:

>>> [x for y in ys for x in xs if x[0] == y]
[[7, 'c'], [3, 'b'], [4, 'a'], [10, 'd']]

嘗試這個:

l = sorted(lst_1, key=lambda x: [i[2] for i in lst_2 if i[1] == x[1]][0])

說明:只有當第二個值與參數(i [1] == x [1])匹配時,我們才使用來自lst_2的第3個值(i [2])進行排序。

請注意,如果lst_2中缺少lst_1中存在的值,則會產生錯誤(可能是合理的,因為缺少密鑰)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM