GroupBy ()에 List <string> 필드를 통합하는 방법

Aug 19 2020

Diff 객체 목록이 있습니다. Diff는 다음과 같습니다.

public class Diff
{
    ChangeAction Action // ChangeAction is an Enum
    string Type
    DateTime StartDateTime
    DateTime EndDateTime
    int rateId
    string rateDescription
    List<string> properties
    DayOfWeek day
    List<DaysOfWeek> DaysOfWeek
    DayOfWeek DayOfWeek

}

내 LINQ 쿼리가 내가 생각하는대로 수행하지 않습니다. 목록 인 diff.properties에서 전달 중이며 GroupBy()목록의 모든 문자열 값이 일치 할 때 그룹화하고 싶습니다.

var results = diffs
    .GroupBy(diff => new { diff.properties, diff.Action, diff.Type, diff.StartDateTime, 
                           diff.EndDateTime, diff.rateId, diff.rateDescription}) 
    .Select(group => new Diff(
        group.Key.Action,
        group.Key.ScheduleType,
        group.Key.StartDateTime,
        group.Key.EndDateTime,
        group.Key.rateId,
        group.Key.rateDescription,
        group.Key.properties,
        group
            .Select(ts => ts.DayOfWeek)
            .Distinct()
            .OrderBy(dow => dow)
            .ToList()))
    .ToList();

유일한 차이점 resultsdiffs단수이다 DayOfWeek이전에 저장된 diffs이제 복수에 투입 DaysOfWeek필드 (그러나 목록에있는 단지 1 항목). 현재 resultsdiffs.

결과 목록에서보고 싶은 것은 다음과 같습니다.

  1. 모든 그룹 (diff.properties 목록 값 포함)에 대한 일치를 기반으로 통합되는 더 짧은 목록
  2. 이것은 또한 DaysOfWeek 목록에서 둘 이상의 항목을 의미합니다.

내 질문은 :

내가보고 싶은 내용을보기 위해 위의 LINQ 쿼리를 어떻게 변경할 수 results있습니까?

답변

4 TravisJ Aug 19 2020 at 06:02

익명 유형과 함께 사용 List<string>하는 그룹에는 1-1 그룹화되지 않은 집합을 가져 오는이 포함되어 있습니다 .

당신은

  • 그룹화 및 과부하에 대한 사용자 정의 클래스를 사용 GetHashCode하고 Equals이 문제와 같이 참조 그룹에 LINQ GROUPBY를 사용하여 값 객체 대신 객체

-또는-

  • diff.properties, diff.Action, diff.Type, diff.startdatetime, diff.enddatetime, diff.rateId, diff.rateDescription그룹화 할 고유 키 역할을 할 값 또는 그 하위 집합 ( ) 에서 문자열을 작성합니다.
2 RufusL Aug 19 2020 at 07:16

일부 GroupBy속성은 참조 유형이며 이러한 유형의 기본 비교자는 참조 비교이므로 이들 중 어느 것도 일치하지 않습니다. 이를 극복하기 위해 우리는 우리 자신의 방식으로 비교할 수 있도록 클래스에 EqualityComparer대해 우리 자신 을 작성할 Diff수 있습니다.

public class DiffEqualityComparer : IEqualityComparer<Diff>
{
    public bool Equals(Diff first, Diff second)
    {
        if (first == null || second == null) return ReferenceEquals(first, second);

        if (first.Properties == null && second.Properties != null) return false;
        if (first.Properties != null && second.Properties == null) return false;
        if (first.Properties != null && second.Properties != null &&
            !first.Properties.OrderBy(p => p)
                .SequenceEqual(second.Properties.OrderBy(p => p)))
            return false;
        if (!first.Action.Equals(second.Action)) return false;
        if (!string.Equals(first.Type, second.Type)) return false;
        if (!first.Start.Equals(second.Start)) return false;
        if (!first.End.Equals(second.End)) return false;
        if (!first.RateId.Equals(second.RateId)) return false;
        if (!string.Equals(first.RateDescription, second.RateDescription)) return false;

        return true;
    }

    public int GetHashCode(Diff obj)
    {
        var hash = obj.Properties?.Aggregate(0,
            (accumulator, current) => accumulator * 17 + current.GetHashCode()) ?? 0;
        hash = hash * 17 + obj.Action.GetHashCode();
        hash = hash * 17 + obj.Type?.GetHashCode() ?? 0;
        hash = hash * 17 + obj.Start.GetHashCode();
        hash = hash * 17 + obj.End.GetHashCode();
        hash = hash * 17 + obj.RateId.GetHashCode();
        hash = hash * 17 + obj.RateDescription?.GetHashCode() ?? 0;

        return hash;
    }
}

마지막으로 GroupBy메서드 에서이 사용자 지정 비교자를 사용할 수 있습니다 .

var results = diffs
    .GroupBy(diff => new DiffEqualityComparer())
    .Select( // rest of code omitted 
1 surprised_ferret Aug 19 2020 at 06:48

내가 해결 했어!

이 질문에 대한 다른 질문 과 댓글 + 답변을 읽고 이해 하는 데 도움이되었습니다!

public class DiffComparer : IEqualityComparer<Diff>
    {
        public bool Equals(Diff x, Diff y)
        {
            return x.Action == y.Action &&
                x.Type == y.Type &&
                x.StartDateTime == y.StartDateTime &&
                x.EndDateTime == y.EndDateTime &&
                x.rateId== y.rateId &&
                x.rateDescription == y.rateDescription &&
                x.properties.SequenceEqual(y.properties);
        }

        public int GetHashCode(Diff x)
        {
            int hash = 17;

            hash = hash * 23 + x.Action.GetHashCode();
            hash = hash * 23 + x.Type.GetHashCode();
            hash = hash * 23 + x.StartDateTime .GetHashCode();
            hash = hash * 23 + x.EndDateTime.GetHashCode();
            hash = hash * 23 + x.rateId.GetHashCode();
            hash = hash * 23 + x.rateDescription.GetHashCode();

            foreach (string prop in x.properties)
            {
                hash = hash * 31 + prop.GetHashCode();
            }

            return hash;
        }
    }

그리고 LINQ를 이렇게 편집했습니다.


var results = diffs
    .GroupBy(diff => diff, new DiffComparer()) 
    .Select(group => new Diff(
        group.Key.Action,
        group.Key.ScheduleType,
        group.Key.StartDateTime,
        group.Key.EndDateTime,
        group.Key.rateId,
        group.Key.rateDescription,
        group.Key.properties,
        group
            .Select(ts => ts.DayOfWeek)
            .Distinct()
            .OrderBy(dow => dow)
            .ToList()))
    .ToList();