Linq Union订购 - 我如何确保第一个IEnumerable中的项目保持在第一位?

时间:2018-05-16 08:16:10

标签: c# linq

我想知道是否可以(或者,如果它已经完成)确保保留第一个IEnumerable中的项目 - 而来自另一个IEnumerable的并集的重复项将被丢弃。

例如:

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

namespace MyApp.ExampleStuff
{
public class SomeDto
{
    string name {get; set;}
    int classId {get; set;}
    int notComparedObject {get; set;}
}

public class test {

    public void DoSomething()
    {
        IEnumerable<SomeDto> firstDto = new List<SomeDto>() { new SomeDto() {name = "Dave", classId = 1, notComparedObject = 12}};
        IEnumerable<SomeDto> secondDto = new List<SomeDto>() { new SomeDto() {name = "Dave", classId = 1, notComparedObject = 16}, new SomeDto() {name = "Brad", classId = 1, notComparedObject = 77}};

        var result = GetUnionedLists(firstDto, secondDto);
    }

    public ILookup<SomeDto> GetUnionedLists (IEnumerable<SomeDto> dtoA, IEnumerable<SomeDto> dtoB)
    {
            return dtoA.Union(dtoB, new SomeDtoComparer()).ToLookUp(x => x.classId);
    }

}

public class SomeDtoComparer : IEqualityComparer<SomeDto>
    {
        public bool Equals(SomeDto SomeDtoA, SomeDto SomeDtoB)
        {
            if (SomeDtoA == null && SomeDtoB == null)
            {
                return true;
            } else if (SomeDtoA == null || SomeDtoB == null)
            {
                return false;
            }

            return (SomeDtoA.Name == SomeDtoB.Name && SomeDtoA.classId == SomeDtoB.classId);
        }

        public int GetHashCode(SomeDto SomeDtoX)
        {
            int hashName = SomeDtoX.Name == null ? 0 : SomeDtoX.Name.GetHashCode();
            int hashClassId  = SomeDtoX.classId == null ? 0 : SomeDtoX.classId.GetHashCode();

            return hashName ^ hashClassId;
        }
    }
}

如果运行 - 我希望DoSomething()中的结果值是一个Lookup,它只包含classId“1”下面的someDto:

SomeDto() {name = "Dave", classId = 1, notComparedObject = 12}
SomeDto() {name = "Brad", classId = 1, notComparedObject = 77}

正如你所看到的,如果“Name”和“classId”是相同的 - 结果被认为是Equal,然后我想保留原始IEnumerable中的项目,并丢弃“duplicate” - 在此案例是:

SomeDto() {name = "Dave", id = 1, notComparedObject = 16}

如果结果是这样的 - 它会被认为是错误的(因为第二个Enumerable中的项目放在结果的第一位):

SomeDto() {name = "Brad", classId = 1, notComparedObject = 77}
SomeDto() {name = "Dave", classId = 1, notComparedObject = 12}

2 个答案:

答案 0 :(得分:1)

我认为您可以使用MoreLinq库中提供的FullJoin函数(在NuGet上提供)来实现此目的。

https://morelinq.github.io/3.0/ref/api/html/M_MoreLinq_MoreEnumerable_FullJoin__3_1.htm

示例:

public ILookup<SomeDto> GetUnionedLists (IEnumerable<SomeDto> dtoA, IEnumerable<SomeDto> dtoB)
{
        return dtoA
            .FullJoin(dtoB, 
                 e => e, 
                 first => first,
                 second => second, 
                 (first, second) => first, 
                 new SomeDtoComparer())
            .ToLookUp(x => x.classId);
}

答案 1 :(得分:1)

Enumerable.Union方法已按照您所描述的顺序生成项目。它写在docs

  

当枚举此方法返回的对象时,Union 按顺序枚举第一个和第二个,并产生尚未产生的每个元素。

另一方面,Lookup类型以及IGrouping接口不对元素顺序提供任何保证(看起来ToLookup的当前实现保持原始顺序,但这可能更改)。所以,如果真的很重要,你应该添加一些额外的逻辑 - 比如使用自定义类型而不是Lookup,为它添加索引和排序的自定义属性,或者可能使用GroupBy,这确保了顺序正如docs中所述。

  

IGrouping对象的生成顺序基于生成每个IGrouping的第一个键的源中元素的顺序。分组中的元素按照生成它们的元素出现在源中的顺序产生。

相关问题