Cómo incorporar el campo List <string> en GroupBy ()

Aug 19 2020

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.propertiesel 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 resultsy diffses que el singular DayOfWeekque se almacenó anteriormente diffsahora se coloca en el DaysOfWeekcampo plural (pero solo 1 elemento en la lista). Actualmente, el mismo número de elementos en ambos resultsy diffs.

Lo que me gustaría ver en la lista de resultados son:

  1. 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)
  2. 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

4 TravisJ Aug 19 2020 at 06:02

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 GetHashCodey Equalscomo 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
2 RufusL Aug 19 2020 at 07:16

Algunas de sus GroupBypropiedades 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 EqualityComparerpara la Diffclase 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 GroupBymétodo:

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

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