LINQ相交,一个对象的多个列表,一些空

时间:2016-06-21 07:26:11

标签: c# linq

我试图找到与LINQ的交叉。

我有这样的对象:

 public class objA
 {
     public int foo1{ get; set; }
     public string foo2 { get; set; }
     public bool foo3 { get; set; }
 }

样品:

List<objA> name1= new List<int>() {{1,"bb",true} , {2,"cc",false}};
List<objA> name2= new List<int>();
List<objA> name3= new List<int>() { {1,"bb",true} };
List<objA> name4= new List<int>() { {1,"bb",true} , {2,"cc",false} };
List<objA> name5= new List<int>() { {1,"bb",true} };

想要返回:{1,&#34; bb&#34;,true},因为它存在于所有列表中..如果我运行:

List<objA>= name1
            .Intersect(name2)
            .Intersect(name3)
            .Intersect(name4)
            .Intersect(name5).ToList();

当列表2为空时,它不返回任何内容。

我知道这条消息: LINQ intersect, multiple lists, some empty

但我不知道如何处理对象。

5 个答案:

答案 0 :(得分:2)

首先,让我们定义一个执行交集的扩展方法,但忽略任何空的列表。

public static class Ex
{
    public static IEnumerable<T> IntersectIgnoreEmpty<T>(this IEnumerable<T> @this, IEnumerable<T> other, IEqualityComparer<T> comparer)
    {
        return other.Any() ? @this.Intersect(other, comparer) : @this;
    }
}

现在我们为objA定义一个相等比较器:

public class objAEqualityComparer : IEqualityComparer<objA>
{
    public bool Equals(objA left, objA right)
    {
        return
            left.foo1 == right.foo1
            && left.foo2 == right.foo2
            && left.foo3 == right.foo3;
    }

    public int GetHashCode(objA @this)
    {
        return
            @this.foo1.GetHashCode()
            ^ @this.foo2.GetHashCode()
            ^ @this.foo3.GetHashCode();
    }
}

然后我们可以运行这段代码:

List<objA> name1 = new List<objA>()
{
    new objA { foo1 = 1, foo2 = "bb", foo3 = true },
    new objA { foo1 = 2, foo2 = "cc", foo3 = false },
};
List<objA> name2 = new List<objA>();
List<objA> name3 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true } };
List<objA> name4 = new List<objA>()
{
    new objA { foo1 = 1, foo2 = "bb", foo3 = true },
    new objA { foo1 = 2, foo2 = "cc", foo3 = false },
};
List<objA> name5 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true } };

objAEqualityComparer comparer = new objAEqualityComparer();

List<objA> result =
    name1
        .IntersectIgnoreEmpty(name2, comparer)
        .IntersectIgnoreEmpty(name3, comparer)
        .IntersectIgnoreEmpty(name4, comparer)
        .IntersectIgnoreEmpty(name5, comparer)
        .ToList();

我们得到的结果是:

result

您可以始终覆盖Equals中的GetHashCodeobjA,但这需要将objA设为只读,以便不破坏平等合同 - 制作{在这种情况下,{1}}是更好的选择,因为它允许{/ 1}}进行读/写。

答案 1 :(得分:0)

首先,name2为空。 &#34;相交&#34;总是什么也没有。所以删除name2。

List<objA> output = name1
    .Intersect(name3)
    .Intersect(name4)
    .Intersect(name5).ToList();

其次,删除&#34; .Intersect(name2)&#34;仍然什么都没有。

因为你必须同时实施&#34; Equal&#34;和&#34; GetHashCode&#34;自定义类中的方法。

public class objA
{
    public int foo1{ get; set; }
    public string foo2 { get; set; }
    public bool foo3 { get; set; }

    public override bool Equals(object obj)
    {
        if (obj is objA == false) return false;

        var a = (objA)obj;
        return a.foo1 == foo1
            && a.foo2 == foo2
            && a.foo3 == foo3;
    }
    public override int GetHashCode()
    {
        return foo1.GetHashCode()
            ^ foo2.GetHashCode()
            ^ foo3.GetHashCode();
    }
}

