我正在努力寻找更好的方法来解决我的问题。 我有一个属性列表。一些属性应排在"排名" (谁更好)。
以下是数据结构:
public class MovieRankData
{
public Guid MovieId { get; set; }
public RankData TotalViews { get; set; }
public RankData TotalMoney { get; set; }
}
public class RankData
{
public string Name { get; set; }
public double? Value { get; set; }
public int Rank { get; set; }
}
非常简单的代码(请参阅评论):
var movieRankData = new List<MovieRankData>
{
new MovieRankData
{
MovieId = Guid.NewGuid(),
TotalViews = new RankData {Name = "Total Movie View", Value = 985 }, // Rank should be 2 (of 3)
TotalMoney = new RankData {Name = "Total Movie Money", Value = 525.12}, // Rank should be 1 (of 3)
},
new MovieRankData
{
MovieId = Guid.NewGuid(),
TotalViews = new RankData {Name = "Total Movie View", Value = 812}, // Rank should be 3 (of 3)
TotalMoney = new RankData {Name = "Total Movie Money", Value = 100}, // Rank should be 3 (of 3)
},
new MovieRankData
{
MovieId = Guid.Empty,
TotalViews = new RankData {Name = "Total Movie View", Value = 1000}, // Rank should be 1 (of 3)
TotalMoney = new RankData {Name = "Total Movie Money", Value = 321}, // Rank should be 2 (of 3)
}
};
我提出了丑陋的foreach,许多临时变量来比较和设置Rank属性。
通过每个元素,将其与其他元素进行比较并对其进行评分的更好方法是什么?
感谢您的建议!
答案 0 :(得分:0)
参见BotondBalázs对更简单解决方案的回答
继承IComparable接口并根据需要实现CompareTo方法。 例如:
class MovierankData:IComparable
{
public int CompareTo(MovierankData obj)
{
if(this.Value > obj.Value)
{
return 1;
}
else if(this.Value < obj.Value)
{
return -1;
}
else
{
return this.Name.CompareTo(obj.Name);
}
}
}
您现在可以调用yourlist.Sort()并按值排序,如果值相同,则按名称排序。
答案 1 :(得分:0)
您可以实现一个自定义IComparer,用于比较MovieRankData元素并使用Lists Sort方法。
在此处查看示例(请参阅第二个答案): LINQ Custom Sort
答案 2 :(得分:0)
我将如何做到这一点:
var byTotalViews = movieRankData.OrderByDescending(m => m.TotalViews.Value).ToList();
var byTotalMoney = movieRankData.OrderByDescending(m => m.TotalMoney.Value).ToList();
foreach (var movie in movieRankData)
{
movie.TotalViews.Rank = byTotalViews.IndexOf(movie) + 1;
movie.TotalMoney.Rank = byTotalMoney.IndexOf(movie) + 1;
}
虽然这不会处理null
和TotalViews
的{{1}}值。如果你告诉我在这种情况下该怎么做,我会修改我的答案。我目前的解决方案将最高等级分配给TotalMoney
,因为可以进行可比较的比较。
合理的解决方案是让null
也可以为空,如果Rank
为null
,则将其设置为Value
。
编辑:也请在评论中阅读@ GiladGreen的建议,以提高绩效。
编辑2:这里是基于字典的版本,可以在大型集合中表现良好。首先,我们定义一个函数来计算排名并将它们存储在字典中:
null
然后我们使用该函数生成private static Dictionary<MovieRankData, int> CalculateRanks(
List<MovieRankData> movieRankData,
Func<MovieRankData, double?> orderKeySelector)
{
return movieRankData
.OrderByDescending(orderKeySelector)
.Select((item, index) => new { item, index })
.ToDictionary(e => e.item, e => e.index + 1);
}
和byTotalViews
词典:
byTotalMoney
答案 3 :(得分:0)
在下文中,如果index
1最低,则rank
+ 1是rank
的值。
movieRankData.OrderBy(t => t.TotalViews.Value);
movieRankData.OrderBy(t => t.TotalMoney.Value);
或者rank
1是最高的:
movieRankData.OrderByDesc(t => t.TotalViews.Value);
movieRankData.OrderByDesc(t => t.TotalMoney.Value);
在任何一种情况下,您都可以使用for loop
将rank
设置为index
+ 1中object
的{{1}}
collection
答案 4 :(得分:0)
在orderby结果上使用带有索引的select重载,您只需循环每个集合一次。为了可维护性,可以使用辅助函数来设置特定属性的等级。这将带来额外的好处,如果添加额外的排名,可以为该排名添加函数调用:
void SetRank(IEnumerable<MovieRankData> list, Func<MovieRankData,RankData> prop)
{
foreach(var ri in list.Select(prop).OrderByDescending(r=>r.Value).Select((r,i)=> new{r,i}))
ri.r.Rank = ri.i + 1;
}
用法:
SetRank(movieRankData, m => m.TotalViews);
SetRank(movieRankData, m => m.TotalMoney);
(或者保持要在lambda数组中调用的属性并为每个调用函数)