C#: Refactoring to functional collection parameters
I wrote about a month or so ago about the functional collection parameters now available in C# and certainly one of the most fun refactorings for me is trying to get code written using a for loop into a state where it is using one of these.
With a bit of help from my colleague James Crisp, these are some of the most common refactorings that I have come across so far.
There's a little bit of repetition with regards to my previous post but I think it's interesting to see the procedural approach to solving this problems versus the functional approach.
For the sake of the examples, the following classes are common:
public class Foo { private String bar; private String baz; private readonly bool specialFlag; public Foo(string bar, string baz, bool specialFlag) { this.bar = bar; this.baz = baz; this.specialFlag = specialFlag; } public bool HasSpecialFlag() { return specialFlag; } } public class NewFoo { public NewFoo(Foo foo) { } } public class NumericFoo { public int Value { get; set; } }
Going from one collection to another conditionally
var foos = new List<Foo> {new Foo("bar1", "baz2", true), new Foo("bar2", "baz2", false)}.Where(foo => foo.HasSpecialFlag()); var newFoos = new List<NewFoo>(); foreach (var foo in foos) { newFoos.Add(new NewFoo(foo)); }
We started off well using the 'Where' method to reduce the original list but we can do even better!
var foos = new List<Foo> {new Foo("bar1", "baz2", true), new Foo("bar2", "baz2", false)}; foos.Where(foo => foo.HasSpecialFlag()) .Select(foo => new NewFoo(foo));
Not significantly less code but it removes the need for more state in our code and makes the code more expressive at the same time which can only be a good thing.
Returning just one result
I came across some code last week which was iterating through a collection and then returning the first item which matched a certain criteria.
This would be useful if we wanted to get the first Foo object which has the special flag set for example.
private Foo GetSpecialFoo(List<Foo> foos) { foreach (var foo in foos) { if(foo.HasSpecialFlag()) { return foo; } } return null; }
My initial thoughts on coming upon this code were that it should be possible to get rid of the loop but I wasn't sure how to do the return. Luckily the 'First' method solves this problem.
private Foo GetSpecialFoo(List<Foo> foos) { return foos.Where(foo => foo.HasSpecialFlag()).First(); }
Accumulating some values
Adding values in collections together is surely one of the most common operations we do and functional collection parameters can help us here too.
var numericFoos = new List<NumericFoo> {new NumericFoo {Value = 2}, new NumericFoo {Value = 5}}; int total = 0; foreach (var numericFoo in numericFoos) { total += numericFoo.Value; }
Introducing a functional approach here gives us the following code:
var numericFoos = new List<NumericFoo> {new NumericFoo {Value = 2}, new NumericFoo {Value = 5}}; int total = numericFoos.Sum(foo => foo.Value);
Good post Mark, I too enjoy these kinds of refactorings (careful though, it can be addicting)!
Anyway, in your GetSpecialFoo example, you can simplify by passing your predicate (Foo => foo.HasSpecialFlag()) into the First method, eliminating the call to Where. Also, First will throw an exception if it never finds any items matching the predicate, so you should really use FirstOrDefault to match the functionality of the original method.
Robin Clowers
3 Feb 09 at 10:05 am
Ah cool, didn't realise that!
Thanks for the FirstOrDefault tip as well – was wondering how to do that.
Mark Needham
3 Feb 09 at 10:46 am
[...] C#: Refactoring to functional collection parameters – Mark Needham shows some of the nice collection refactorings that are possible in C#3 [...]
Reflective Perspective - Chris Alcock » The Morning Brew #278
3 Feb 09 at 6:16 pm
[...] C#: Refactoring to Functional Collection Parameters (Mark Needham) [...]
Dew Drop - February 3, 2009 | Alvin Ashcraft's Morning Dew
4 Feb 09 at 12:54 am
[...] written previously about the ways I've been making use of functional collection parameters in my code but what I hadn't really considered was that the way of [...]
Functional Collection Parameters: A different way of thinking about collections at Mark Needham
19 Jun 09 at 12:13 am