gpt4 book ai didi

c# - 如何使用 IEnumerable.GroupBy 比较元素之间的多个属性?

转载 作者:太空狗 更新时间:2023-10-29 21:26:07 25 4
gpt4 key购买 nike

如何将“相邻”站点分组:

给定数据:

List<Site> sites = new List<Site> {
new Site { RouteId="A", StartMilepost=0.00m, EndMilepost=1.00m },
new Site { RouteId="A", StartMilepost=1.00m, EndMilepost=2.00m },
new Site { RouteId="A", StartMilepost=5.00m, EndMilepost=7.00m },
new Site { RouteId="B", StartMilepost=3.00m, EndMilepost=5.00m },
new Site { RouteId="B", StartMilepost=11.00m, EndMilepost=13.00m },
new Site { RouteId="B", StartMilepost=13.00m, EndMilepost=14.00m },
};

我要结果:

[
[
Site { RouteId="A", StartMilepost=0.00m, EndMilepost=1.00m },
Site { RouteId="A", StartMilepost=1.00m, EndMilepost=2.00m }
],
[
Site { RouteId="A", StartMilepost=5.00m, EndMilepost=7.00m }
],
[
Site { RouteId="B", StartMilepost=3.00m, EndMilepost=5.00m }
],
[
Site { RouteId="B", StartMilepost=11.00m, EndMilepost=13.00m },
Site { RouteId="B", StartMilepost=13.00m, EndMilepost=14.00m }
]
]

我尝试将 GroupBy 与自定义比较器功能结合使用,检查 routeIds 匹配,并且第一个站点的结束里程碑等于下一个站点的开始里程碑。我的 HashKey 函数只检查 routeId,因此路由中的所有站点都将合并在一起,但我认为比较器会做出假设,如果 A = B,并且 B = C,则 A = C,因此 C 不会与 A 分组,B,C 因为在我的邻接情况下,A 将不等于 C。

最佳答案

首先,让Site class be(用于调试/演示)

public class Site {
public Site() { }

public string RouteId;
public Decimal StartMilepost;
public Decimal EndMilepost;

public override string ToString() => $"{RouteId} {StartMilepost}..{EndMilepost}";
}

好吧,如您所见,我们必须打破规则:相等性必须传递,即无论何时

A equals B
B equals C

然后

A equals C

在您的示例中并非如此。但是,如果我们按 StartMilepost 对网站进行排序从技术上讲,我们可以实现IEqualityComparer<Site>像这样:

public class MySiteEqualityComparer : IEqualityComparer<Site> {
public bool Equals(Site x, Site y) {
if (ReferenceEquals(x, y))
return true;
else if (null == x || null == y)
return false;
else if (x.RouteId != y.RouteId)
return false;
else if (x.StartMilepost <= y.StartMilepost && x.EndMilepost >= y.StartMilepost)
return true;
else if (y.StartMilepost <= x.StartMilepost && y.EndMilepost >= x.StartMilepost)
return true;

return false;
}

public int GetHashCode(Site obj) {
return obj == null
? 0
: obj.RouteId == null
? 0
: obj.RouteId.GetHashCode();
}
}

然后 GroupBy照常;请注意 OrderBy是必需的,因为这里的比较顺序很重要。假设我们有

A = {RouteId="X", StartMilepost=0.00m, EndMilepost=1.00m}
B = {RouteId="X", StartMilepost=1.00m, EndMilepost=2.00m}
C = {RouteId="X", StartMilepost=2.00m, EndMilepost=3.00m}

在这里A == B , B == C (所以在 A, B, C 的情况下,所有项目都将在同一组中)但是 A != C (因此在 A, C, B 中将以 3 组结束)

代码:

 List<Site> sites = new List<Site> {
new Site { RouteId="A", StartMilepost=0.00m, EndMilepost=1.00m },
new Site { RouteId="A", StartMilepost=1.00m, EndMilepost=2.00m },
new Site { RouteId="A", StartMilepost=5.00m, EndMilepost=7.00m },
new Site { RouteId="B", StartMilepost=3.00m, EndMilepost=5.00m },
new Site { RouteId="B", StartMilepost=11.00m, EndMilepost=13.00m },
new Site { RouteId="B", StartMilepost=13.00m, EndMilepost=14.00m },
};

var result = sites
.GroupBy(item => item.RouteId)
.Select(group => group
// Required Here, since MySiteEqualityComparer breaks the rules
.OrderBy(item => item.StartMilepost)
.GroupBy(item => item, new MySiteEqualityComparer())
.ToArray())
.ToArray();

// Let's have a look
var report = string.Join(Environment.NewLine, result
.Select(group => string.Join(Environment.NewLine,
group.Select(g => string.Join("; ", g)))));

Console.Write(report);

结果:

A 0.00..1.00; A 1.00..2.00
A 5.00..7.00
B 3.00..5.00
B 11.00..13.00; B 13.00..14.00

关于c# - 如何使用 IEnumerable.GroupBy 比较元素之间的多个属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56114916/

25 4 0