复合 Class 的可重用匿名排名函数
Reusable Anonymous Ranking Function For Composite Class
我有一个 class 组合,由来自不同来源的 classes 统计数据组成。复合 class 根据统计 classes 中的排序和排名属性获得 "total score"。
例如,一个统计数据class可能如下:
public class StatsTypeA
{
public int PropAX { get; set; }
public int PropAXPoints { get; set; }
public int PropAY { get; set; }
public int PropAYPoints { get; set; }
}
复合class就像这个例子:
class CompositeType
{
public StatsTypeA StatsA { get; set; }
public StatsTypeB StatsB { get; set; }
public int Id { get; set; }
public int TotalPoints
{
get
{
return StatsA.PropAXPoints + StatsA.PropAYPoints + StatsB.PropBXPoints + StatsB.PropBYPoints;
}
}
}
在主要 class 中,将有一个复合 classes 的列表,其中包含 classes 中每个属性的值:
List<CompositeType> participants = new List<CompositeType>();
我想对它们进行排序和排名,并且可以用蛮力做到这一点,但想不出一种不重复一堆代码的方法。例如,单个 属性 的排名和评分可能为:
var statsX = composites.GroupBy(s => s.StatsA.PropAX,
s => s.Id,
(key, group) => (new
{
Stats = key,
Participants = group.ToList()
}))
.OrderBy(s => s.Stats);
int points = 0;
foreach (var statGroup in statsX)
{
var maxPoints = points + statGroup.Participants.Count * 2 - 2;
var thisPoints = (points + maxPoints) / 2;
foreach (var id in statGroup.Participants)
{
composites.Single(data => data.Id == id).StatsA.PropAXPoints = points;
}
points = maxPoints + 2;
}
有没有办法使用上面 LINQ 语句中的匿名类型创建可重用的匿名函数?换句话说,我怎么能有一个单一的 function/extension 方法来对每个单独的统计数据进行排名 属性?
这是我想出的通用方法:
Action<IEnumerable<CompositeType>, Func<CompositeType, int>, Action<CompositeType, int>> compute =
(cs, f, a) =>
{
var stats =
from c in cs
group c by f(c) into gs
orderby gs.Key
select new
{
Stats = gs.Key,
Participants = gs.ToList()
};
stats.Aggregate(0, (points, statGroup) =>
{
var maxPoints = points + statGroup.Participants.Count * 2 - 2;
var avgPoints = (points + maxPoints)/2;
statGroup.Participants.ForEach(c => a(c, avgPoints));
return maxPoints + 2;
});
};
现在我可以这样称呼它了:
compute(composites, c => c.StatsA.PropAX, (c, p) => c.StatsA.PropAXPoints = p);
compute(composites, c => c.StatsA.PropAY, (c, p) => c.StatsA.PropAYPoints = p);
compute(composites, c => c.StatsB.PropBX, (c, p) => c.StatsB.PropBXPoints = p);
compute(composites, c => c.StatsB.PropBY, (c, p) => c.StatsB.PropBYPoints = p);
这是你想要的吗?
我有一个 class 组合,由来自不同来源的 classes 统计数据组成。复合 class 根据统计 classes 中的排序和排名属性获得 "total score"。
例如,一个统计数据class可能如下:
public class StatsTypeA
{
public int PropAX { get; set; }
public int PropAXPoints { get; set; }
public int PropAY { get; set; }
public int PropAYPoints { get; set; }
}
复合class就像这个例子:
class CompositeType
{
public StatsTypeA StatsA { get; set; }
public StatsTypeB StatsB { get; set; }
public int Id { get; set; }
public int TotalPoints
{
get
{
return StatsA.PropAXPoints + StatsA.PropAYPoints + StatsB.PropBXPoints + StatsB.PropBYPoints;
}
}
}
在主要 class 中,将有一个复合 classes 的列表,其中包含 classes 中每个属性的值:
List<CompositeType> participants = new List<CompositeType>();
我想对它们进行排序和排名,并且可以用蛮力做到这一点,但想不出一种不重复一堆代码的方法。例如,单个 属性 的排名和评分可能为:
var statsX = composites.GroupBy(s => s.StatsA.PropAX,
s => s.Id,
(key, group) => (new
{
Stats = key,
Participants = group.ToList()
}))
.OrderBy(s => s.Stats);
int points = 0;
foreach (var statGroup in statsX)
{
var maxPoints = points + statGroup.Participants.Count * 2 - 2;
var thisPoints = (points + maxPoints) / 2;
foreach (var id in statGroup.Participants)
{
composites.Single(data => data.Id == id).StatsA.PropAXPoints = points;
}
points = maxPoints + 2;
}
有没有办法使用上面 LINQ 语句中的匿名类型创建可重用的匿名函数?换句话说,我怎么能有一个单一的 function/extension 方法来对每个单独的统计数据进行排名 属性?
这是我想出的通用方法:
Action<IEnumerable<CompositeType>, Func<CompositeType, int>, Action<CompositeType, int>> compute =
(cs, f, a) =>
{
var stats =
from c in cs
group c by f(c) into gs
orderby gs.Key
select new
{
Stats = gs.Key,
Participants = gs.ToList()
};
stats.Aggregate(0, (points, statGroup) =>
{
var maxPoints = points + statGroup.Participants.Count * 2 - 2;
var avgPoints = (points + maxPoints)/2;
statGroup.Participants.ForEach(c => a(c, avgPoints));
return maxPoints + 2;
});
};
现在我可以这样称呼它了:
compute(composites, c => c.StatsA.PropAX, (c, p) => c.StatsA.PropAXPoints = p);
compute(composites, c => c.StatsA.PropAY, (c, p) => c.StatsA.PropAYPoints = p);
compute(composites, c => c.StatsB.PropBX, (c, p) => c.StatsB.PropBXPoints = p);
compute(composites, c => c.StatsB.PropBY, (c, p) => c.StatsB.PropBYPoints = p);
这是你想要的吗?