从B中删除A中的元素

时间:2012-10-13 02:34:58

标签: python python-2.7

我有两个列表,让我们说:

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'。

3 个答案:

答案 0 :(得分: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中的顺序(如果您关心这一点)。

答案 1 :(得分: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])

答案 2 :(得分:1)

一般来说,你总是要避免使用list.remove()(你是对的,它会严重损害性能)。此外,在字典或集合中查找元素比在列表中查找要快得多(O(1));所以从list1创建一个集合(如果顺序无关紧要,则从列表2中创建)。

这样的事情:

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.

但是我不知道你选择拆除元素的实际算法是什么。请详细说明“但不是所有事件”。