วิธีการรวมฟิลด์รายการ <string> ใน GroupBy ()

Aug 19 2020

ฉันมีรายการของวัตถุที่แตกต่างลักษณะต่างกันเช่นนี้

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

}

แบบสอบถาม LINQ ของฉันไม่ทำในสิ่งที่ฉันคิดว่าจะทำ ฉันกำลังผ่านในdiff.propertiesในGroupBy()ซึ่งเป็นรายการและฉันต้องการให้กลุ่มเมื่อค่าสตริงทั้งหมดในการแข่งขันรายการ

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

ความแตกต่างเพียงอย่างเดียวระหว่างresultsและdiffsคือตอนนี้เอกพจน์DayOfWeekที่เก็บไว้ก่อนหน้านี้diffsจะถูกใส่ลงในDaysOfWeekฟิลด์พหูพจน์(แต่มีเพียง 1 รายการในรายการ) ขณะนี้มี # รายการเดียวกันทั้งในresultsและdiffs.

สิ่งที่ฉันต้องการเห็นในรายการผลลัพธ์คือ:

  1. รายการที่สั้นกว่าที่รวมตามการจับคู่บนการจัดกลุ่มทั้งหมด (รวมถึงค่ารายการ diff.properties)
  2. นอกจากนี้ยังหมายถึงมากกว่า 1 รายการในรายการ DaysOfWeek

คำถามของฉันคือ:

ฉันจะเปลี่ยนข้อความค้นหา LINQ ด้านบนเพื่อดูสิ่งที่ฉันต้องการดูได้resultsอย่างไร

คำตอบ

4 TravisJ Aug 19 2020 at 06:02

การจัดกลุ่มที่คุณใช้กับประเภทที่ไม่ระบุตัวตนมีสิ่งList<string>ที่ทำให้คุณได้รับชุดที่ไม่ได้จัดกลุ่ม 1-1

คุณต้องทำอย่างใดอย่างหนึ่ง

  • ใช้คลาสที่กำหนดเองสำหรับการจัดกลุ่มและการโอเวอร์โหลดGetHashCodeและEqualsดังที่แสดงในคำถามนี้: การใช้ LINQ GroupBy เพื่อจัดกลุ่มตามวัตถุอ้างอิงแทนที่จะเป็นวัตถุค่า

- หรือ -

  • เขียนสตริงจากค่าหรือส่วนย่อยของพวกเขาถูกเลือก ( diff.properties, diff.Action, diff.Type, diff.startdatetime, diff.enddatetime, diff.rateId, diff.rateDescription) ซึ่งจะทำหน้าที่เป็นคีย์เฉพาะในการจัดกลุ่มด้วย
2 RufusL Aug 19 2020 at 07:16

GroupByร็อพเพอร์ตี้บางส่วนของคุณเป็นประเภทอ้างอิงและตัวเปรียบเทียบเริ่มต้นสำหรับประเภทเหล่านี้เป็นการเปรียบเทียบอ้างอิงดังนั้นจึงไม่มีสิ่งใดที่จะตรงกัน เพื่อเอาชนะสิ่งนี้เราสามารถเขียนของเราเองEqualityComparerสำหรับDiffชั้นเรียนเพื่อให้เราสามารถเปรียบเทียบได้ในแบบของเรา:

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

และในที่สุดเราก็สามารถใช้ตัวเปรียบเทียบที่กำหนดเองนี้ในGroupByวิธีของเรา:

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

ฉันแก้ไขแล้ว!

การอ่านคำถามอื่นและความคิดเห็น + คำตอบในคำถามนี้ช่วยให้ฉันเข้าใจได้!

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

และฉันได้ทำการแก้ไขนี้กับ 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();