To be concrete: the input sequence contains all (ordered) historical versions of the same insurance policy object. From this sequence we need to filter all items for which a specific property has changed since the previous version. This could be implemented like:
1: Version previous = null;
2:
3: foreach (var version in Versions)
4: {
5: if (previous != null && previous.amount != version.amount)
6: {
7: // do something with this item
8: }
9: previous = version;
10: }
Using the pattern I described last week this could be written like
1: Versions.select((item, index) => new {item, index}).Skip(1)
2: .where(v=>Versions.ElementAt(v.index - 1).amount != v.item.amount)
The ElementAt() method however is not very efficient, because in most cases to find a single item it iterates the sequence from the begin to the requested item. Finding each item's preceding item by re-looping the sequence again can get you into serious performance issues when using larger sets.
Because I needed this kind of queries a lot in this project, I wanted to make then both easy to write and efficient in execution. So I wrote an extension method I called WithContext() that wraps each input item in a container object, along with some information about its context in the sequence. This allows for the code above to be written as:
1: Versions.WithContext().Where(v=>v.Previous != null && v.Previous.Amount != v.Current.Amount)
1: Versions.WithContext().Select(v.Next.StartDate - v.Current.StartDate)
1: public class ElementWithContext
2: {
3: public T Previous { get; private set; }
4: public T Current { get; private set; }
5: public T Next { get; private set; }
6: public int Index { get; private set; }
7:
8: public IEnumerablePreceding { get; private set; }
9: public IEnumerableFollowing { get; private set; }
10:
11: internal ElementWithContext(T previous, T current, T next,
12: int index, IEnumerablepreceding, IEnumerable following)
13: {
14: Current = current;
15: Previous = previous;
16: Next = next;
17: Index = index;
18: Preceding = preceding;
19: Following = following;
20: }
21: }
1: static public IEnumerable> WithContext (this IEnumerable source)
2: {
3: // initialize the previous and current item for the first source element
4: T previous = default(T);
5: T current = source.FirstOrDefault();
6: int index = 0;
7:
8: // Loop all 'Next' items
9: foreach (T next in source.Union(new[] { default(T) }).Skip(1))
10: {
11: yield return new ElementWithContext(previous, current, next,
12: index, source.Take(index), source.Skip(index + 1));
13:
14: previous = current;
15: current = next;
16: index++;
17: }
18: }
Oh, and thanks to Erno for pointing me to this Live Writer Add-In for the code snippets. I hope this helps reading these way to long code samples (sorry for that).
No comments:
Post a Comment