It's…Functional Friday!
So how would you write a function that, given a list of train cars, returns the food cars which are directly behind a hazmat car? (Maybe we don’t want to eat the food from ‘em.) At first we might try using LINQ, but then get stuck on how to compare each element with the next or previous one. So then we might go the for
loop route. No! Icky! (You do already prefer LINQ over a for
loop in a simple where/select scenario, right?) Wait, there’s still hope: Zip and Tail!
public IEnumerable<TrainCar> GetFoodCarsBehindHazmats(List<TrainCar> input)
{
return input
.Zip(input.Skip(1), (prev, next) => (prev, next))
.Where(pair => pair.Item1.IsFood && pair.Item2.IsHazmat)
.Select(pair => pair.Item1);
}
Zip “zips” two collections together, according to the function you give it. So in our case we’re saying to create a tuple of two train cars. Any FP-focused language such as F# has a Tail
method on a collection that returns all elements past the first. LINQ’s Tail
equivalent is Skip(1)
, so if you start adopting some FP in C# (you'll be happier!), Skip(1)
will be your friend. Also, FP languages’ “zip” function by default creates a tuple, but in C# we must specify explicitly. So by zipping a collection with its tail, each element has a train car and the train car after it.
This type of scenario does come up in real applications quite often, e.g. if you need to assume that a list is in a certain order (and you aren’t going to sort it for the caller every time), assert it on your ctor or function parameter! Cool?