Skip to main content

Functional Patterns in C#: MapWhile

I had already solved Project Euler 23 in F# but wanted to translate my solution to C# for a friend. Those translations always tend to become very functional. This time around I needed a MapWhile method. This method should take a list and map a function over all elements as long as the while condition is true. This is actually very simple to implement, if you know how.

public static class Extensions
{
    public static IEnumerable<T> MapWhile<T>(this IEnumerable<T> list, Func<T, T> mapFunction, Func<T, bool> whilePredicate)
    {
        return MapWhile(list, (i, item) => mapFunction(item), (i, item) => whilePredicate(item));
    }

public static IEnumerable&lt;T&gt; MapWhile&lt;T&gt;(this IEnumerable&lt;T&gt; list, Func&lt;int, T, T&gt; mapFunction, Func&lt;int, T, bool&gt; whilePredicate)
{
    var enumerator = list.GetEnumerator();
    var index = 0;

    while (enumerator.MoveNext())
    {
        if (!whilePredicate(index, enumerator.Current))
        {
            break;
        }

        yield return mapFunction(index, enumerator.Current);
        index++;
    }
}

}

And some unit tests on usage.

[TestFixture]
public class MapWhileShould
{
    [Test]
    public void ReturnAtEndOfList()
    {
        /* Setup */
        var list = new[] { 1, 2, 3, 4 };

    /* Test */
    var result = list.MapWhile(x =&gt; x * 2, x =&gt; true);

    /* Assert */
    Assert.That(result.Sum(), Is.EqualTo(20));
}

[Test]
public void BreakWhenWhileConditionIsFalse()
{
    /* Setup */
    var list = new[] { 1, 2, 3, 4 };

    /* Test */
    var result = list.MapWhile(x =&gt; x * 2, x =&gt; x &lt; 3);

    /* Assert */
    Assert.That(result.Sum(), Is.EqualTo(6));
}

[Test]
public void ReturnEmptyListWhenWhileConditionIsFalseByDefault()
{
    /* Setup */
    var list = new[] { 1, 2, 3, 4 };

    /* Test */
    var result = list.MapWhile(x =&gt; x * 2, x =&gt; false);

    /* Assert */
    Assert.That(result.Count(), Is.EqualTo(0));
}

[Test]
public void WorkWithStrings()
{
    /* Setup */
    var list = new[] { &quot;Hello&quot;, &quot;World&quot;, &quot;Santa&quot;, &quot;Claudius&quot; };

    /* Test */
    var result = list.MapWhile((i, s) =&gt; i % 2 == 0 ? s.ToUpper() : s, (i, s) =&gt; true);

    /* Assert */
    Assert.That(string.Join(string.Empty, result), Is.EqualTo(&quot;HELLOWorldSANTAClaudius&quot;));
}

}

Usage in Project Euler 23

This is how I solved Project Euler 23 with the MapWhile function. This is not the most effective solution imaginable, but it is short and readable.

private const int AbundantSumMax = 28123;

public static void Main(string[] args) { // Just create this once var defaultRange = Enumerable.Range(1, AbundantSumMax);

// Returns true if n is abundant
Func&lt;int, bool&gt; isAbundant = n =&gt; Enumerable.Range(1, n / 2).Where(x =&gt; n % x == 0).Sum() &gt; n;

// Get all abundants up to 28123
var abundants = defaultRange.Where(isAbundant).ToList();

// Get abundant sums
var abundantSums = GetAbundantSums(abundants);

// Invert abundant sums
var result = defaultRange.Except(abundantSums).Sum();

// Print
Console.WriteLine(&quot;Result: {0}&quot;, result);

}

private static IEnumerable<int> GetAbundantSums(IList<int> abundants) { // Unique set of numbers var result = new HashSet<int>();

// The Tortoise and the Hare
foreach (var abundant in abundants)
foreach (var abundantSum in abundants.MapWhile(x =&gt; x + abundant, x =&gt; x &lt;= abundant))
{
    result.Add(abundantSum);
}

return result;

}

You can find my F# solution at bitbucket.

comments powered by Disqus