グループ化してセット演算子を適用する。 2
再考。さっきよりは良い感じ。
Distinct は IEnumerable<IGrouping> を受け取って、他のセット演算は IEnumerable<IGrouping> を返すのがポイント。
■グループ化セット演算
public static class LinqExtensions { // グループ化したキーで重複除去 (sourceはグループ化済) public static IEnumerable<T> DistinctByKey<T, TKey>(this IEnumerable<IGrouping<TKey, T>> source) { var lookup = source.SelectMany(x => x.Select(y => new { key = x.Key, value = y })).ToLookup(x => x.key, x => x.value); return lookup.Select(x => x.First()); } // グループ化したキーで和 public static IEnumerable<IGrouping<TKey, T>> GroupUnion<T, TKey>(this IEnumerable<T> first, Func<T, TKey> keySelector, IEnumerable<T> second) { var g1 = first.GroupBy(keySelector); var g2 = second.GroupBy(keySelector); 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<T> first, Func<T, TKey> keySelector, IEnumerable<T> second, bool onlyFirstSource = false) { var g1 = first.GroupBy(keySelector); var g2 = second.GroupBy(keySelector); 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<T> first, Func<T, TKey> keySelector, IEnumerable<T> second) { var g1 = first.GroupBy(keySelector); var g2 = second.GroupBy(keySelector); var exceptedKeys = g1.Select(x => x.Key).Except(g2.Select(x => x.Key)); return g1.Where(x => exceptedKeys.Contains(x.Key)); } }
■使い方
// a, b のシーケンスがある
var distincted = a.GroupBy(x => x.Item).DistinctByKey();
var excepted = a.GroupExcept(x => x.Item, b).DistinctByKey();