Linq的互斥小组

时间:2012-10-24 19:58:52

标签: c# linq grouping

我想要相互独立地捕获重复项,也就是说,我需要显示第一项和第三项都是重复项,第一项和第四项是重复项。

public class Foo
{
    public String Name { get; set; }
    public String SName { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var list = new List<Foo>();
        list.Add(new Foo { Name = "a", SName = "d" });
        list.Add(new Foo { Name = "b", SName = "e" });
        list.Add(new Foo { Name = "c", SName = "a" });
        list.Add(new Foo { Name = "a", SName = "f" });

        // only groups by 1 name
        var duplicates = list.GroupBy(i => i.Name).Where(g => g.Count() > 1).Select(g => g.Key);
    }
}

我知道这可以用foreach完成,我想学习。

4 个答案:

答案 0 :(得分:1)

因此,我们首先从任一列获取所有重复的名称值,这非常简单:

IEnumerable<string> repeatedNames = list.SelectMany(foo => new[] { foo.Name, foo.SName })
    .GroupBy(name => name)
    .Where (g => g.Count () > 1)
    .Select(g => g.Key);

接下来,我们将获取每个名称并查找包含该值的所有项目。最终结果是每个不同名称的序列,其中序列是包含该值的所有Foo项。

IEnumerable<List<Foo>> groupings = repeatedNames .Select(name =>
    list.Where(foo => foo.Name == name || foo.SName == name).ToList());

如果你想要它,而不是一系列Foos列表,一个具有不同值和序列的项目序列,那么它很容易添加:

var groupings = repeatedNames .Select(name => new
    {
        Name = name,
        Foos = list.Where(foo => foo.Name == name || foo.SName == name).ToList()
    });

答案 1 :(得分:0)

如果您只想要重复的值而不是它们出现的索引,那么您可以在进行分组之前使用SelectMany将名称转换为单个字符串列表:

var duplicates2 = list.SelectMany(n => new string[] {n.Name, n.SName})
                      .GroupBy(g => g)
                      .Where (g => g.Count () > 1)
                      .Select(g => g.Key)
                      .ToList();

修改 如果你想要Foo个对象而不仅仅是字符串,你可以像这样选择它们:

var duplicates3 = list.Where(n => duplicates2.Contains(n.Name) ||
                                  duplicates2.Contains(n.SName));

答案 2 :(得分:0)

我想出了这个:

// Will return "a" - one which you already had
var duplicatesBetweenNames = list.GroupBy(i => i.Name)
    .Where(g => g.Count() > 1)
    .Select(g => g.Key).ToArray();

var duplicatedInSName = list.Select(x => x.Name)
    .Intersect(list.Select(x => x.SName));
// Will return "c" - represents Names where in SName is duplicate
var duplicatesBetweenNameAndSName = list
    .Where(f => duplicatedInSName.Contains(f.SName))
    .Select(x=>x.Name).ToArray();

答案 3 :(得分:0)

有趣的线程,我只是认为在这种情况下查询语法非常具有表现力:

var r = from l in list
        from s in new [] {l.Name,l.SName}
        group s by s into g
        where g.Count() > 1
        select g.Key into u
        from l in list
        where u.Contains(l.Name) || u.Contains(l.SName)
        select l;