0

我有两个列表,比方说:

a = [1,2,3]
b = [1,2,3,1,2,3]

我想从列表 b 中删除 1、2 和 3,但不是所有出现的情况。结果列表应具有:

b = [1,2,3]

我目前有:

for element in a:
    try:
        b.remove(element)
    except ValueError:
        pass

但是,当 a 和 b 变得非常大时,它的性能很差。有没有更有效的方法来获得相同的结果?

编辑

为了澄清“并非所有事件”,我的意思是我不希望从 b 中删除两个“1”,因为 a 中只有一个“1”。

4

3 回答 3

2

我会这样做:

set_a = set(a)
new_b = []
for x in b:
  if x in set_a:
    set_a.remove(x)
  else:
    new_b.append(x)

与其他设置解决方案不同,这可以保持顺序b(如果您关心的话)。

于 2012-10-13T02:54:11.803 回答
1

我会做这样的事情:

from collections import defaultdict

a = [1, 2, 3]
b = [1, 2, 3, 1, 2, 3]

# Build up the count of occurrences in b
d = defaultdict(int)
for bb in b:
    d[bb] += 1

# Remove one for each occurrence in a
for aa in a:
    d[aa] -= 1

# Create a list for all elements that still have a count of one or more
result = []
for k, v in d.iteritems():
    if v > 0:
        result += [k] * v

或者,如果您愿意稍微模糊一点:

from operator import iadd

result = reduce(iadd, [[k] * v for k, v in d.iteritems() if v > 0], [])

defaultdict 生成每个键的出现次数。一旦它从 建立起来b,它会随着每次出现的键而递减a。然后我们打印出仍然剩余的元素,允许它们多次出现。

defaultdict 适用于 python 2.6 及更高版本。如果您使用的是更高版本的 python(我相信 2.7 及更高版本),您可以查看collections.Counter.


稍后:您还可以对此进行概括并创建反式默认字典的减法:

from collections import defaultdict
from operator import iadd

a = [1, 2, 3, 4, 5, 6]
b = [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]

def build_dd(lst):
    d = defaultdict(int)
    for item in lst:
        d[item] += 1
    return d

def subtract_dd(left, right):
    return {k: left[k] - v for k, v in right.iteritems()}

db = build_dd(b)
da = build_dd(a)
result = reduce(iadd,
                [[k] * v for k, v in subtract_dd(db, da).iteritems() if v > 0],
                [])

print result

但是reduce现在的表达方式很模糊。


稍后:在 python 2.7 及更高版本中,使用collections.Counter,它看起来像这样:

from collections import Counter

base = [1, 2, 3]
missing = [4, 5, 6]
extra = [7, 8, 9]
a = base + missing
b = base * 4 + extra

result = Counter(b) - Counter(a)
print result
assert result == dict([(k, 3) for k in base] + [(k, 1) for k in extra])
于 2012-10-13T02:45:07.413 回答
1

通常,您希望始终避免使用 list.remove() (您是对的,这会严重损害性能)。此外,在字典或集合中查找元素比在列表中查找元素要快得多(O(1));所以从你的list1中创建一个集合(如果顺序无关紧要,从你的list2中)。

像这样的东西:

sa = set(a)
new_b = [x for x in b if not x in sa]
# here you created a 3d list but I bet it's OK.

但是,我不知道您选择要删除的元素的实际算法是什么。请详细说明“但不是所有情况”。

于 2012-10-13T02:51:02.973 回答