Skip to content

Instantly share code, notes, and snippets.

@savaged
Last active February 22, 2023 08:27
Show Gist options
  • Save savaged/d229dcefda0659d60f54ea4468f6cfea to your computer and use it in GitHub Desktop.
Save savaged/d229dcefda0659d60f54ea4468f6cfea to your computer and use it in GitHub Desktop.
FP styled C# example inspired by Isaac Abraham's book Programming With F#
using Unit = System.ValueTuple;
_ = App.Run(Console.WriteLine);
static class App
{
public static Unit Run(Action<string> outputter)
{
outputter(GetAwayWins().ToSummary());
return Unit.Create();
}
static IList<FootballTeamWinSummary> GetAwayWins() =>
SampleDataset.Results.GetAwayWins();
}
static class Extensions
{
public static IList<FootballTeamWinSummary> GetAwayWins(
this IEnumerable<FootballGameResult> self) =>
self.Where(r => r.IsAwayWin())
.GroupBy(
r => r.AwayTeamResult.TeamName,
r => r,
(t, g) => new FootballTeamWinSummary(t, g.Count()))
.OrderBy(s => s.TeamName)
.ToList();
public static bool IsAwayWin(this FootballGameResult self) =>
self.AwayTeamResult.Goals > self.HomeTeamResult.Goals;
public static string ToSummary(this FootballTeamResult self) =>
$"{self.TeamName}: {self.Goals}";
public static string ToSummary(this IList<FootballTeamWinSummary> self) =>
SummariseWins(self);
private static string SummariseWins(
IList<FootballTeamWinSummary> w, int i = 0, string s = "")
{
if (w.TryGetNonEnumeratedCount(out int count) && count <= i)
return s;
s += $"{w[i].TeamName}: {w[i].Wins}{Environment.NewLine}";
return SummariseWins(w, ++i, s);
}
}
record struct FootballTeamWinSummary(string TeamName, int Wins);
record struct FootballTeamResult(string TeamName, int Goals);
record struct FootballGameResult(
FootballTeamResult HomeTeamResult, FootballTeamResult AwayTeamResult);
static class SampleDataset
{
public static IEnumerable<FootballGameResult> Results =>
new List<FootballGameResult>
{
new FootballGameResult(
new FootballTeamResult("Middlesbrough", 1),
new FootballTeamResult("Hull City", 2)),
new FootballGameResult(
new FootballTeamResult("Middlesbrough", 1),
new FootballTeamResult("Huddersfield Town", 3)),
new FootballGameResult(
new FootballTeamResult("Huddersfield Town", 3),
new FootballTeamResult("Hull City", 1)),
new FootballGameResult(
new FootballTeamResult("Huddersfield Town", 2),
new FootballTeamResult("Middlesbrough", 1)),
new FootballGameResult(
new FootballTeamResult("Hull City", 4),
new FootballTeamResult("Middlesbrough", 2)),
new FootballGameResult(
new FootballTeamResult("Hull City", 1),
new FootballTeamResult("Huddersfield Town", 2)),
};
}
@savaged
Copy link
Author

savaged commented Feb 21, 2023

F# version (please remember I'm an FP noob)

type FootballTeamResult =
    {
        TeamName : string
        Goals : int
    }

type FootballGameResult =
    {
        HomeTeamResult : FootballTeamResult
        AwayTeamResult : FootballTeamResult
    }

let createFTR (n, g) =
    {
        TeamName = n
        Goals = g
    }

let createFGR (hr, ar) =
    {
        HomeTeamResult = hr
        AwayTeamResult = ar
    }

let sampleDataSet = 
    [
        createFGR (createFTR ("Middlesbrough", 1), createFTR ("Hull City", 2))
        createFGR (createFTR ("Middlesbrough", 1), createFTR ("Huddersfield Town", 3))
        createFGR (createFTR ("Huddersfield Town", 3), createFTR ("Hull City", 1))
        createFGR (createFTR ("Huddersfield Town", 2), createFTR ("Middlesbrough", 1))
        createFGR (createFTR ("Hull City", 4), createFTR ("Middlesbrough", 2))
        createFGR (createFTR ("Hull City", 1), createFTR ("Huddersfield Town", 2))
    ]


let isAwayWin result =
    result.AwayTeamResult.Goals > result.HomeTeamResult.Goals

let toSummary (team, wins) =
    String.concat ": " [ team; string wins ]

let outputter summaries =
    summaries
    |> List.iter (fun s -> printfn "%s" s)
    ()

let awayWins =
    sampleDataSet
    |> List.filter isAwayWin
    |> List.countBy(fun result -> result.AwayTeamResult.TeamName)
    |> List.sortByDescending(fun (_, aw) -> aw)
    |> List.map (fun aw -> aw |> toSummary)

let run =
    outputter awayWins
    ()

run

@savaged
Copy link
Author

savaged commented Feb 22, 2023

SQL (MySQL 8.0) version gives all the clues to how simple a declarative style can be

DROP TEMPORARY TABLE IF EXISTS tmp;

CREATE TEMPORARY TABLE tmp
(
    hometeam VARCHAR(50) NOT NULL,
    homegoals INT NOT NULL,
    awayteam VARCHAR(50) NOT NULL,
    awaygoals INT NOT NULL
);

INSERT INTO tmp
    (hometeam, homegoals, awayteam, awaygoals)
VALUES
    ('Middlesbrough', 1, 'Hull City', 2),
    ('Middlesbrough', 1, 'Huddersfield Town', 3),
    ('Huddersfield Town', 3, 'Hull City', 1),
    ('Huddersfield Town', 2, 'Middlesbrough', 1),
    ('Hull City', 4, 'Middlesbrough', 2),
    ('Hull City', 1, 'Huddersfield Town', 2);

SELECT
    awayteam,
    COUNT(*) AS wins
FROM tmp
WHERE awaygoals > homegoals
GROUP BY awayteam
ORDER BY wins;

DROP TEMPORARY TABLE tmp;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment