【问题标题】:GroupBy() not grouping correctly on List<T> property [duplicate]GroupBy() 没有在 List<T> 属性上正确分组[重复]
【发布时间】:2018-11-23 07:58:59
【问题描述】:

我试图有条件地显示一个“周列表”(一周中的每一天),要么显示所有 5 天(周一至周五),要么基于单个“列表项”(代表一周中所有天的单个列表项) on 如果每天的所有项目都相同(键除外)。在我将“DayFunctions”(列表)添加为“周列表”的一个项目之前,以下工作。使用 List 属性进行分组时是否需要做一些不同的事情?

我无法在 SO 中找到任何解决此特定分组问题的内容,甚至在谷歌上搜索(好几天)。

使用自定义比较器更新

//POCO
public class ScheduleWeek : IScheduleWeek
{
    public int Id { get; set; }
    public int Yr { get; set; }
    public int WeekNo { get; set; }
    public List<ScheduleDay> ScheduleDays { get; set; }
}


// POCO
public class ScheduleDay : IScheduleDay
{
    public int Id { get; set; }
    public int ScheduleWeekId { get; set; }
    public string EmpId { get; set; }
    public DateTime ScheduleDayDt { get; set; }
    public Shift Shift { get; set; }
    public Station Station { get; set; }
    public List<DayFunction> DayFunctions { get; set; }
}

// POCO
public class DayFunction
{
    public int ScheduleDayId { get; set; }
    public int FunctionId { get; set; }
}


// HELPER METHOD
public async Task<List<ScheduleDay>> GetScheduleDaysWithDayFunctions(int stationId, 
                                                                        string empId, 
                                                                        int scheduleWeekId)
{
    var sdlist = await _ctx.ScheduleDays
        .Where(sd => sd.ScheduleDayDt.DayOfWeek != DayOfWeek.Sunday &&
                        sd.ScheduleDayDt.DayOfWeek != DayOfWeek.Saturday &&
                        sd.Station.Id == stationId &&
                        sd.EmpId == empId &&
                        sd.ScheduleWeekId == scheduleWeekId)
        .Include(d => d.DayFunctions)
        .ToListAsync();

    return sdlist;
}

// HELPER METHOD
//  ** ADDED CUSTOM COMPARER
public class ScheduleDayComparer : IEqualityComparer<ScheduleDay>
{
    public bool Equals(ScheduleDay x, ScheduleDay y)
    {
        return x.Id == y.Id && x.DayFunctions
                    .SequenceEqual(y.DayFunctions);
    }

    public int GetHashCode(ScheduleDay x)
    {
        return x.Id.GetHashCode() ^ x.DayFunctions
                    .Aggregate(0, (a, y) => a ^ y.GetHashCode());
    }
}

// MODEL FOR VIEW
public class ScheduleWeekViewModel
{
    public ScheduleWeek ScheduleWeek { get; set; }
    public ScheduleHelpers ScheduleHelpers { get; set; }
}


// MVC VIEW
@{
    var scheduleDayComparer = new ScheduleHelpers.ScheduleDayComparer(); // <-- ADDED        
    var scheduledays = Model.ScheduleHelpers
        .GetScheduleDaysWithDayFunctions(station.Id,
            employee.Id.ToString(),
            Model.ScheduleWeek.Id).Result
        .GroupBy(x => x, scheduleDayComparer) // <-- ADDED
            .Select(g => g.First())           // <-- ADDED
            .ToList();                        // <-- ADDED
        //.GroupBy(sd => new
        //    {
        //        sd.Shift.Id,
        //        sd.DayFunctions // <-- this causes the grouping to break
        //    })
        //.Select(g => g.First());
}
<table>
    @foreach (var scheduleday in scheduledays)
    {
        <tr>
            <td>
                <text>SHIFT: @scheduleday.Shift.Name </text>
            </td>
            <td>
                @foreach (var df in scheduleday.DayFunctions)
                    {
                        <text>DAY FUNCTIONS: @df.FunctionId </text>
                    }   
            </td>
        </tr>
    }
</table>

//  CONTROLLER ACTION
public async Task<IActionResult> LocationScheduler()
{
    var vm = new ScheduleWeekViewModel{};
    var user = await _userManager.GetUserAsync(User);
    vm.Employees = await _tcomsHelpers.GetEmployeesOfSupervisor(user);
    vm.ScheduleDays = await _scheduleHelpers.GetScheduleWeekDaysForScheduler(vm.Employees);
    vm.ScheduleWeek = await _scheduleHelpers.GetCurrentScheduleWeek();
    vm.ScheduleHelpers = _scheduleHelpers;
    return View(vm);
}

【问题讨论】:

  • C# 不知道,如何比较两个列表进行分组。您必须为两个列表编写自定义比较函数
  • 我不是在比较两个列表。这是列表中的列表。
  • GroupBy 需要一些函数来比较对象。对于默认类型(整数、字符串),它使用默认比较。但是像List&lt;T&gt; 这样的复杂类型需要自定义。阅读有关GroupBy 方法的更多信息
  • @Backs 那是票。感谢您指点我查看 IEqualityComparer!在您帮助我缩小搜索范围后,我找到了这个link。请回答,这样我就可以给你信用,谢谢!
  • @Emo333 您对IEqualityComparer&lt;T&gt;SequenceEquals 调用的实现除了具有相同的内容外,还需要相同的序列。这可能有问题,具体取决于您的要求。您的哈希码方法也不是最佳的,因为^ 可能会导致“误报”。更好的实现使用小的素数和乘法,如下所示:Aggregate(0, (a, y) =&gt; 31*a + y.GetHashCode()); 最后,请注意您需要在 DayFunction 类中实现 HashCodeEquals

标签: c# asp.net-mvc linq .net-core


【解决方案1】:

当您在 GroupBy 中包含列表属性时,您最终会在 GroupBy 实现的深处进行列表比较,这通常不是您想要的。

您可以解决这个by implementing a custom IEqualityComparer,但是您必须为组键定义一个命名类型,因为不能为匿名类型定义IEqualityComparer。另一种方法是按复合 string 键进行分组,如下所示:

.GroupBy(sd => new {
    sd.Shift.Id
,   string.Join(
        "|"
    ,   sd.DayFunctions
            .OrderBy(f => f.ScheduleDayId).ThenBy(f => f.FunctionId)
            .Select(f => $"{f.ScheduleDayId}:{f.FunctionId}")
    )
})

【讨论】:

    猜你喜欢
    • 2022-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-22
    • 2013-06-24
    • 1970-01-01
    相关资源
    最近更新 更多