Linq To Objects Extension: Full Outer Join

I need a report which shows a comparison of the sales of each service with the previous year. Some services might be new and they don't appear in the previous year, others might be put off and so there are no sales for them in the current year. There can be services sold in both periods.
public static IEnumerable<TResult> FullOuterJoin<TOuter, TInner, TKey, TResult>
(this IEnumerable<TOuter> outer,
IEnumerable<TInner> inner,
Func<TOuter, TKey> outerKeySelector,
Func<TInner, TKey> innerKeySelector,
Func<TOuter, TInner, TResult> resultSelector)
{
// left side
foreach (var current in outer)
{
if (!inner.Select(i => innerKeySelector(i)).Contains(outerKeySelector(current)))
yield return resultSelector(current, default(TInner));
}
// middle side
var innerJoin = outer.Join(inner, outerKeySelector, innerKeySelector, resultSelector);
foreach (var current in innerJoin)
yield return current;
//right side
foreach (var current in inner)
{
if (!outer.Select(i => outerKeySelector(i)).Contains(innerKeySelector(current)))
yield return resultSelector(default(TOuter), current);
}
yield break;
}
public static IEnumerable<TResult> FullOuterJoin<TOuter, TInner, TKey, TResult>
(this IEnumerable<TOuter> outer,
IEnumerable<TInner> inner,
Func<TOuter, TKey> outerKeySelector,
Func<TInner, TKey> innerKeySelector,
Func<TOuter, TInner, TResult> resultSelector,
IEqualityComparer<TKey> comparer)
{
foreach (var current in outer)
{
if (!inner.Select(i => innerKeySelector(i)).Contains(outerKeySelector(current), comparer))
yield return resultSelector(current, default(TInner));
}
var innerJoin = outer.Join(inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
foreach (var current in innerJoin)
yield return current;
foreach (var current in inner)
{
if (!outer.Select(i => outerKeySelector(i)).Contains(innerKeySelector(current), comparer))
yield return resultSelector(default(TOuter), current);
}
yield break;
}
An a sample:
var lastYearServices = new [] {
new {
Year = 2009,
Services = new []{ new { Service = "A", Amount = 2009200m },
new { Service = "B", Amount = 2009100m }}
},
new {
Year = 2010,
Services = new []{ new { Service = "X", Amount = 20102000m }}
},
new {
Year = 2011,
Services = new []{ new { Service = "AA", Amount = 2011200m }}
}
};
var currentYearServices = new [] {
new {
Year = 2010,
Services = new []{ new { Service = "B", Amount = 2010200m },
new { Service = "C", Amount = 2010100m }}
},
new {
Year = 2011,
Services = new []{ new { Service = "X", Amount = 2011200m }}
},
new {
Year = 2012,
Services = new []{ new { Service = "AA", Amount = 2012200m },
new { Service = "BB", Amount = 2012100m }}
}
};
var query = from l in lastYearServices
join c in currentYearServices on l.Year + 1 equals c.Year
select new {
Year = c.Year,
Services = l.Services.FullOuterJoin(c.Services, ls => ls.Service, cs => cs.Service, (ls, cs) => new {
LastYearAmount = ls != null ? ls.Amount : default(decimal?),
CurrentYearAmount = cs != null ? cs.Amount : default(decimal?),
Sevice = ls != null ? ls.Service : cs.Service
})
};
query.Dump();

Comments

Popular posts from this blog

IIS 7.5, HTTPS Bindings and ERR_CONNECTION_RESET

Verify ILogger calls with Moq.ILogger

Table Per Hierarchy Inheritance with Column Discriminator and Associations used in Derived Entity Types