IT story

익명 유형을 가진 LINQ Select Distinct

hot-time 2020. 6. 15. 08:11
반응형

익명 유형을 가진 LINQ Select Distinct


그래서 객체 모음이 있습니다. 정확한 유형은 중요하지 않습니다. 그것으로부터 특정 속성 쌍의 모든 고유 쌍을 추출하고 싶습니다.

myObjectCollection.Select(item=>new
                                {
                                     Alpha = item.propOne,
                                     Bravo = item.propTwo
                                }
                 ).Distinct();

그래서 내 질문은 :이 경우 기본 객체 같음 (각 객체가 새 것이므로 나에게 쓸모가 없음)을 사용하거나 다른 같음 (이 경우 알파와 브라보의 동일한 값)을 수행하도록 지시받을 수 있습니다 => 동일한 인스턴스)? 그렇지 않은 경우 해당 결과를 달성 할 수있는 방법이 있습니까?


K. Scott Allen의 훌륭한 게시물을 읽으십시오.

모든 사람의 평등 ... 익명 유형

짧은 대답 (및 인용) :

익명 유형에 대해 C # 컴파일러가 Equals 및 GetHashCode를 대체합니다. 재정의 된 두 메서드의 구현은 형식의 모든 공용 속성을 사용하여 개체의 해시 코드를 계산하고 동등성을 테스트합니다. 익명 유형이 동일한 두 개체의 속성 값이 모두 같은 경우 개체가 동일합니다.

따라서 익명 형식을 반환하는 쿼리에서 Distinct () 메서드를 사용하는 것이 안전합니다.


public class DelegateComparer<T> : IEqualityComparer<T>
{
    private Func<T, T, bool> _equals;
    private Func<T, int> _hashCode;
    public DelegateComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
    {
        _equals= equals;
        _hashCode = hashCode;
    }
    public bool Equals(T x, T y)
    {
        return _equals(x, y);
    }

    public int GetHashCode(T obj)
    {
        if(_hashCode!=null)
            return _hashCode(obj);
        return obj.GetHashCode();
    }       
}

public static class Extensions
{
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, 
        Func<T, T, bool> equals, Func<T,int> hashCode)
    {
        return items.Distinct(new DelegateComparer<T>(equals, hashCode));    
    }
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items,
        Func<T, T, bool> equals)
    {
        return items.Distinct(new DelegateComparer<T>(equals,null));
    }
}

var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName})
            .Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList();

이전에 엉망인 형식으로 죄송합니다.


C #에서는 작동하지만 VB에서는 작동하지 않는다는 흥미로운

26자를 반환합니다 :

var MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";
MyBet.ToCharArray()
.Select(x => new {lower = x.ToString().ToLower(), upper = x.ToString().ToUpper()})
.Distinct()
.Dump();

52를 반환합니다 ...

Dim MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ"
MyBet.ToCharArray() _
.Select(Function(x) New With {.lower = x.ToString.ToLower(), .upper = x.ToString.ToUpper()}) _
.Distinct() _
.Dump()

약간의 테스트를 실행하여 속성이 값 유형 인 경우 제대로 작동하는 것으로 나타났습니다. 이들이 값 유형이 아닌 경우, 유형이 작동하려면 자체 Equals 및 GetHashCode 구현을 제공해야합니다. 문자열은 효과가 있다고 생각합니다.


You can create your own Distinct Extension method which takes lambda expression. Here's an example

Create a class which derives from IEqualityComparer interface

public class DelegateComparer<T> : IEqualityComparer<T>
{
    private Func<T, T, bool> _equals;
    private Func<T, int> _hashCode;
    public DelegateComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
    {
        _equals= equals;
        _hashCode = hashCode;
    }
    public bool Equals(T x, T y)
    {
        return _equals(x, y);
    }

    public int GetHashCode(T obj)
    {
        if(_hashCode!=null)
            return _hashCode(obj);
        return obj.GetHashCode();
    }       
}

Then create your Distinct Extension method

public static class Extensions
{
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, 
        Func<T, T, bool> equals, Func<T,int> hashCode)
    {
        return items.Distinct(new DelegateComparer<T>(equals, hashCode));    
    }
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items,
        Func<T, T, bool> equals)
    {
        return items.Distinct(new DelegateComparer<T>(equals,null));
    }
}

and you can use this method find distinct items

var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName})
            .Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList();

If Alpha and Bravo both inherit from a common class, you will be able to dictate the equality check in the parent class by implementing IEquatable<T>.

For example:

public class CommonClass : IEquatable<CommonClass>
{
    // needed for Distinct()
    public override int GetHashCode() 
    {
        return base.GetHashCode();
    }

    public bool Equals(CommonClass other)
    {
        if (other == null) return false;
        return [equality test];
    }
}

Hey there i got the same problem and i found an solution. You have to implement the IEquatable interface or simply override the (Equals & GetHashCode) Methods. But this is not the trick, the trick coming in the GetHashCode Method. You should not return the hash code of the object of your class but you should return the hash of the property you want to compare like that.

public override bool Equals(object obj)
    {
        Person p = obj as Person;
        if ( obj == null )
            return false;
        if ( object.ReferenceEquals( p , this ) )
            return true;
        if ( p.Age == this.Age && p.Name == this.Name && p.IsEgyptian == this.IsEgyptian )
            return true;
        return false;
        //return base.Equals( obj );
    }
    public override int GetHashCode()
    {
        return Name.GetHashCode();
    }

As you see i got an class called person got 3 properties (Name,Age,IsEgyptian"Because I am") In the GetHashCode i returned the hash of the Name property not the Person object.

Try it and it will work ISA. Thank you, Modather Sadik


In order for it to work in VB.NET, you need to specify the Key keyword before every property in the anonymous type, just like this:

myObjectCollection.Select(Function(item) New With
{
    Key .Alpha = item.propOne,
    Key .Bravo = item.propTwo
}).Distinct()

I was struggling with this, I thought VB.NET didn't support this type of feature, but actually it does.

참고URL : https://stackoverflow.com/questions/543482/linq-select-distinct-with-anonymous-types

반응형