import { Enumerable, IEnumerable } from '../enumerable' import { EqualityComparer, IEqualityComparer } from '../equality-comparer' import { IGrouping } from '../grouping' import * as utils from '../utils' export function groupJoin }>( outer: Iterable, inner: Iterable, outerKeySelector: (item: TOuter) => TKey, innerKeySelector: (item: TInner) => TKey, resultSelector?: (item: TOuter, inners: IEnumerable) => TResult, comparer?: IEqualityComparer ): IEnumerable { utils.throws.ThrowIfNull('outer', outer) utils.throws.ThrowIfNull('inner', inner) utils.throws.ThrowIfNull('outerKeySelector', outerKeySelector) utils.throws.ThrowIfNull('innerKeySelector', innerKeySelector) let _resultSelector: any = resultSelector || ((o, is) => { return { Outer: o, Inners: is } }) comparer = comparer || EqualityComparer.Default() let outerKeys = Enumerable.ToList(Enumerable.Select(outer, (item) => outerKeySelector(item))) let groupedInner = Enumerable.ToList( Enumerable.GroupBy( Enumerable.Where(inner, (item) => Enumerable.Contains(outerKeys, innerKeySelector(item), comparer)), (item) => innerKeySelector(item), (g) => g, comparer ) ) return Enumerable.AsEnumerable(join(outer, outerKeySelector, groupedInner, _resultSelector, comparer)) } function* join( outer: Iterable, outerKeySelector: (item: TOuter) => TKey, groupedInner: Iterable>, resultSelector: (item: TOuter, inners: IEnumerable) => TResult, comparer?: IEqualityComparer ) { for (let item of outer) { let key = outerKeySelector(item) let inners: IEnumerable = Enumerable.FirstOrDefault(groupedInner, null, (g) => comparer.Equals(g.Key, key)) if (!inners) { inners = Enumerable.Empty() } else { inners = Enumerable.ToList(inners) } yield resultSelector(item, inners) } }