using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace CRM.Web.TopTaobao.Extensions { internal static class FullOuterExtensions { public static IDictionary<TK, IEnumerable<TV>> ToDictionary<TK, TV>(this IEnumerable<IGrouping<TK, TV>> grouping) { return grouping.ToDictionary(g => g.Key, g => g.AsEnumerable()); } internal static IEnumerable<TV> OuterGet<TK, TV>(this IDictionary<TK, IEnumerable<TV>> dict, TK k) { IEnumerable<TV> result; if (!dict.TryGetValue(k, out result)) yield break; foreach (var v in result) yield return v; } internal static IList<TR> FullOuterGroupJoin<TA, TB, TK, TR>( this IEnumerable<TA> a, IEnumerable<TB> b, Func<TA, TK> selectKeyA, Func<TB, TK> selectKeyB, Func<IEnumerable<TA>, IEnumerable<TB>, TK, TR> projection) { var adict = a.GroupBy(selectKeyA).ToDictionary(); var bdict = b.GroupBy(selectKeyB).ToDictionary(); var keys = new HashSet<TK>(adict.Keys); keys.UnionWith(bdict.Keys); var join = from key in keys let xa = adict.OuterGet(key) let xb = bdict.OuterGet(key) select projection(xa, xb, key); return join.ToList(); } internal static IList<TR> FullOuterJoin<TA, TB, TK, TR>( this IEnumerable<TA> a, IEnumerable<TB> b, Func<TA, TK> selectKeyA, Func<TB, TK> selectKeyB, Func<TA, TB, TK, TR> projection, TA defaultA = default(TA), TB defaultB = default(TB)) { var adict = a.GroupBy(selectKeyA).ToDictionary(); var bdict = b.GroupBy(selectKeyB).ToDictionary(); var keys = new HashSet<TK>(adict.Keys); keys.UnionWith(bdict.Keys); var join = from key in keys from xa in adict.OuterGet(key).DefaultIfEmpty(defaultA) from xb in bdict.OuterGet(key).DefaultIfEmpty(defaultB) select projection(xa, xb, key); return join.ToList(); } } }
class Product { public string Name { get; set; } public int CategoryID { get; set; } } class Category { public string Name { get; set; } public int ID { get; set; } }
void FullOuterJoinEx() { // Specify the first data source. List<Category> categories = new List<Category>() { new Category(){Name="Beverages", ID=001}, new Category(){ Name="Condiments", ID=002}, new Category(){ Name="Vegetables", ID=003}, new Category() { Name="Grains", ID=004}, new Category() { Name="Fruit", ID=005} }; // Specify the second data source. List<Product> products = new List<Product>() { new Product{Name="Cola", CategoryID=001}, new Product{Name="Tea", CategoryID=001}, new Product{Name="Mustard", CategoryID=002}, new Product{Name="Pickles", CategoryID=002}, //new Product{Name="Carrots", CategoryID=003}, //new Product{Name="Bok Choy", CategoryID=003}, new Product{Name="Peaches", CategoryID=005}, new Product{Name="Melons", CategoryID=005}, new Product{Name="Carrots222", CategoryID=006}, }; var query111 = categories.FullOuterGroupJoin(products, a => a.ID, b => b.CategoryID, (a, b, id) => new { a, b }).ToList(); var query222 = categories.FullOuterJoin(products, a => a.ID, b => b.CategoryID, (a, b, ID) => new { a, b }, new Category { ID = -1, Name = "(no firstname)" }, new Product { CategoryID = -2, Name = "(no surname)" }).ToList(); //var ax = new[] { //new { id = 1, name = "John" }, //new { id = 2, name = "Sue" } }; //var bx = new[] { //new { id = 1, surname = "Doe" }, //new { id = 3, surname = "Smith" } }; //ax.FullOuterGroupJoin(bx, a => a.id, b => b.id, (a, b, id) => new { a, b }) // .ToList().ForEach(Console.WriteLine); //ax.FullOuterJoin(bx, a => a.id, b => b.id, //(a, b, id) => new { a.name, b.surname }, //new { id = -1, name = "(no firstname)" }, //new { id = -2, surname = "(no surname)" }).ToList().ForEach(Console.WriteLine); }