Comment incorporer le champ List <string> dans GroupBy ()

Aug 19 2020

J'ai une liste d'objets Diff. Diff ressemble à ceci

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

}

Ma requête LINQ ne fait pas ce que je pense qu'elle fera. Je passe dans diff.propertiesle GroupBy(), qui est une liste et je veux qu'il soit groupé lorsque toutes les valeurs de chaîne d'une liste correspondent

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

La seule différence entre resultset diffsest que le singulier DayOfWeekqui était précédemment stocké dans diffsest maintenant placé dans le DaysOfWeekchamp pluriel (mais seulement 1 élément dans la liste). Actuellement, même nombre d'articles dans les deux resultset diffs.

Ce que j'aimerais voir dans la liste des résultats sont:

  1. Une liste plus courte qui consolide en fonction de la correspondance sur tous les regroupements (y compris les valeurs de liste diff.properties)
  2. Cela signifierait également plus d'un élément dans la liste DaysOfWeek.

Ma question est:

Comment puis-je modifier ma requête LINQ ci-dessus pour voir ce que je veux voir results?

Réponses

4 TravisJ Aug 19 2020 at 06:02

Le regroupement que vous utilisez avec le type anonyme contient un List<string>qui vous amène à obtenir un ensemble non groupé 1-1.

Vous devez soit

  • utiliser une classe personnalisée pour le regroupement et la surcharge GetHashCodeet Equalscomme indiqué dans cette question: Utilisation de LINQ GroupBy pour grouper par des objets de référence au lieu d'objets de valeur

- OU -

  • composer une chaîne à partir des valeurs, ou d'un sous-ensemble de celles-ci, en cours de sélection ( diff.properties, diff.Action, diff.Type, diff.startdatetime, diff.enddatetime, diff.rateId, diff.rateDescription) qui servira de clé unique avec laquelle regrouper
2 RufusL Aug 19 2020 at 07:16

Quelques-unes de vos GroupBypropriétés sont des types de référence, et le comparateur par défaut pour ces types est une comparaison de référence, donc aucune de celles-ci ne correspondra jamais. Pour surmonter cela, nous pouvons écrire les nôtres EqualityComparerpour la Diffclasse afin de pouvoir les comparer à notre manière:

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

Et enfin, nous pouvons utiliser ce comparateur personnalisé dans notre GroupByméthode:

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

Je l'ai résolu!

La lecture d' une autre question et les commentaires + réponses à cette question m'ont aidé à comprendre!

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

Et j'ai fait cette modification sur le 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();