目前,这只是我很好奇的事情,我没有任何我正在研究的代码,但我想知道如何实现这一目标......
让我们举例来说,我有一个应用程序可以追踪世界上所有足球队的成绩。我希望能够确定任何特定球队最长的“胜利”连胜。
我想我很可能会有这样的数据表:
所以我想要做的就是找到最长的连胜点TeamA = "My Team"
,显然这意味着TeamAGoals
必须大于TeamBGoals
。
正如我所说,这只是一个例子。对于这样的事情,对于不同的DB设计可能更好。但根本问题是如何计算匹配结果的最长条纹/连续运行。
答案 0 :(得分:4)
现在这是一个老问题,但我必须自己解决同样的问题,并认为人们可能对Rawling的LongestStreak扩展方法的完全LINQ实现感兴趣。这使用带有种子和结果选择器的Aggregate来运行列表。
public static int LongestStreak<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
return source.Aggregate(
new {Longest = 0, Current = 0},
(agg, element) => predicate(element) ?
new {Longest = Math.Max(agg.Longest, agg.Current + 1), Current = agg.Current + 1} :
new {agg.Longest, Current = 0},
agg => agg.Longest);
}
答案 1 :(得分:3)
没有开箱即用的LINQ方法来计算条纹,所以你需要一个自定义的LINQy方法,比如
public static int LongestStreak<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
int longestStreak = 0;
int currentStreak = 0;
foreach (TSource s in source)
{
if (predicate(s))
currentStreak++;
else
{
if (currentStreak > longestStreak) longestStreak = currentStreak;
currentStreak = 0;
}
}
if (currentStreak > longestStreak) longestStreak = currentStreak;
return longestStreak;
}
然后,要使用此功能,请先将每个“匹配结果”转换为一对“团队结果”。
var teamResults = matches.SelectMany(m => new[] {
new {
MatchDate = m.MatchDate,
Team = m.TeamA,
Won = m.TeamAGoals > m.TeamBGoals },
new {
MatchDate = m.MatchDate,
Team = m.TeamB,
Won = m.TeamBGoals > m.TeamAGoals }
});
按团队分组。
var groupedResults = teamResults.GroupBy(r => r.Team);
然后计算条纹。
var streaks = groupedResults.Select(g => new
{
Team = g.Key,
StreakLength = g
// unnecessary if the matches were ordered originally
.OrderBy(r => r.MatchDate)
.LongestStreak(r => r.Won)
});
如果您只想要最长条纹,请使用MoreLinq's MaxBy
;如果您想要全部订购,可以使用OrderByDescending(s => s.StreakLength)
。
或者,如果您想在一次通过中执行此操作,并假设matches
已经订购,请使用以下类
class StreakAggregator<TKey>
{
public Dictionary<TKey, int> Best = new Dictionary<TKey, int>();
public Dictionary<TKey, int> Current = new Dictionary<TKey, int>();
public StreakAggregator<TKey> UpdateWith(TKey key, bool success)
{
int c = 0;
Current.TryGetValue(key, out c);
if (success)
{
Current[key] = c + 1;
}
else
{
int b = 0;
Best.TryGetValue(key, out b);
if (c > b)
{
Best[key] = c;
}
Current[key] = 0;
}
return this;
}
public StreakAggregator<TKey> Finalise()
{
foreach (TKey k in Current.Keys.ToArray())
{
UpdateWith(k, false);
}
return this;
}
}
然后你可以做
var streaks = teamResults.Aggregate(
new StreakAggregator<string>(),
(a, r) => a.UpdateWith(r.Team, r.Won),
(a) => a.Finalise().Best.Select(kvp =>
new { Team = kvp.Key, StreakLength = kvp.Value }));
和OrderBy
或之前的任何内容。
答案 2 :(得分:2)
您可以通过单个查询获得团队的所有结果:
var results = from m in Matches
let homeMatch = m.TeamA == teamName
let awayMatch = m.TeamB == teamName
let hasWon = (homeMatch && m.TeamAGoals > m.TeamBGoals) ||
(awayMatch && m.TeamBGoals > m.TeamAGoals)
where homeMatch || awayMatch
orderby m.MatchDate
select hasWon;
然后只做简单的最长条纹计算:
int longestStreak = 0;
int currentStreak = 0;
foreach (var hasWon in results)
{
if (hasWon)
{
currentStreak++;
if (currentStreak > longestStreak)
longestStreak = currentStreak;
continue;
}
currentStreak = 0;
}
您可以按原样使用它,提取到方法,或c reate IEnumerable extension来计算结果中的最长序列。
答案 3 :(得分:2)
您可以使用string.Split
。像这样:
int longestStreak =
string.Concat(results.Select(r => (r.ours > r.theirs) ? "1" : "0"))
.Split(new[] { '0' })
.Max(s => s.Length);
或者,更好的是,为Split
创建一个IEnumerable<T>
扩展方法,以避免需要通过字符串,如下所示:
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> items, Predicate<T> p)
{
while (true)
{
items = items.SkipWhile(i => !p(i));
var trueItems = items.TakeWhile (i => p(i)).ToList();
if (trueItems.Count > 0)
{
yield return trueItems;
items = items.Skip(trueItems.Count);
}
else
{
break;
}
}
}
然后你可以这样做:
int longestStreak = results.Split(r => r.ours > r.theirs).Max(g => g.Count());