Last active
March 8, 2016 07:31
Paste into a C# Interactive window and press enter.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.IO; | |
using static LifeBoundary; | |
/* | |
* The idea is to use a stack to keep track of how many presidents | |
* are alive at a given time. | |
* | |
* When a president is born we push a birth year to the stack, while popping a | |
* year for every death. | |
* | |
* Thus, if we check the length of the stack every time we are about to pop a year, | |
* we have the number of living presidents from the year in the first element of | |
* the stack until the death year that caused the popping event. | |
*/ | |
/// <summary> | |
/// POCO that holds a year and the indication whether | |
/// it is a birth year or a death year | |
/// </summary> | |
public sealed class LifeBoundary | |
{ | |
public enum YearKind { Open, Close }; | |
public YearKind Kind { get; private set; } | |
public int Year { get; private set; } | |
public LifeBoundary(YearKind kind, int year) { Kind = kind; Year = year; } | |
public override string ToString() => $"{Year} {(Kind == YearKind.Close ? "✟" : "🎂")}"; | |
} | |
/// <summary> | |
/// An object meant to keep track of the number of presidents | |
/// alive at a given period of years | |
/// </summary> | |
public sealed class Range | |
{ | |
public int FromYear { get; private set; } | |
public int ToYear { get; private set; } | |
public int LivingPresidents { get; private set; } | |
public int Length => ToYear - FromYear; | |
public Range(int start, int stop, int presidentsCount) { FromYear = start; ToYear = stop; LivingPresidents = presidentsCount;} | |
public override string ToString() => $"{LivingPresidents} alive in {FromYear} - {ToYear}"; | |
} | |
var stack = new Stack<LifeBoundary>(); | |
File.ReadAllLines(@"Presidents.csv") | |
.Skip(1) | |
.Select(line => line.Split(',').Select(field => field.Trim()).ToArray()) | |
.SelectMany(record => new[] { | |
new LifeBoundary (YearKind.Open,DateTime.Parse(record[1]).Year), | |
new LifeBoundary (YearKind.Close, string.IsNullOrEmpty(record[3]) | |
? DateTime.Today.Year | |
: DateTime.Parse(record[3]).Year)}) | |
.OrderBy(item => item.Year) | |
.ThenBy(item => item.Kind) | |
// Sorting according to kind prevents a death from neutralizing a birth in | |
// the same year, as long as the birth year comes first | |
.Select(item => | |
{ | |
switch (item.Kind) | |
{ | |
case YearKind.Open: | |
stack.Push(item); | |
break; | |
case YearKind.Close: | |
var currentRange = new Range(stack.First().Year, item.Year, stack.Count); | |
stack.Pop(); | |
return currentRange; | |
} | |
return new Range(0, 0, 0); | |
}) | |
.GroupBy(range=> range.LivingPresidents ) | |
.OrderBy(group=> group.Key) | |
.Last() | |
.Select(line => string.Join(", ", line)) | |
.ToList() | |
.ForEach(Console.WriteLine); | |
;; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment