Archive for the ‘functional’ tag
Functional Collection Parameters in C#
While talking through my understanding of the Select method which can be applied to collections in C# with a colleague, it became clear that C# doesn’t seem to use the same names for these type of operations as are used in the world of functional programming.
Coincidentally on the same day I came across Bill Six’s post about using functional collection parameters in Ruby, so I thought I’d see what the equivalent operations are in C#.
Map
Map evaluates a high order function on all the elements in a collection and then returns a new collection containing the results of the function evaluation.
In C# we can use the ‘Select’ method. For example, to capitalise all the items in a list:
1 2 | var someValues = new List<string> {"mark", "sydney", "sunny"}; var upperCaseValues = someValues.Select(item => item.ToUpper()); |
The responsibility for iterating the collection has been taken away from me and I can just focus on what I want to do with the collection rather than worrying too much about the details. A more declarative approach.
If I want to see the results of that operation I just do the following:
1 | upperCaseValues.ForEach(Console.WriteLine); |
MARK SYDNEY SUNNY
Filter
Filter applies a predicate against all of the elements in a collection and then returns a collection of elements which matched the predicate.
Conveniently there is actually a built in delegate called ‘Predicate’ which when combined with the ‘FindAll’ method can be used to solve this problem.
1 2 3 | var someValues = new List<string> {"mark", "sydney", "sunny"}; var valuesWithSIn = someValues.FindAll(item => item.Contains("s")); valuesWithSIn.ForEach(Console.WriteLine); |
sydney sunny
If we just want the first value in the collection that matches the predicate we would use the ‘Find’ method instead:
1 2 3 | var someValues = new List<string> {"mark", "sydney", "sunny"}; var valueWithSIn = someValues.Find(item => item.Contains("s")); Console.WriteLine(valueWithSIn); |
sydney
Reduce
Reduce applies a high order function against all the elements in a collection and then returns a single result.
We can use the ‘Aggregate’ method to achieve this:
1 2 3 | var someValues = new List<string> {"mark", "sydney", "sunny"}; var valuesConcatenated = someValues.Aggregate("",(accumulator, item) => accumulator + item); Console.WriteLine(valuesConcatenated); |
marksydneysunny
The “” passed in as the first parameter to ‘Aggregate’ is the initial value for the accumulator.
Combining expressions
As with Ruby we can chain these expressions together to return even greater results.
For example, to get concatenate all the items which contain an s we would do the following:
1 2 3 4 | var someValues = new List<string> {"mark", "sydney", "sunny"}; var valuesConcatenated = someValues.FindAll(item => item.Contains("s")) .Aggregate("",(accumulator, item) => accumulator + item); Console.WriteLine(valuesConcatenated); |
sydneysunny
Overall
I really like having this high order functions available to us – it has taken away the need to write some of the most boring code that we used to have to write and makes our code more concise and easier to read.
C#’s Lambda ForEach: Only on Lists?
One of my favourite things introduced into C# recently is the new ForEach method which can be applied to (apparently only!) lists.
Last week we had a situation where we wanted to make use of the ForEach method on an IDictionary which we were using to store a collection of Selenium clients.
1 | IDictionary<string, ISelenium> seleniumClients = new Dictionary<string, ISelenium>(); |
We wanted to write a piece of code to exit all of the clients when our tests had completed. We thought the following would do the trick:
1 | seleniumClients.Values.ForEach(client => client.Stop()); |
The problem is that code doesn’t actually compile!
‘seleniumClients.Values’ returns an ICollection which extends IEnumerable so we thought ForEach should be available.
We eventually got around the problem by putting the collection into a list and then applying the ForEach method but it seems like there should be a better way to do this.
1 | new List<ISelenium>(seleniumClients.Values).ForEach(client => client.Stop()); |
Is there?