using System.Collections.Generic;
using System.Collections.Immutable;
public static void Main()
const string input = "aaaabbbcca";
var imperativeCounts = GetLettersCount(input);
var functionalCounts = GetLettersCountFunctional(input);
Console.WriteLine($"Imperative: [{string.Join(", ", imperativeCounts)}]");
Console.WriteLine($"Functional: [{string.Join(", ", functionalCounts)}]");
private static ICollection<(char letter, int count)> GetLettersCount(string input)
var output = new List<LetterCount>();
foreach (var @char in input)
var lastOutput = output.LastOrDefault();
lastOutput.Letter != @char)
.Select(letterCount => (letterCount.Letter, letterCount.Count))
private sealed class LetterCount
public LetterCount(char letter)
public char Letter { get; }
public int Count { get; set; }
private static ICollection<(char letter, int count)> GetLettersCountFunctional(string input)
.Select(slice => (slice[0], slice.Length))
public static class StringCountStringExtensions
public static int IndexOf(this string input, Func<char, bool> predicate)
.Select((@char, index) => (@char, index))
.First(pair => predicate(pair.@char))
catch (InvalidOperationException)
public static (string repeatedBlock, string remaining) GetRepeatedBlock(this string input)
var nextCharIndex = input
.IndexOf(@char => @char != input[0]);
var sliceLength = nextCharIndex == -1
input.Substring(0, sliceLength),
input.Substring(sliceLength));
public static ICollection<string> SliceRepeatedLetters(this string input)
ICollection<string> DoSlice(string remainingInput, ImmutableList<string> results)
if (remainingInput.Length == 0) return results;
var (block, remaining) = remainingInput.GetRepeatedBlock();
return DoSlice(remaining, results.Add(block));
return DoSlice(input, ImmutableList<string>.Empty);