使用LINQ选择具有相同子集合的项目

4

以下是伪代码案例:

class parent{
   string name; //Some Property
   List<int> myValues;

   .......
}
........
//Initialize some parent classes

List<parent> parentList = new List<parent>();
parentList.add(parent123); //parent123.myValues == {1,2,3}
parentList.add(parent456); //parent456.myValues == {4,5,6}
parentList.add(parentMatch); //parentMatch.myValues == {1,2,3}

我想要的查询是检索一组父对象,其中它们的“myValues”列表是相同的。在这种情况下,它将返回parent123和parentMatch。

@forcripesake:你能澄清一下“其中myValues列表包含相同整数”是什么意思吗? - Ani
这是否意味着您想找到所有“父级”,其“myValues”彼此匹配?即List<int>相同? - p.campbell
顺便说一句,如果你想让查询在List<parent>上工作而不需要诉诸反射,你将不得不通过公共属性公开这些字段(或使字段本身公开,但这是一个坏主意)。 - Ani
抱歉之前问题表述不清,已经修改以便更加明确。 - ForCripeSake
@Ani,类内部的省略号表示适当的访问器和方法。再次抱歉,为了更清晰表达已经重新措辞。 - ForCripeSake
3个回答

3

如果您实现了IEqualityComparer,则可以将逻辑封装起来并仅使用GroupBy

class IntegerListComparer : IEqualityComparer<List<int>>
{
    #region IEqualityComparer<List<int>> Members

    public bool Equals(List<int> x, List<int> y)
    {
        //bool xContainsY = y.All(i => x.Contains(i));
        //bool yContainsX = x.All(i => y.Contains(i));
        //return xContainsY && yContainsX;
        return x.SequenceEqual(y);
    }

    public int GetHashCode(List<int> obj)
    {
        return 0;
    }

    #endregion
}

这样调用:

var results = list
    .GroupBy(p => p.MyValues, new IntegerListComparer())
    .Where(g => g.Count() > 1)
    .SelectMany(g => g);

+1 我刚刚在我的答案中写了这个... 这可能是最好/正确的解决方案。 ;) - digEmAll
我打算尝试一下,如何返回List<parent>而不是匿名类型? - ForCripeSake
@Zachary:也许你可以通过在GetHashCode中返回obj.Count,或者例如返回前2-3个元素的总和来稍微提高速度。另外,你错过了筛选单次出现的父级别的where子句。 - digEmAll
@forcripesake:首先,您应该添加.Where(x => x.Count() > 1)来过滤具有唯一列表的父项,然后添加.SelectMany(x => x).ToList()以获取所有重复列表的父项列表。 - digEmAll
{3,2,1} == {1,2,3} == {3,3,3,2,2,1} ? {3,2,1} == {1,2,3} == {3,3,3,2,2,1} 吗? - Nappy
显示剩余3条评论

1

非常愚蠢的解决方案:

var groups = list.GroupBy(p => string.Join(",", p.list.Select(i => i.ToString()).ToArray()))
                    .Where(x => x.Count() > 1).ToList();

结果:

一个包含具有相同 int 列表(按相同顺序)的父对象的组的 IEnumerable

如果您需要匹配任意顺序的元素列表(即 1、2、3 == 3、1、2),只需将 p.list 更改为 p.list.OrderBy(x => x)

此外,如果您的目标框架是 4.0,可以避免使用 ToArrayToString


编辑:

添加了一个where语句来过滤单次出现的组。

现在,如果您有这些父级:

parent  A  1,2,3
parent  B  1,2,3
parent  C  1,2,3
parent  D  4,5,6
parent  E  4,5,6
parent  F  7,8,9

它返回:

(A,B,C) - (D,E)

这将返回整数的扁平化数组,然后需要一些工作来找到第一个对象,其列表与此扁平化值匹配。还会返回仅具有1个条目而不是多个条目的对象。 - p.campbell
它运行良好,我已经测试过了。它返回具有相同元素的父组。 - digEmAll
+1 @digEmAll:我没时间修改我的解决方案以满足提问者的要求;这需要更多的工作来使其更高效。这个解决方案简单,尽管依赖于字符串比较,但它能正常工作,这才是最重要的。 - Ani
@Ani:是的,我刚刚在修改我的解决方案,使用IEqualityComparer避免字符串转换/比较,但是Zachary比我更先想到了 :D - digEmAll

0

试试这个:

var matches = (from p1 in parentList
               from p2 in parentList
               let c1 = p1.myValues
               let c2 = p2.myValues
               where p1 != p2 &&
                     c1.All(child => c2.Contains(child)) &&
                     c2.All(child => c1.Contains(child))
               select p1).Distinct();

忘了提一下...这个忽略了重复和顺序。 - Joshua Rodgers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接