Flipping a Boolean When All Values Meet a Condition with LINQ

Every once in a while, a seemingly simple coding problem can throw a chicken-or-egg situation at you. You’ve built complex software in mere hours, how can a for loop throw you off like this…!?!?

Don’t worry, it happens…

Here’s a quick example: you want to loop through a collection and flip a boolean if a condition is met by all of the elements in the collection.

So you try something like this:

public class Vector {
    public float DX { get; set; }
    public float DY { get; set; }
}
...
[Fact]
public void Flip_bool_on_collection_condition() {

    var vectors = new List<Vector>() {
        new Vector() {DX = 0.0f, DY = 0.0f},
        new Vector() {DX = 1.0f, DY = 0.0f},
        new Vector() {DX = 0.0f, DY = 0.0f},
        new Vector() {DX = 1.0f, DY = 0.0f},
        new Vector() {DX = 1.0f, DY = 0.0f}
    };

    bool myBool = false;
    for (int i = 0; i < vectors.Count; i++) {
        if (vectors[i].DX > 0) {
            myBool = true;
        }
        else {
            myBool = false;
        }
    }
    _output.WriteLine("All vectors.DX > 0? " + myBool);
}
All vectors.DX > 0? True

myBool is going to flick off and on until it reaches the last element. All vectors.DX > 0 is not True it’s a lie!

It was probably obvious to you while you read the code, but what if you’ve been in a state of flow for 2 hours?… Goodbye flow…

Fight for your Flow

There are a few ways to make the code work the way you want it to, but there’s one that will help you stay in your flow because of its readability and brevity:

[Fact]
public void Flip_bool_on_collection_condition_1() {

    var vectors = new List<Vector>() {
        new Vector() {DX = 0.0f, DY = 0.0f},
        new Vector() {DX = 1.0f, DY = 0.0f},
        new Vector() {DX = 0.0f, DY = 0.0f},
        new Vector() {DX = 1.0f, DY = 0.0f},
        new Vector() {DX = 1.0f, DY = 0.0f}
    };

    bool myBool = vectors.All(vector => vector.DX > 0);

    _output.WriteLine("All vectors.DX > 0? " + myBool);
}
All vectors.DX > 0? False

So:

[Fact]
public void Flip_bool_on_collection_condition() {

    var vectors = new List<Vector>() {
        new Vector() {DX = 1.0f, DY = 0.0f},
        new Vector() {DX = 1.0f, DY = 0.0f},
        new Vector() {DX = 1.0f, DY = 0.0f},
        new Vector() {DX = 1.0f, DY = 0.0f},
        new Vector() {DX = 1.0f, DY = 0.0f}
    };

    bool myBool = vectors.All(vector => vector.DX > 0);

    _output.WriteLine("All vectors.DX > 0? " + myBool);
}
All vectors.DX > 0? True

We aren’t being lied to anymore. Enumerable.All is used to determine “whether all elements in a sequence satisfy a condition.”

Sure, we could have set myBool to true and flipped it to false when we found an element that didn’t satisfy the condition. We could have also used break to break out of the loop when we found the element.

I think the LINQ way is fine for our purpose here. It’s simple and clear what our intention is. It’s important to keep in mind that there are performance implications with LINQ. In a tight-loop situation, we want to avoid instantiating objects and excessive string manipulation inside the loop.

We also want to be aware that .All() evaluates to true for empty lists:

[Fact]
public void Flip_bool_on_collection_condition() {

    var vectors = new List<Vector>() {
    };

    bool myBool = vectors.All(vector => vector.DX > 0);

    _output.WriteLine("All vectors.DX > 0? " + myBool);
}
All vectors.DX > 0? True

So we can add a .Any() to make sure there are elements in the list before we evaluate the condition:

[Fact]
public void Flip_bool_on_collection_condition() {

    var vectors = new List() {
    };

    bool myBool = vectors.Any() && vectors.All(vector => vector.DX > 0);

    _output.WriteLine("All vectors.DX > 0? " + myBool);
}
All vectors.DX > 0? False

Summary

We were perfectly fine in our flow when suddenly a little problem/solution tripped us up when we wrote our first pass on it. All we wanted to do was flip a bool if all the elements in a collection met a condition.

Luckily, LINQ .All() rescued us with a simple and clear solution to meet the requirement. We have to be aware of the performance implications if we are doing something more complex inside the LINQ statement so that memory doesn’t get away from us. We also need to be aware that .All() evaluates to true for empty lists. Adding .Any() to make sure there are elements in the list covers that case.

As always, thanks for reading! Make sure you sign up for my newsletter so you get these sent right to your inbox!


Related Posts:

Tweet
comments powered by Disqus