5

現在、これは私が興味を持っていることであり、作業中のコードはありませんが、これをどのように達成できるか疑問に思っています...

たとえば、世界中のすべてのサッカー チームの結果を追跡するアプリケーションがあるとします。私ができるようにしたいのは、特定のチームの最長の「連勝」を特定することです.

次のようなデータテーブルがある可能性が最も高いと思います。

  • MatchDate日時
  • チームA文字列
  • チームB文字列
  • TeamAGoals int
  • TeamBGoals int

たとえば、私がやりたいことは、最長の連勝を見つけることでありTeamA = "My Team"、明らかにこれはTeamAGoalsよりも大きくなければならないことを意味しTeamBGoalsます。

私が言ったように、これはすべて例にすぎません。このような場合は、別の DB 設計の方がよい場合があります。しかし、根本的な問題は、一致する結果の最長ストリーク/ランをどのように計算するかです。

4

4 に答える 4

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);
    }
于 2015-03-11T08:13:12.493 に答える
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 のMaxBy;を使用します。それらをすべて並べ替えたい場合は、 を使用できますOrderByDescending(s => s.StreakLength)

または、これを 1 回のパスで実行したい場合で、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または以前と同じように。

于 2012-10-16T11:11:01.480 に答える
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());
于 2012-10-16T12:25:52.843 に答える
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;
}

そのまま使用することも、メソッドに抽出することも、結果の最長シーケンスを計算するためのIEnumerable 拡張を作成することもできます。

于 2012-10-16T12:11:47.883 に答える