Write Clean Code Without Loops
Tags: csharp, programming, functional, codequality
Published: 2020-08-30 17:26:27

![](https://cdn-images-1.medium.com/max/1000/0*Q010nh59YRrvNBXp)
<figcaption>
Loops are messy - Photo by 
<a href="https://unsplash.com/@danielfazio">
Daniel Fazio
</a> 
on
<a href="https://unsplash.com">
Unsplash
</a>
</figcaption>

A few years ago, I participated in a public workshop organized by Wix. We practiced TDD by implementing the [Game Of Life kata](https://codingdojo.org/kata/GameOfLife/) with different constraints. One constraint was to **write code without loops**. I was shocked and stuck, I could not grasp how it is possible. Eventually, I figured it out: **map, filter, and reduce!** I knew this concept before but it wasn’t part of my daily programming (back then I wrote in C++). When I returned to C#, I started using this concept more and more and understood its benefits.

In this post, I show how to write clean code by replacing foreach loops with map, filter, and reduce. **This approach is relevant to all languages.** 

# TL;DR
-   Declarative code written with map, filter, and reduce is more readable and concise than imperative code written with foreach loops.
-   Reduce boilerplate code and avoid stupid bugs.
-   Achieve clean code by separating between the logic of which items to operate on and the operation itself.
-   Code is better in terms of the Single Responsibility Principle

----------
# What Are Map, Filter & Reduce?

Map, Filter and Reduce are functions that work on collections. They receive a collection and a function and return a new collection based on the result of the function.

-   Map — maps each object in a collection into a new object. It returns a new collection with the same size as the given collection.
-   Filter — filters a collection according to a given condition. The new collection includes only the elements that pass the condition.
-   Reduce — reduces a collection down to a single value.

{% twitter 741089564329054208 %}

----------

I wrote the code examples with [C# LINQ](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/linq-to-objects) and simplified them for the post. In real-life systems the code is not clearly divided into functions, instead, most of the code is inside the loop.

## Replace foreach with map

Sometimes we see code with this structure:
{% gist https://gist.github.com/limonana/f87fc4806043635fc1dd0798ad425377 %}

Now, let’s rewrite it using [LINQ Select](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.select?view=netcore-3.1#System_Linq_Enumerable_Select__2_System_Collections_Generic_IEnumerable___0__System_Func___0___1__) which is the LINQ version of map :
{% gist https://gist.github.com/limonana/371ccf693786059ba00ed0760e44ecee %}
\* I call `ToList()` because [LINQ is lazy](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/deferred-execution-and-lazy-evaluation-in-linq-to-xml). 

This code is shorter — a single line!

## Replace if with filter

Sometimes we see code with these structures:
{% gist https://gist.github.com/limonana/7ebb4e8a9791d7228887adcf5d751374 %}

The loops contain two logics:

1.  Which items to operate on.
2.  The operation itself.

We can replace **if** with [**LINQ Where**](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.where), which is the LINQ version of filter. When we replace `if (someCondition) continue;` with `Where` we write the negative form of the condition.
{% gist https://gist.github.com/limonana/88826d04fa0168f0581259734968e790 %}
The two logics are now separated. Each logic has a single responsibility.

## Replace Boilerplate Code with Any

Sometimes we see code with these structures:
{% gist https://gist.github.com/limonana/3f25a646a0b327765ce4de22ba1de0ab %}

These are patterns for “return true if **at least one** of the items **returns true** for a given condition” aka the **Any function**. [LINQ Any](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.any) determines whether any element of a sequence exists or satisfies a condition. Any is a specific form of a reduce function.

Even though we are used to writing these boilerplate patterns, there is always a chance to cause bugs by doing a wrong initialization or confusing between the AND operator and the OR operator.

Let’s rewrite the code by using [LINQ Any](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.any):
{% gist https://gist.github.com/limonana/32eda989131cf971fe1fa21ec688c26c %}

This code is clearer, shorter (single line!), and **emphasizes the code’s meaning**. It is also more efficient, `Any` stops as soon as the result can be determined.

## Replace Boilerplate Code with All

Sometimes we see code with these structures:
{% gist https://gist.github.com/limonana/fe96181c4dfb98c98aa008ad1a466d77 %}

These patterns mean “return true **if _ALL_** the items **return true** for a given condition” aka the **All function**. [LINQ All](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.all?view=netcore-3.1) Determines whether all elements of a sequence satisfy a condition. All is a specific form of a reduce function.

Similar to `Any`, there is a chance to miswrite this boilerplate code and to cause bugs.

Let’s rewrite the code by using [LINQ All](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.all).

{% gist https://gist.github.com/limonana/93638d76bfc538f821610cb896673d86 %}

This code is clearer, shorter (single line!), and **emphasizes the code’s meaning**.

---

# Code Evolution

In this section, I show how writing code with foreach loops can lead to spaghetti code while writing code with map, filter, and reduce results in clear, readable, and maintainable code.

Let’s imagine we are building a fun system for organizing fun days. 💃 🎉 🍻
Each employee should receive a welcome email for the fun day they registered to. Here is the code:

{% gist https://gist.github.com/limonana/07f734c4f0841b5f461862a28ca42fff %}

Now we discover that some people forgot to register. They were unaware of the fun days 😲. What should we do? Let’s send them a reminder email to register for a fun day.

Sending a reminder also operates only on current employees. The current code already finds the fun day for each employee, so this functionality is quite simple to add.

{% gist https://gist.github.com/limonana/ae44ce967cc5e0d475c595180a8e69eb %}

Great! Now people will not miss out on the fun. 😃

Now we discover that some people registered for a fun day but forgot to register for the optional activities. Let’s send them a reminder to register for the optional activities.

The current code already finds the fun day for each employee, so let’s use it to add the new functionality.

{% gist https://gist.github.com/limonana/d8b7856e1d5b49960d536a0b628881d9 %}

Yammi 😋 we got spaghetti…  code! 🤦‍♀

![](https://cdn-images-1.medium.com/max/1000/1*0Zbr6IoIzamHnMDNK4csuw.jpeg)
<figcaption>
<a href="https://www.freepik.com/vectors/computer">
Computer vector created by freepik — www.freepik.com
</a>
</figcaption>

### The problems in the above code are:

-   Many logics mixed together aka violates the Single Responsibility Principle.
-   In each row, `employee` passed different filters so it is hard to grasp which filters it passed.
-   Quite nested code (4 nesting levels)

## Let’s Try Again — Rewrite 💣💣💣

Now we start from scratch, writing the code with LINQ.

Let’s rewrite the first feature —  welcome email.

The original code:
{% gist https://gist.github.com/limonana/07f734c4f0841b5f461862a28ca42fff %}
Rewrite with LINQ:
{% gist https://gist.github.com/limonana/3bc5ac2350805aacf892f196f9488178 %}

### What are the differences?

-   In both implementations `SendWelcomeEmail` operates only on employes who are registered for a fun day. In the LINQ implementation, **I separated the logic of whom to operates and the operation itself.**

-   I separated the filtering of current employees from the employes' loop. The filtering is done in a separate function called `GetCurrentEmployees` which emphasizes the code's meaning. I replaced `if (IsPastEmployee(employee)) continue;` and `if (IsFutureEmployee(employee)) continue;` with `Where`.

-   Instead of searching the fun day inside the employes' loop, I created a dictionary between an employee and a fun day called `employee2Funday`. I replaced `if (funday.IsEmployeeRegistered(employee.Id))` with `employee2Funday.Where`.

-   We assume that an employee is registered to a single fun day. The original code implicitly chooses the first fun day by using the `break` statement. The new implementation uses LINQ `FirstOrDefault` which is more explicit. `FirstOrDefault` is a specific form of a reduce function.

----------

Now let's implement the next feature — a reminder email to register for a fun day for employees who didn’t register for any fun day.

The original code:
{% gist https://gist.github.com/limonana/ae44ce967cc5e0d475c595180a8e69eb %}

Rewrite with LINQ:
{% gist https://gist.github.com/limonana/ad86fa71b731bdfd62fefac92dc82b47 %}


### What are the differences?

-   I separated the logic of whom to operates and the operation itself. The function `SendUnregisteredToFunday` operates only on employes who are unregistered for any fun day.

-   In the original code, **the two functionalities are tangled together**. In the rewritten code **each functionality has its own method**. This is possible because the data this functionality operates on, aka the employees and whether they are registered to a fun day, exists outside of the first functionality implementation.

----------

Now let's implement the last feature — a reminder email for registering to the optional activities.

The original code:
{% gist https://gist.github.com/limonana/d8b7856e1d5b49960d536a0b628881d9 %}

Rewrite with LINQ:
{% gist https://gist.github.com/limonana/aff288283a5f9a16769c4ee1e6f2faac %}

### What are the differences?

-   Again, I separated between the logic of whom to operates and the operation itself. The function `SendEmailAboutOptionalActivities` operates only on employees who are registered for a fun day but unregistered to any optional activity.

-   In the original code, **the functionalities are tangled together**. In the rewritten code **each functionality has its own method**. This is possible because the data this functionality operates on, aka the employees and whether they are registered to a fun day, exists outside of the other functionalities implementation.

-   `IsUnregisteredToOptionalActivities` is implemented with `Where` and `Any` instead of boilerplate code. It is shorter and clearer.

----------

This paradigm is part of the **functional programming paradigm** which has some concepts that help write clean, readable, and less error-prone code. Some of these concepts are immutable classes, avoid variables reassignment, and avoid shared state.

I currently write code in [Scala]
(https://www.youtube.com/watch?v=GeG9VPmUa28) and I barely see foreach loops. Out of curiosity, I gathered some statistics from the codebase I work on: map has ~3500 occurrences, filter has ~3000 occurrences, and foreach has only ~500 occurrences.