Comment incorporer le champ List <string> dans GroupBy ()
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:
- Une liste plus courte qui consolide en fonction de la correspondance sur tous les regroupements (y compris les valeurs de liste diff.properties)
- 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
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
GetHashCodeetEqualscomme 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
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
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();