如何使用set减法保留重复项?

时间:2018-05-31 13:17:46

标签: list groovy difference

通过groovy方式,代码如:

def l1 = [] as List;
def l2 = [] as List;

for(int i = 0; i < 5; i++) {
    l1 << i;
    l2 << i;
    l1 << i;
}

println(l1);
println(l2);

def l = l1-l2;
println(l);

l是空的,但我希望它是[0,1,2,3,4],只需减去一次项目(如果只有l2中的项目)。

2 个答案:

答案 0 :(得分:1)

它以这种方式工作,因为List.minus(Object el)搜索匹配el对象的所有元素,并将它们从输入列表中删除。或者,您可以使用List.removeElement(Object el)搜索第一个元素并将其从列表中删除。此方法可以与Groovy的inject方法结合使用:

def l1 = [] as List
def l2 = [] as List

for(int i = 0; i < 5; i++) {
    l1 << i
    l2 << i
    l1 << i
}

def l3 = l2.inject(l1) { List list, el ->
    list.removeElement(el)
    return list
}

println "l1 is ${l1}"
println "l2 is ${l2}"
println "l3 is ${l3}"

输出:

l1 is [0, 0, 1, 1, 2, 2, 3, 3, 4, 4]
l2 is [0, 1, 2, 3, 4]
l3 is [0, 1, 2, 3, 4]

在此示例中,我们使用l2列表来迭代其元素,并将l1作为初始值传递给inject方法。然后对于l2列表中的每个元素,我们删除传递给inject方法的列表,然后返回它,所以在下一个迭代步骤中,这个列表在传递给{{1的闭包中被视为list变量}}。 inject是一种安全的方法,如果我们尝试删除列表中不存在的元素,则不会抛出异常。

但是,我向您展示的代码有一个明显的缺点 - 它将List.removeElement(Object el)传递给inject方法,并且该列表会被它修改。这就是为什么当您最后打印l1时,您会看到它与我们刚刚使用inject方法创建的l1相等。这个问题的解决方案非常简单 - 您可以创建此列表的副本,而不是将参考传递给l3。使用l1和现在new ArrayList<>(l1)列表不会被inject方法更新。

l1

希望它有所帮助。

答案 1 :(得分:1)

问题是删除是在元素相等的基础上完成的。

作为替代方案,您可以使用索引搜索较大的列表,并构建一个diff列表,其中包含l1中除第一个匹配索引之外的所有元素:

//find the first index of each l2 element in l1:
def match = l2.collect{l1.indexOf(it)}

//find indices of all elements that were not found in l1
def indices = (0..(-1+l1.size())).grep{!match.contains(it)}

//make a list of all elements that were not selected
//and that's basically the result of YOUR l1-l2
def diff = indices.collect{l1[it]}

println(diff)

输出:

[0, 1, 2, 3, 4]

请注意,这不是一般方法。它完全基于您上面的示例,并假设l1l2的超集,但它提供了一个想法。