不同对象类型的 C# 列表:比 Zip 更好的检查相等性的方法
C# List of different object types: Better way than Zip to check equality
我现在有可用的代码,在 lambda 中有效地执行 Linq 的 Zip 扩展方法的元素比较,然后检查是否有任何失败的匹配,但我几乎可以肯定必须有一种更简洁和可读的方式完成同样的事情。
我知道我可以实现一个 EqualityComparer 并使用 SequenceEqual,但我不喜欢像这个这样的一次性列表比较解决方案的开销。我愿意接受 EqualityComparer 是 正确的 解决方案,但如果有其他选择,我很乐意看到它们。
class Occurrence {
DateTime Start;
// More fields...
}
bool SameDates(List<Occurrence> occurrences, List<DateTime> selectedDates)
{
if (occurrences.Count != selectedDates.Count)
return false;
// These three lines are the focus of the question
var dateEqualList = occurrences.Zip(selectedDates, (a, b) => a.Start.Date == b.Date);
if (dateEqualList.Contains(false))
return false;
return true;
}
这些列表有多大?是否可以采取另一种方法并做这样的事情:
class Occurrence {
DateTime Start;
// More fields...
}
bool SameDates(List<Occurrence> occurrences, List<DateTime> selectedDates)
{
if (occurrences.Count != selectedDates.Count)
return false;
var dates1List = occurrences.Select(o => o.Start).Distinct();
var dates1String = string.Join(",", dates1List.ToArray());
var dates2List = selectedDates.Select(o => o.Start).Distinct();
var dates2String = string.Join(",", dates2List.ToArray());
return dates1String == dates2String;
}
即使这不能完全解决或解决您的问题,也许它会给您一些想法。
我写了两个不同的备选方案,第二个有更好的性能。
选项 1:
bool SameDates(List<Occurrence> occurrences, List<DateTime> selectedDates)
{
if (occurrences.Count != selectedDates.Count)
return false;
var matchCount = occurrences.TakeWhile((d, i) => d.Start.Date == selectedDates[i].Date).Count();
return matchCount == occurrences.Count;
}
选择2:
bool SameDates2(List<Occurrence> occurrences, List<DateTime> selectedDates)
{
if (occurrences.Count != selectedDates.Count)
return false;
for (var i = 0; i < occurrences.Count; i++)
{
if (occurrences[i].Start.Date != selectedDates[i].Date)
return false;
}
return true;
}
因此您需要一个 LINQ 语句来检查所有索引的 Occurrences[i].Start
是否等于 selectedDates[i]
。您想在发现不相等的序列后立即停止。
我的建议是先 Select 属性 开始,然后检查结果序列是否等于 selectedDates:
List<Occurence> occurences = ...
List<DateTime> selectedDates = ...
var allDatesEqual = occurrences.Count == selectedDates.Count
&& occurrences.Select(occurrence => occurence.Start)
.SequenceEqual(selectedDates);
如果两个序列都实现了 ICollection,通常 SequenceEqual 会检查长度。然而,Select
的结果并未实现 ICollection,因此对于此优化,您需要自己检查计数。
我读到您在开始比较之前对序列进行排序。如果所有日期都是唯一的,请考虑使用 HashSet<DateTime>
List<Occurence> occurences = ...
HashSet<DateTime> datesSet = new HashSet<DateTime>(selectedDates);
var allDatesEqual = datesSet.SetEquals(occurrences
.Select(occurrence => occurence.Start));
SetEquals returns 如果它们包含完全相同的元素,忽略顺序,则为真,因此设置 {A, B, C} 等于设置 {B, C, A},但不等于 {A, B},也不是{A、B、C、D}
我现在有可用的代码,在 lambda 中有效地执行 Linq 的 Zip 扩展方法的元素比较,然后检查是否有任何失败的匹配,但我几乎可以肯定必须有一种更简洁和可读的方式完成同样的事情。
我知道我可以实现一个 EqualityComparer 并使用 SequenceEqual,但我不喜欢像这个这样的一次性列表比较解决方案的开销。我愿意接受 EqualityComparer 是 正确的 解决方案,但如果有其他选择,我很乐意看到它们。
class Occurrence {
DateTime Start;
// More fields...
}
bool SameDates(List<Occurrence> occurrences, List<DateTime> selectedDates)
{
if (occurrences.Count != selectedDates.Count)
return false;
// These three lines are the focus of the question
var dateEqualList = occurrences.Zip(selectedDates, (a, b) => a.Start.Date == b.Date);
if (dateEqualList.Contains(false))
return false;
return true;
}
这些列表有多大?是否可以采取另一种方法并做这样的事情:
class Occurrence {
DateTime Start;
// More fields...
}
bool SameDates(List<Occurrence> occurrences, List<DateTime> selectedDates)
{
if (occurrences.Count != selectedDates.Count)
return false;
var dates1List = occurrences.Select(o => o.Start).Distinct();
var dates1String = string.Join(",", dates1List.ToArray());
var dates2List = selectedDates.Select(o => o.Start).Distinct();
var dates2String = string.Join(",", dates2List.ToArray());
return dates1String == dates2String;
}
即使这不能完全解决或解决您的问题,也许它会给您一些想法。
我写了两个不同的备选方案,第二个有更好的性能。
选项 1:
bool SameDates(List<Occurrence> occurrences, List<DateTime> selectedDates)
{
if (occurrences.Count != selectedDates.Count)
return false;
var matchCount = occurrences.TakeWhile((d, i) => d.Start.Date == selectedDates[i].Date).Count();
return matchCount == occurrences.Count;
}
选择2:
bool SameDates2(List<Occurrence> occurrences, List<DateTime> selectedDates)
{
if (occurrences.Count != selectedDates.Count)
return false;
for (var i = 0; i < occurrences.Count; i++)
{
if (occurrences[i].Start.Date != selectedDates[i].Date)
return false;
}
return true;
}
因此您需要一个 LINQ 语句来检查所有索引的 Occurrences[i].Start
是否等于 selectedDates[i]
。您想在发现不相等的序列后立即停止。
我的建议是先 Select 属性 开始,然后检查结果序列是否等于 selectedDates:
List<Occurence> occurences = ...
List<DateTime> selectedDates = ...
var allDatesEqual = occurrences.Count == selectedDates.Count
&& occurrences.Select(occurrence => occurence.Start)
.SequenceEqual(selectedDates);
如果两个序列都实现了 ICollection,通常 SequenceEqual 会检查长度。然而,Select
的结果并未实现 ICollection,因此对于此优化,您需要自己检查计数。
我读到您在开始比较之前对序列进行排序。如果所有日期都是唯一的,请考虑使用 HashSet<DateTime>
List<Occurence> occurences = ...
HashSet<DateTime> datesSet = new HashSet<DateTime>(selectedDates);
var allDatesEqual = datesSet.SetEquals(occurrences
.Select(occurrence => occurence.Start));
SetEquals returns 如果它们包含完全相同的元素,忽略顺序,则为真,因此设置 {A, B, C} 等于设置 {B, C, A},但不等于 {A, B},也不是{A、B、C、D}