这是因为&#34;相交&#34;使用&#34;等于&#34;比较对象。 它使用&#34; GetHashCode&#34;加快速度。

答案 2 :(得分:0)

如果您想要准确理解为什么您的代码不起作用,那么Tommy的答案是完美的 - 删除&#34; .Intersect(name2)&#34; line(因为&#34; name2&#34;没有任何项目,因此与它相交将始终返回零结果)然后在你的&#34; objA&#34;上实现GetHashCode和Equals方法。类。

但是,如果您只是想以最简单的方式使代码工作,您可以将objA从类更改为结构,因为结构会自动接收GetHashCode和Equals实现,它们可以执行您想要的操作。

我修复了原始代码,以便编译和更改&#34; objA&#34;进入一个结构,现在是最后的&#34; x&#34; value将是一个具有单个值的列表(foo1 = 1,foo2 =&#34; bb&#34;,foo3 = true)。

using System.Collections.Generic;
using System.Linq;

namespace StackOverflowAnswer
{
    public struct objA
    {
        public int foo1 { get; set; }
        public string foo2 { get; set; }
        public bool foo3 { get; set; }
    }

    class Program
    {
        static void Main()
        {
            List<objA> name1 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true }, new objA { foo1 = 2, foo2 = "cc", foo3 = false } };
            List<objA> name2 = new List<objA>();
            List<objA> name3 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true } };
            List<objA> name4 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true }, new objA { foo1 = 2, foo2 = "cc", foo3 = false } };
            List<objA> name5 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true } };

            List<objA> x = name1
                //.Intersect(name2)
                .Intersect(name3)
                .Intersect(name4)
                .Intersect(name5)
                .ToList();
        }
    }
}

在阅读了关于这个问题的其他答案的评论之后,也许我错过了部分要点 - 是否希望在执行&#34;交叉&#34;时有效地忽略空集?

如果是这样,那么你可以通过编写自己版本的&#34; Intersect&#34;来忽略空集 - 例如。

using System.Collections.Generic;
using System.Linq;

namespace StackOverflowAnswer
{
    public struct objA
    {
        public int foo1 { get; set; }
        public string foo2 { get; set; }
        public bool foo3 { get; set; }
    }

    class Program
    {
        static void Main()
        {
            List<objA> name1 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true }, new objA { foo1 = 2, foo2 = "cc", foo3 = false } };
            List<objA> name2 = new List<objA>();
            List<objA> name3 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true } };
            List<objA> name4 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true }, new objA { foo1 = 2, foo2 = "cc", foo3 = false } };
            List<objA> name5 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true } };

            List<objA> x = name1
                .IntersectUnlessEmpty(name2)
                .IntersectUnlessEmpty(name3)
                .IntersectUnlessEmpty(name4)
                .IntersectUnlessEmpty(name5)
                .ToList();
        }
    }

    public static class IEnumerableExtensions
    {
        public static IEnumerable<T> IntersectUnlessEmpty<T>(this IEnumerable<T> source, IEnumerable<T> second)
        {
            return second.Any() ? source.Intersect(second) : source;
        }
    }
}

决赛&#34; x&#34;在这种情况下,值仍然是一个包含单个项目的列表 - 空的&#34; name2&#34;数据并不能阻止非空列表相互交叉。

答案 3 :(得分:-1)

以同样的方式:

public IEnumerable<T> IntersectsNonEmpty<T>(
                          params IEnumerable<T>[] input)
{
   var nonEmpty = input.Where(i => i.Any()).ToList();
   if (!nonEmpty.Any()) return false; //or whatever
   return nonEmpty.Aggregate((l1, l2) => l1.Intersect(l2));
}

用法:

List<objA> result =  IntersectsNonEmpty(name1, name2, name3, name4 .. and go on)

请注意,objA应该覆盖Equals,以便考虑具有相同字段值的不同实例:

public class objA
{
    public override bool Equals(object other)
    {
          // impl
    }
}

答案 4 :(得分:-1)

结果为空的原因是name2列表为空。如果在name2中添加{1,&#34; bb&#34;,true},则结果不会为空。