Python 如果列表项位于另一个列表中,如何在保持顺序的同时将其移到前面

Python 如果列表项位于另一个列表中,如何在保持顺序的同时将其移到前面,python,Python,我有两个示例列表 vals = ["a", "c", "d", "e", "f", "g"] xor = ["c", "g"] 我想根据xor列表对vals列表进行排序, i、 例如,xor中的值应按精确顺序放在vals列表的第一位。VAL中的其余值应保持相同顺序 另外,xor中的值可能不在vals中,在这些情况下,只需忽略这些值即

我有两个示例列表

vals = ["a", "c", "d", "e", "f", "g"]
xor  = ["c", "g"] 
我想根据
xor
列表对
vals
列表进行排序, i、 例如,
xor
中的值应按精确顺序放在
vals
列表的第一位。
VAL
中的其余值应保持相同顺序

另外,
xor
中的值可能不在
vals
中,在这些情况下,只需忽略这些值即可。而且,在重复的情况下,我只需要一个值

期望输出:

vals = ["c", "g", "a", "d", "e", "f"]
# here a, d, e, f are not in xor so we keep them in same order as found in vals.
我的做法:

new_list = []
for x in vals:
    for y in xor:
        if x == y:
            new_list.append(x)
            
for x in vals:
    if x not in xor:
        new_list.append(x)

vals
列表目前约有800k个单词或短语。
xor
列表有300k个单词或短语,但以后可能会增加。有些短语也有点长。解决此问题的最有效方法是什么?

xor
中构建索引的顺序dict,并将其用作排序键:

order = {n: i for i, n in enumerate(xor)}

sorted(vals, key=lambda x: order.get(x, len(xor)))
# ['c', 'g', 'a', 'd', 'e', 'f']
使用
len(vals)
作为默认值,可确保所有不在
xor
中的值都位于后面。当然,这是假设您希望根据
xor
中的值的顺序对
xor
中的值进行排序(使流程
O(M+NlogN)
)。否则,您可以通过以下方式更快(
O(M+N)
):

或者以更具可读性的方式:

s = set(xor)
result = [v for v in vals if v in s]
result += (v for v in vals if v not in s)

这也应该是您的一线解决方案:

vals = [1, 2, 5, 4, 3, 2, 11, 6]
xor = [10, 11]
new_list = xor + [elem for elem in vals if elem not in xor]

(编辑:不带已排序和正确的变量)

附加
xor
中也存在于
vals
中的所有值,以及
xor
中不存在的
vals
的所有值的列表:

sorted_list = [v for v in xor if v in vals] + [v for v in vals if v not in xor]
另一种方法:

output = list(filter(lambda x: x in vals, xor)) + list(filter(lambda x: x not in xor, vals))

鉴于问题中添加的澄清,此处发布的一些答案实际上没有提供预期的答案,其他答案也没有效率

一种解决方案是采用@dreamcrash的答案,但使用集合进行查找:

def order_by1(vals, xor):
    set_vals = set(vals)
    set_xor = set(xor)
    return [v for v in xor if v in set_vals] + [v for v in vals if v not in set_xor]
这将通过在循环的每次迭代中删除耗时的()列表查找来提高效率

由于您不关心在
xor
中找不到的值的顺序,因此此操作的一个变体是用set操作替换第二个列表理解:

def order_by2(vals, xor):
    set_vals = set(vals)
    return [v for v in xor if v in set_vals] + list(set_vals - set(xor))
另一个解决方案是使用

有序的集合在引擎盖下使用字典。我们可以直接使用字典,利用以下事实:

另一个真正做到所需的解决方案是@schwobasegl答案中的第一个。使用800000个和300000个随机短语列表对这些短语进行计时,其中150000个重叠:

import random
import string
import timeit

def random_phrase():
    return "".join(random.choices(string.ascii_letters, k=10))

def generate_lists(M, N, overlap):
    a = [random_phrase() for _ in range(M)] 
    b = ([random_phrase() for _ in range(N - overlap)] +
          random.sample(a, k=overlap))
    random.shuffle(b)
    return a, b

def dreamcrash(vals, xor):
    return [v for v in xor if v in vals] + [v for v in vals if v not in xor]

def schwobaseggl(vals, xor):
    order = {n: i for i, n in enumerate(xor)}
    len_xor = len(xor)   # saves some time when sorting
    return sorted(vals, key=lambda x: order.get(x, len_xor))

vals, xor = generate_lists(800000, 300000, overlap=150000) # this takes a while
for f in [dreamcrash, order_by1, order_by2, order_by3, order_by4, schwobaseggl]:
    print(f, end='...')
    print(timeit.timeit(stmt=f"{f.__name__}(vals, xor)", number=1, globals=globals()))

我放弃了计时
dreamcrash
,因为时间太长了
order\u by2
似乎速度最快,其次是
order\u by4
order\u by1
,和
schwobasegl
,在我的电脑上每种时间大约为.5-1.5s。有序集解决方案要慢得多。检查集合是否包含项,以及在字典中设置项,这都解释了为什么字典和基于集合的版本具有相似的性能。

另一种排序方法:

vals.sort(key=set(xor).__contains__, reverse=True)

我怀疑它比其他的更快,但是我不想尝试创建可能与实际数据相似的测试数据。(部分原因是您的问题还不完全清楚。我将讨论您的参考实现的功能,即两个组在
VAL
中保持顺序)

此方法的时间复杂性是什么?我有一个巨大的列表。
O(M+NlogN)
具有
M
大小为
xor
N
大小为
vals
。我不想对“vals”中的值进行排序,在xor中找不到的vals中的其他值应该保持相同的顺序。不要使用像这样的dunder方法this@schwobaseggl然后是类似于
partial的东西(operator.contains,a))
会更好。我冒昧地编辑了一下,现在应该修好了。这是一个简单的列表生成问题。从他所说的来看,并不清楚他是否希望它们出现在最终的列表中。从技术上讲,您可以使用另一个列表生成器来实现这一点,而不是在开始时使用xor列表。第二种方法的时间复杂度是多少?不确定,但认为它与普通集合类似——因此O(aM+bN)(a和b取决于xor和VAL之间的重叠)平均为O(MN)。实际上,在python中实现有序集可能会减慢速度。有关可能更有效的算法,请参见编辑。再次编辑一些性能测试。
def order_by4(vals, xor):
    """ Order list a by each item's position in list xor, if it is found in xor,
        otherwise by its position in list vals """
    d = {k: False for k in xor}
    for k in vals:
        d[k] = True
    return [k for k, v in d.items() if v]
import random
import string
import timeit

def random_phrase():
    return "".join(random.choices(string.ascii_letters, k=10))

def generate_lists(M, N, overlap):
    a = [random_phrase() for _ in range(M)] 
    b = ([random_phrase() for _ in range(N - overlap)] +
          random.sample(a, k=overlap))
    random.shuffle(b)
    return a, b

def dreamcrash(vals, xor):
    return [v for v in xor if v in vals] + [v for v in vals if v not in xor]

def schwobaseggl(vals, xor):
    order = {n: i for i, n in enumerate(xor)}
    len_xor = len(xor)   # saves some time when sorting
    return sorted(vals, key=lambda x: order.get(x, len_xor))

vals, xor = generate_lists(800000, 300000, overlap=150000) # this takes a while
for f in [dreamcrash, order_by1, order_by2, order_by3, order_by4, schwobaseggl]:
    print(f, end='...')
    print(timeit.timeit(stmt=f"{f.__name__}(vals, xor)", number=1, globals=globals()))
vals.sort(key=set(xor).__contains__, reverse=True)