如何在linq中的动态列表中查找重复项?

时间:2019-02-28 06:31:04

标签: c# linq

我在C#中有一个List<List<string>>-父级列表中的项数会有所不同-可能是1或可能是5。在考虑所有值时,我需要知道是否有重复项在所有列表中处于相同位置。

这类似于对复合键的数据库唯一约束,在复合键中不能重复。每个列表都包含表中数据的所有值。

例如,如果我具有以下结构(但每个结构只能有1列或更多列):

Product    Color    Size
Tshirt     Blue     S
Tshirt     Blue     M
Tshirt     Blue     L
Tshirt     Blue     S <-- this is a duplicate
Tshirt     Red      S

应该是

var items = new List<List<string>>()
{
    new List<string>() { "Tshirt", "Tshirt", "Tshirt", "Tshirt", "Tshirt", },
    new List<string>() { "Blue", "Blue", "Blue", "Blue", "Red", },
    new List<string>() { "S", "M", "L", "S", "S", },
};

我需要检测是否存在重复项,并将重复项打印为

Duplicate: Tshirt, Blue, S

注意:按引用的“重复项”中的说明在单个列表中查找重复项很容易,如果列表是静态的,则可以查找重复项,但这是不同的,因为大小完全未知。它实际上可能是一个List<List<string>>,具有0个元素,1个或更多元素。

2 个答案:

答案 0 :(得分:4)

尝试一下:

var items = new List<List<string>>()
{
    new List<string>() { "Tshirt", "Tshirt", "Tshirt", "Tshirt", "Tshirt", },
    new List<string>() { "Blue", "Blue", "Blue", "Blue", "Red", },
    new List<string>() { "S", "M", "L", "S", "S", },
};

var duplicates =
    Enumerable
        .Range(0, items.First().Count)
        .Select(x => new { Product = items[0][x], Color = items[1][x], Size = items[2][x], })
        .GroupBy(x => x)
        .SelectMany(x => x.Skip(1).Take(1))
        .ToArray();

给出:

duplicates


鉴于需要处理可变数量的内部列表,以下是处理方法:

var duplicates =
    Enumerable
        .Range(0, items.First().Count)
        .Select(x => Enumerable.Range(0, items.Count).Select(y => items[y][x]).ToArray())
        .GroupBy(x => String.Join("|", x))
        .SelectMany(x => x.Skip(1).Take(1))
        .ToArray();

给出:

duplicates2


这是不使用Count的惰性版本:

var duplicates =
    items
        .Select(xs => xs.Select(y => Enumerable.Repeat(y, 1)))
        .Aggregate((z0s, z1s) => z0s.Zip(z1s, (z0, z1) => z0.Concat(z1)))
        .GroupBy(ws => String.Join("|", ws))
        .SelectMany(gws => gws.Skip(1).Take(1));

答案 1 :(得分:1)

您可以使用Zip()Aggregate() LINQ方法在List<List<string>>(甚至List<List<object>>)中查找重复项:

string separator = ";%;*;%;";   // Pick a string that's very unlikely to appear in results

var duplicates = items.Aggregate((currentList, nextList) =>
                            currentList.Zip(nextList, (currentListItem, nextListItem) =>
                                $"{currentListItem}{separator}{nextListItem}").ToList())
                      .GroupBy(item => item).Where(group => group.Count() > 1)
                      .Select(item => item.Key.Split(new[] { separator }, StringSplitOptions.None)
                            .ToList())
                      .ToList();

Aggregate()方法将有效地遍历外部列表,一次考虑2个内部列表。然后可以将这些列表逐项Zip合并在一起,以产生新的IEnumerable<string>;必须进行ToList()调用,因为这个新的IEnumerable<string>成为Aggregate()方法的下一个输入,并且必须与下一个List<string>

的格式相同

将所有内部List<string>压缩到新的IEnumerable<string>中后,其中的项目将与分隔符连接在一起(对于使用分隔符非常重要,以避免对重复匹配产生误报,例如{ {1}}),您只需"aa" + "abb" == "aaa" + "bb"个项目,然后找到包含1个以上项目的任何组。

最后,最后一个GroupBy()将结果转换回Select()格式,以便与原始数据轻松比较。

此解决方案完全是LINQ(您甚至可以将List<List<string>>直接硬编码到查询中),并且适用于任意数量的内部列表(仅包括1个列表)。