简体   繁体   English

MVC LINQ Union与比较IComparer和IEqualityComparer

[英]MVC LINQ Union with compare IComparer vs IEqualityComparer

I have an example class 我有一个例子班

public class Item 
{
    public int Id;
    public string Name;
    public int ItemParentId;
}

then I put many Items into the database, there they have an Id, Name and ItemParentId, but I also create a list of new Items, where they have Name, ItemParentId, but Id = 0; 然后我将许多Items放入数据库中,它们有一个Id,Name和ItemParentId,但是我还创建了一个新Items的列表,其中有Name,ItemParentId,但Id = 0;

I do select all items from database to list1. 我确实选择了从数据库到list1的所有项目。 I create new list2 with new Items. 我用新项目创建新的list2。

I want to make something like this: 我想做这样的事情:

list1.Union(list2); // need to combine only with different ItemParentId

but the problem is that I need to combine only those items, which ItemParentId are not equal. 但是问题是我只需要合并那些ItemParentId不相等的项目。 Linq Union only let to create IEqualityComparer, but this one is not suitable. Linq Union只允许创建IEqualityComparer,但此不合适。 Also I tried IComparer, but Union doesn't let use it. 我也尝试过IComparer,但是Union不允许使用它。 Any help would be appreciated. 任何帮助,将不胜感激。

Example of lists and what result I want: 清单示例以及想要的结果:

var list1 = { 
       Item { Id = 1, Name = "item1", ItemParentId = 100 },
       Item { Id = 2, Name = "item2", ItemParentId = 200 },
       Item { Id = 3, Name = "item3", ItemParentId = 300 },
       Item { Id = 4, Name = "item4", ItemParentId = 400 }
  } 

var list2 = new List<Item>{ 
       new Item { Id = 0, Name = "item5", ItemParentId = 500 },
       new Item { Id = 0, Name = "item6", ItemParentId = 300 },
       new Item { Id = 0, Name = "item7", ItemParentId = 400 },
  }

result list should contain 3 items, which names are "item1", "item2", "item3", "item4" and "item5" 结果列表应包含3个项目,名称分别为“ item1”,“ item2”,“ item3”,“ item4”和“ item5”

UPDATE: 更新:

thanks guys, with your help I managed to compare items by single property, but now I have to do that by two of them. 谢谢大家,在您的帮助下,我设法按单个属性比较了项目,但现在我必须按其中两个进行比较。 Actually my lass has now 10 properties, but I have to compare only by two, the comparer looks fine, only thing I want to know is what do the HashCode used for? 实际上我的夫人现在有10个属性,但是我只需要比较两个,比较器看起来就很好,我唯一想知道的是HashCode的作用是什么?

Seems is like IEqualityComparer is what you want after all: 似乎就像IEqualityComparer毕竟您想要的:

public class Comparer : IEqualityComparer<Item>
{
    public bool Equals(Item x, Item y)
    {
        return x.ItemParentId == y.ItemParentId;
    }

    public int GetHashCode(Item obj)
    { 
        return obj.ItemParentId;
    }
}

Calling code: 调用代码:

var result = list1.Union(list2, new Comparer())

Update: If you want to compare multiple properties, you can alter the comparer: 更新:如果要比较多个属性,可以更改比较器:

public class Comparer : IEqualityComparer<Item>
{
    public bool Equals(Item x, Item y)
    {
        return x.ItemParentId == y.ItemParentId 
            || x.Name == y.Name;
    }

    public int GetHashCode(Item obj)
    {
        unchecked 
        {
            int hash = 17;
            hash = hash * 23 + obj.ItemParentId.GetHashCode();
            hash = hash * 23 + obj.Name.GetHashCode();
            return hash;
        }
    }
}

To learn more about the GetHashCode implementation see this answer . 要了解有关GetHashCode实现的更多信息,请参见此答案

And to learn more about the GetHashCode in general see these answers . 并详细了解GetHashCode 一般看到这些 答案

You probably noticed that if you just return 1 or something from the GetHashCode method that your code still works. 您可能已经注意到,如果只return 1或从GetHashCode方法返回的值,则您的代码仍然有效。 If you do not implement GetHashCode Union will call Equals which will work but is slower than GetHashCode . 如果您未实现GetHashCode Union将调用Equals ,它将起作用,但比GetHashCode慢。 Implementing GetHashCode increases performance. 实现GetHashCode提高性能。

If it's linq-to-objects then you can indeed use an IEqualityComparer : 如果它是对象的对象,那么您确实可以使用IEqualityComparer

public class ByParentIdComparer : IEqualityComparer<Item>
{
  public bool Equals(Item x, Item y)
  {
    return x.ItemParentId == y.ItemParentId;
  }
  public int GetHashCode(Item obj)
  { 
    return obj.ItemParentId;
  }
}

Then: 然后:

list1.Union(list2, new ByParentIdComparer())

Will work. 将工作。

This though won't translate well into SQL. 尽管这不能很好地转换为SQL。 If you might be doing the unioning on a database then you're better off with: 如果您可能要在数据库上进行合并,那么最好使用:

list1.Concat(list2).GroupBy(item => item.ItemParentId).Select(grp => grp.First())

Which takes both lists (not yet filtering out duplicates), then groups them by the ItemParentId and then takes the first element from each group, and as such gives the equivalent results. 这将获取两个列表(尚未过滤出重复列表),然后将其按ItemParentId分组,然后从每个组中获取第一个元素,从而得到等效的结果。

This will also work in linq-to-objects, but the version using an equality comparer will be faster. 这也适用于linq-to-objects,但是使用相等比较器的版本会更快。

Maybe this helps: 也许这会有所帮助:

        var list1 = new List<Item>
        {
            new Item { Id = 1, Name = "item1", ItemParentId = 100 },
            new Item { Id = 2, Name = "item2", ItemParentId = 200 },
            new Item { Id = 3, Name = "item3", ItemParentId = 300 },
            new Item { Id = 1, Name = "item4", ItemParentId = 400 }
        };

        var list2 = new List<Item>
        {
            new Item { Id = 0, Name = "item5", ItemParentId = 500 },
            new Item { Id = 0, Name = "item6", ItemParentId = 300 },
            new Item { Id = 0, Name = "item7", ItemParentId = 400 },
        };

        var listMerge = list1.Union(list2.Where(l2 => !list1.Select(l1 => l1.ItemParentId).Contains(l2.ItemParentId))).ToList();

I would personally split that expression in two parts: 我个人将这种表达分为两个部分:

        var list2new = list2.Where(l2 => !list1.Select(l1 => l1.ItemParentId).Contains(l2.ItemParentId));
        var listMerge = list1.Union(list2new).ToList();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM