我有一个元组列表to_order
,例如:
to_order = [(0, 1), (1, 3), (2, 2), (3,2)]
给出一个列表,它指定了对 to_order
每个元组的第二个元素应用的顺序:
order = [2, 1, 3]
所以我正在寻找一种方法来获得这个输出:
ordered_list = [(2, 2), (3,2), (0, 1), (1, 3)]
有什么想法吗?
您可以提供一个key
,它将检查在order
中第二个元素的索引,并基于其进行排序:
to_order = [(0, 1), (1, 3), (2, 2), (3,2)]
order = [2, 1, 3]
print(sorted(to_order, key=lambda item: order.index(item[1]))) # [(2, 2), (3, 2), (0, 1), (1, 3)]
编辑
既然讨论了时间复杂度,以下算法在使用Eric的输入示例时运行时间为O(n+m)
:
N = 5
to_order = [(randrange(N), randrange(N)) for _ in range(10*N)]
order = list(set(pair[1] for pair in to_order))
shuffle(order)
def eric_sort(to_order, order):
bins = {}
for pair in to_order:
bins.setdefault(pair[1], []).append(pair)
return [pair for i in order for pair in bins[i]]
def alfasin_new_sort(to_order, order):
arr = [[] for i in range(len(order))]
d = {k:v for v, k in enumerate(order)}
for item in to_order:
arr[d[item[1]]].append(item)
return [item for sublist in arr for item in sublist]
from timeit import timeit
print("eric_sort", timeit("eric_sort(to_order, order)", setup=setup, number=1000))
print("alfasin_new_sort", timeit("alfasin_new_sort(to_order, order)", setup=setup, number=1000))
输出:
eric_sort 59.282021682999584
alfasin_new_sort 44.28244407700004
sorted(to_order, key=lambda x: (x[0], x[1]))
但是我IRC那是默认行为,所以您也可以简单地执行:sorted(to_order)
。 - Nir Alfasi你可以根据第二个元素将元组分发到一个列表的字典中,并迭代 order
索引以获取排序后的列表:
from collections import defaultdict
to_order = [(0, 1), (1, 3), (2, 2), (3, 2)]
order = [2, 1, 3]
bins = defaultdict(list)
for pair in to_order:
bins[pair[1]].append(pair)
print(bins)
# defaultdict(<class 'list'>, {1: [(0, 1)], 3: [(1, 3)], 2: [(2, 2), (3, 2)]})
print([pair for i in order for pair in bins[i]])
# [(2, 2), (3, 2), (0, 1), (1, 3)]
sort
或index
不需要,输出是稳定的。
这个算法类似于所谓的重复中提到的mapping
。这个链接的答案只适用于to_order
和order
具有相同的长度,这在OP的问题中并不是情况。
该算法对to_order
的每个元素进行两次迭代。复杂度为O(n)
。@alfasin的第一个算法要慢得多(O(n * m * log n)
),但他的第二个算法也是O(n)
。
这里是一个包含10000个随机对的列表,介于0
和1000
之间。我们提取唯一的第二个元素,并将它们混洗以定义order
:
from random import randrange, shuffle
from collections import defaultdict
from timeit import timeit
from itertools import chain
N = 1000
to_order = [(randrange(N), randrange(N)) for _ in range(10*N)]
order = list(set(pair[1] for pair in to_order))
shuffle(order)
def eric(to_order, order):
bins = defaultdict(list)
for pair in to_order:
bins[pair[1]].append(pair)
return list(chain.from_iterable(bins[i] for i in order))
def alfasin1(to_order, order):
arr = [[] for i in range(len(order))]
d = {k:v for v, k in enumerate(order)}
for item in to_order:
arr[d[item[1]]].append(item)
return [item for sublist in arr for item in sublist]
def alfasin2(to_order, order):
return sorted(to_order, key=lambda item: order.index(item[1]))
print(eric(to_order, order) == alfasin1(to_order, order))
# True
print(eric(to_order, order) == alfasin2(to_order, order))
# True
print("eric", timeit("eric(to_order, order)", globals=globals(), number=100))
# eric 0.3117517130003762
print("alfasin1", timeit("alfasin1(to_order, order)", globals=globals(), number=100))
# alfasin1 0.36100843100030033
print("alfasin2", timeit("alfasin2(to_order, order)", globals=globals(), number=100))
# alfasin2 15.031453827000405
order
排序。如果一对具有相同的第二个元素,则它们按照输入中的顺序返回。这与描述和其他答案一致。如果您想对每个子列表进行排序,可以在bins.values()
中这样做。 - Eric Duminilchain.from_iterable
是一个很好的改进,特别是如果你可以在不将其转换为列表的情况下使用它。 - Eric Duminil[item for key in order for item in filter(lambda x: x[1] == key, to_order)]
这个解决方案首先根据order
对to_order
进行过滤,对于order
中的每个key
,筛选出与该key
相等的元素。ordered = []
for key in order:
for item in filter(lambda x: x[1] == key, to_order):
ordered.append(item)
更简洁的写法,但我不知道如何用列表推导式实现:
ordered = []
for key in order:
ordered.extend(filter(lambda x: x[1] == key, to_order))
to_order
包含元组x
,并且x [1]
不在order
中,则不会抛出 ValueError
。[x for y in order for x in to_order if x[1] == y]
。然而,它非常慢,甚至比被接受的答案还要慢。 - Eric Duminil个人而言,我更喜欢使用list
对象的sort
函数,而不是内置的sort
函数,因为后者会生成一个新列表,而不是直接改变原列表。
to_order = [(0, 1), (1, 3), (2, 2), (3,2)]
order = [2, 1, 3]
to_order.sort(key=lambda x: order.index(x[1]))
print(to_order)
>[(2, 2), (3, 2), (0, 1), (1, 3)]
x = [1,2,3,4,5,3,3,5]
print x.index(5)
>4
order
索引有多个to_order
对。所谓重复的高效方法(使用“mapping”)在这种情况下不适用。 - Eric Duminil