[英]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_2
( lst_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.