Cómo incorporar el campo List <string> en GroupBy ()
Tengo una lista de objetos Diff Diff se ve así
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
}
Mi consulta LINQ no hace lo que creo que hará. Estoy pasando en diff.properties
el GroupBy()
, que es una lista y lo quiero al grupo cuando todos los valores de cadena en un partido de la lista
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 única diferencia entre results
y diffs
es que el singular DayOfWeek
que se almacenó anteriormente diffs
ahora se coloca en el DaysOfWeek
campo plural (pero solo 1 elemento en la lista). Actualmente, el mismo número de elementos en ambos results
y diffs
.
Lo que me gustaría ver en la lista de resultados son:
- Una lista más corta que se consolida en función de la coincidencia de todos los grupos (incluidos los valores de la lista de propiedades de diferencia)
- Esto también significaría más de 1 elemento en la lista DaysOfWeek.
Mi pregunta es:
¿Cómo puedo cambiar mi consulta LINQ anterior para ver lo que quiero ver results
?
Respuestas
La agrupación que está utilizando con el tipo anónimo contiene un List<string>
que está provocando que obtenga un conjunto desagrupado 1-1.
Necesitas ya sea
- use una clase personalizada para la agrupación y la sobrecarga
GetHashCode
yEquals
como se muestra en esta pregunta: Usar LINQ GroupBy para agrupar por objetos de referencia en lugar de objetos de valor
- O -
- componer una cadena de los valores, o un subconjunto de ellos, siendo seleccionado (
diff.properties, diff.Action, diff.Type, diff.startdatetime, diff.enddatetime, diff.rateId, diff.rateDescription
) que servirá como una clave única para agrupar con
Algunas de sus GroupBy
propiedades son tipos de referencia, y el comparador predeterminado para estos tipos es una comparación de referencias, por lo que ninguna de ellas coincidirá. Para superar esto, podemos escribir los nuestros EqualityComparer
para la Diff
clase para poder compararlos a nuestra manera:
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;
}
}
Y finalmente podemos usar este comparador personalizado en nuestro GroupBy
método:
var results = diffs
.GroupBy(diff => new DiffEqualityComparer())
.Select( // rest of code omitted
¡Lo resolví!
Leer otra pregunta y los comentarios + respuestas en esta pregunta me ayudó a resolverlo.
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;
}
}
E hice esta edición en 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();