如何在<string> GroupBy() 中合并列表字段



我有一个Diff对象列表Diff看起来像这个

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查询没有做我认为它会做的事情。我在GroupBy()中传递diff.properties,这是一个列表,我希望它在列表中的所有字符串值都匹配时进行分组

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

resultsdiffs之间的唯一区别在于,先前存储在diffs中的单数DayOfWeek现在被放入复数DaysOfWeek字段(但列表中只有1个项目(。目前,resultsdiffs中的项目数量相同。

我想在结果列表中看到的是:

  1. 一个较短的列表,基于所有分组的匹配进行合并(包括diff.properties列表值(
  2. 这也意味着在DaysOfWeek列表中有1个以上的项目

我的问题是:

如何更改上面的LINQ查询以查看我想在results中看到的内容?

您与匿名类型一起使用的分组包含一个List<string>,这将导致您获得一个1-1未分组的集合。

你需要选择

  • 使用自定义类进行分组并重载GetHashCodeEquals,如本问题所示:使用LINQ GroupBy按引用对象而非值对象进行分组

-OR-

  • 根据所选的值或其子集组成字符串(diff.properties, diff.Action, diff.Type, diff.startdatetime, diff.enddatetime, diff.rateId, diff.rateDescription(,该字符串将用作分组的唯一键

您的一些GroupBy属性是引用类型,这些类型的默认比较器是引用比较,因此它们都不会匹配。为了克服这一点,我们可以为Diff类编写自己的EqualityComparer,这样我们就可以用自己的方式进行比较:

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 

我解决了它!

阅读另一个问题和这个问题的评论+答案帮助我弄明白了!

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

最新更新