So integrieren Sie das Feld List <string> in GroupBy ()

Aug 19 2020

Ich habe eine Liste von Diff-Objekten. Diff sieht so aus

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

}

Meine LINQ-Abfrage macht nicht das, was ich denke. Ich bin vorbei in diff.propertiesin dem GroupBy(), der eine Liste ist und ich mag es Gruppe , wenn all String - Wert in einer Liste Spiel

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();

Der einzige Unterschied zwischen resultsund diffsbesteht darin, dass der DayOfWeekzuvor gespeicherte Singular diffsjetzt in das Pluralfeld DaysOfWeekeingefügt wird (aber nur 1 Element in der Liste). Derzeit die gleiche Anzahl von Elementen in beiden resultsund diffs.

Was ich in der Ergebnisliste sehen möchte, sind:

  1. Eine kürzere Liste, die basierend auf dem Abgleich aller Gruppierungen konsolidiert wird (einschließlich der Listenwerte von diff.properties).
  2. Dies würde auch mehr als 1 Element in der DaysOfWeek-Liste bedeuten.

Meine Frage ist:

Wie kann ich meine LINQ-Abfrage oben ändern, um zu sehen, was ich sehen möchte results?

Antworten

4 TravisJ Aug 19 2020 at 06:02

Die Gruppierung, die Sie mit dem anonymen Typ verwenden, enthält a List<string>, wodurch Sie einen 1-1-Satz ohne Gruppierung erhalten.

Sie müssen entweder

  • Verwenden Sie eine benutzerdefinierte Klasse für die Gruppierung und Überladung GetHashCodeund Equalswie in dieser Frage gezeigt: Verwenden von LINQ GroupBy zum Gruppieren nach Referenzobjekten anstelle von Wertobjekten

- ODER -

  • Erstellen Sie eine Zeichenfolge aus den ausgewählten Werten oder einer Teilmenge davon ( diff.properties, diff.Action, diff.Type, diff.startdatetime, diff.enddatetime, diff.rateId, diff.rateDescription), die als eindeutiger Schlüssel zum Gruppieren dient
2 RufusL Aug 19 2020 at 07:16

Einige Ihrer GroupByEigenschaften sind Referenztypen, und der Standardvergleich für diese Typen ist ein Referenzvergleich, sodass keiner dieser Eigenschaften jemals übereinstimmt. Um dies zu überwinden, können wir unsere eigenen EqualityComparerfür die DiffKlasse schreiben, damit wir sie auf unsere eigene Weise vergleichen können:

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;
    }
}

Und schließlich können wir diesen benutzerdefinierten Vergleicher in unserer GroupByMethode verwenden:

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

Ich habe es gelöst!

Das Lesen einer anderen Frage und der Kommentare + Antworten in dieser Frage hat mir geholfen, es herauszufinden!

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;
        }
    }

Und ich habe diese Änderung am LINQ vorgenommen:


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();