グループ化してセット演算子を適用する。 3
さらに再考。
メソッドチェーンを繋げていくことを考えると、IEnumerable<IGrouping> を受け取って IEnumerable<IGrouping> を返すのが良い。
■グループ化セット演算
public static class LinqExtensions { // グループ化したキーで重複除去 public static IEnumerable<T> DistinctByKey<T, TKey>(this IEnumerable<IGrouping<TKey, T>> source) { return source.SelectMany(x => x.Select(y => new { key = x.Key, value = y })).ToLookup(x => x.key, x => x.value).Select(x => x.First()); } // グループ化したキーで和 public static IEnumerable<IGrouping<TKey, T>> GroupUnion<T, TKey>(this IEnumerable<IGrouping<TKey, T>> g1, IEnumerable<IGrouping<TKey, T>> g2) { var unionedKeys = g1.Select(x => x.Key).Union(g2.Select(x => x.Key)); return g1.Union(g2).Where(x => unionedKeys.Contains(x.Key)); } // グループ化したキーで積 public static IEnumerable<IGrouping<TKey, T>> GroupIntersect<T, TKey>(this IEnumerable<IGrouping<TKey, T>> g1, IEnumerable<IGrouping<TKey, T>> g2, bool onlyFirstSource = false) { var intersectedKeys = g1.Select(x => x.Key).Intersect(g2.Select(x => x.Key)); var srcGroup = onlyFirstSource ? g1 : g1.Union(g2); return srcGroup.Where(x => intersectedKeys.Contains(x.Key)); } // グループ化したキーで差 public static IEnumerable<IGrouping<TKey, T>> GroupExcept<T, TKey>(this IEnumerable<IGrouping<TKey, T>> g1, IEnumerable<IGrouping<TKey, T>> g2) { var exceptedKeys = g1.Select(x => x.Key).Except(g2.Select(x => x.Key)); return g1.Where(x => exceptedKeys.Contains(x.Key)); } }
■使い方
// a, b, c のシーケンスがある
var ag = a.GroupBy(x => x.Item);
var bg = b.GroupBy(x => x.Item);
var cg = c.GroupBy(x => x.Item);
var dest = ag.GroupExcept(bg).GroupExcept(cg).DistinctByKey();