Cara menggabungkan bidang Daftar <string> di GroupBy ()

Aug 19 2020

Saya memiliki daftar objek Diff terlihat seperti ini

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

}

Kueri LINQ saya tidak melakukan apa yang menurut saya akan dilakukannya. Saya lewat di diff.propertiesdalam GroupBy(), yang merupakan Daftar dan saya ingin kelompok ketika semua nilai string dalam daftar pertandingan

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

Satu-satunya perbedaan antara resultsdan diffsadalah bahwa bentuk tunggal DayOfWeekyang sebelumnya disimpan diffssekarang dimasukkan ke dalam DaysOfWeekbidang jamak (tetapi hanya 1 item dalam daftar). Saat ini, # item yang sama di resultsdan diffs.

Apa yang ingin saya lihat di daftar hasil adalah:

  1. Daftar yang lebih pendek yang menggabungkan berdasarkan pencocokan pada semua pengelompokan (termasuk nilai daftar properti berbeda)
  2. Ini juga berarti lebih dari 1 item dalam daftar DaysOfWeek.

Pertanyaanku adalah:

Bagaimana cara mengubah kueri LINQ saya di atas untuk melihat apa yang ingin saya lihat results?

Jawaban

4 TravisJ Aug 19 2020 at 06:02

Pengelompokan yang Anda gunakan dengan tipe anonim berisi List<string>yang menyebabkan Anda mendapatkan 1-1 set tidak dikelompokkan.

Anda juga harus melakukannya

  • gunakan kelas khusus untuk pengelompokan dan kelebihan beban GetHashCodedan Equalsseperti yang ditunjukkan dalam pertanyaan ini: Menggunakan LINQ GroupBy untuk mengelompokkan berdasarkan objek referensi, bukan objek nilai

- ATAU -

  • menulis string dari nilai, atau bagiannya, dipilih ( diff.properties, diff.Action, diff.Type, diff.startdatetime, diff.enddatetime, diff.rateId, diff.rateDescription) yang akan berfungsi sebagai kunci unik untuk mengelompokkan
2 RufusL Aug 19 2020 at 07:16

Beberapa GroupByproperti Anda adalah jenis referensi, dan pembanding default untuk jenis ini adalah perbandingan referensi, jadi tidak satu pun dari properti ini yang cocok. Untuk mengatasi hal ini, kita dapat menulis kita sendiri EqualityCompareruntuk Diffkelas sehingga kita dapat membandingkan mereka dengan cara kita sendiri:

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

Dan akhirnya kita bisa menggunakan pembanding khusus ini dalam GroupBymetode kita :

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

Saya menyelesaikannya!

Membaca pertanyaan lain dan komentar + jawaban dalam pertanyaan ini membantu saya mengetahuinya!

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

Dan saya melakukan pengeditan ini ke 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();