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);
}