You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
8.9 KiB
190 lines
8.9 KiB
5 years ago
|
#if NET20 || NET30 || !NET_4_6
|
||
|
|
||
|
using System.Collections;
|
||
|
using System.Collections.Generic;
|
||
|
using LinqInternal.Collections.Specialized;
|
||
|
using LinqInternal.Core;
|
||
|
|
||
|
namespace System.Linq.Reimplement
|
||
|
{
|
||
|
public static partial class Enumerable
|
||
|
{
|
||
|
public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
|
||
|
{
|
||
|
return GroupBy(source, keySelector, null);
|
||
|
}
|
||
|
|
||
|
public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
|
||
|
{
|
||
|
return new GroupedEnumerable<TSource, TKey, TSource>(source, keySelector, FuncHelper.GetIdentityFunc<TSource>(), comparer);
|
||
|
}
|
||
|
|
||
|
public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector)
|
||
|
{
|
||
|
return GroupBy(source, keySelector, elementSelector, null);
|
||
|
}
|
||
|
|
||
|
public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer)
|
||
|
{
|
||
|
LinqCheck.SourceAndKeyElementSelectors(source, keySelector, elementSelector);
|
||
|
|
||
|
return CreateGroupByIterator(source, keySelector, elementSelector, comparer);
|
||
|
}
|
||
|
|
||
|
public static IEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector)
|
||
|
{
|
||
|
return GroupBy(source, keySelector, elementSelector, resultSelector, null);
|
||
|
}
|
||
|
|
||
|
public static IEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector, IEqualityComparer<TKey> comparer)
|
||
|
{
|
||
|
LinqCheck.GroupBySelectors(source, keySelector, elementSelector, resultSelector);
|
||
|
|
||
|
return CreateGroupByIterator(source, keySelector, elementSelector, resultSelector, comparer);
|
||
|
}
|
||
|
|
||
|
public static IEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, IEnumerable<TSource>, TResult> resultSelector)
|
||
|
{
|
||
|
return GroupBy(source, keySelector, resultSelector, null);
|
||
|
}
|
||
|
|
||
|
public static IEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, IEnumerable<TSource>, TResult> resultSelector, IEqualityComparer<TKey> comparer)
|
||
|
{
|
||
|
return new GroupedEnumerable<TSource, TKey, TSource, TResult>(source, keySelector, FuncHelper.GetIdentityFunc<TSource>(), resultSelector, comparer);
|
||
|
}
|
||
|
|
||
|
private static IEnumerable<IGrouping<TKey, TElement>> CreateGroupByIterator<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer)
|
||
|
{
|
||
|
return new GroupedEnumerable<TSource, TKey, TElement>(source, keySelector, elementSelector, comparer)/* as IEnumerable<IGrouping<TKey, TElement>>*/;
|
||
|
}
|
||
|
|
||
|
private static IEnumerable<TResult> CreateGroupByIterator<TSource, TKey, TElement, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector, IEqualityComparer<TKey> comparer)
|
||
|
{
|
||
|
return new GroupedEnumerable<TSource, TKey, TElement, TResult>(source, keySelector, elementSelector, resultSelector, comparer)/* as IEnumerable<TResult>*/;
|
||
|
}
|
||
|
|
||
|
internal class GroupedEnumerable<TSource, TKey, TElement, TResult> : IEnumerable<TResult>
|
||
|
{
|
||
|
private readonly IEqualityComparer<TKey> _comparer;
|
||
|
private readonly Func<TSource, TElement> _elementSelector;
|
||
|
private readonly Func<TSource, TKey> _keySelector;
|
||
|
private readonly Func<TKey, IEnumerable<TElement>, TResult> _resultSelector;
|
||
|
private readonly IEnumerable<TSource> _source;
|
||
|
|
||
|
public GroupedEnumerable(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector, IEqualityComparer<TKey> comparer)
|
||
|
{
|
||
|
if (source == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("source");
|
||
|
}
|
||
|
if (keySelector == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("keySelector");
|
||
|
}
|
||
|
if (elementSelector == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("elementSelector");
|
||
|
}
|
||
|
if (resultSelector == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("resultSelector");
|
||
|
}
|
||
|
_source = source;
|
||
|
_keySelector = keySelector;
|
||
|
_elementSelector = elementSelector;
|
||
|
_resultSelector = resultSelector;
|
||
|
_comparer = comparer;
|
||
|
}
|
||
|
|
||
|
public IEnumerator<TResult> GetEnumerator()
|
||
|
{
|
||
|
var groupings = new NullAwareDictionary<TKey, Lookup<TKey, TElement>.Grouping>(_comparer);
|
||
|
foreach (var item in _source)
|
||
|
{
|
||
|
var key = _keySelector(item);
|
||
|
Lookup<TKey, TElement>.Grouping grouping;
|
||
|
if (!groupings.TryGetValue(key, out grouping))
|
||
|
{
|
||
|
grouping = new Lookup<TKey, TElement>.Grouping(key);
|
||
|
groupings.Add(key, grouping);
|
||
|
}
|
||
|
grouping.Items.Add(_elementSelector(item));
|
||
|
}
|
||
|
return Enumerator(groupings);
|
||
|
}
|
||
|
|
||
|
IEnumerator IEnumerable.GetEnumerator()
|
||
|
{
|
||
|
return GetEnumerator();
|
||
|
}
|
||
|
|
||
|
private IEnumerator<TResult> Enumerator(IDictionary<TKey, Lookup<TKey, TElement>.Grouping> groupings)
|
||
|
{
|
||
|
foreach (var grouping in groupings.Values)
|
||
|
{
|
||
|
yield return _resultSelector(grouping.Key, grouping.Items);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal class GroupedEnumerable<TSource, TKey, TElement> : IEnumerable<IGrouping<TKey, TElement>>
|
||
|
{
|
||
|
private readonly IEqualityComparer<TKey> _comparer;
|
||
|
private readonly Func<TSource, TElement> _elementSelector;
|
||
|
private readonly Func<TSource, TKey> _keySelector;
|
||
|
private readonly IEnumerable<TSource> _source;
|
||
|
|
||
|
public GroupedEnumerable(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer)
|
||
|
{
|
||
|
if (source == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("source");
|
||
|
}
|
||
|
if (keySelector == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("keySelector");
|
||
|
}
|
||
|
if (elementSelector == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("elementSelector");
|
||
|
}
|
||
|
_source = source;
|
||
|
_keySelector = keySelector;
|
||
|
_elementSelector = elementSelector;
|
||
|
_comparer = comparer;
|
||
|
}
|
||
|
|
||
|
public IEnumerator<IGrouping<TKey, TElement>> GetEnumerator()
|
||
|
{
|
||
|
var groupings = new NullAwareDictionary<TKey, Lookup<TKey, TElement>.Grouping>(_comparer);
|
||
|
foreach (var item in _source)
|
||
|
{
|
||
|
var key = _keySelector(item);
|
||
|
Lookup<TKey, TElement>.Grouping grouping;
|
||
|
if (!groupings.TryGetValue(key, out grouping))
|
||
|
{
|
||
|
grouping = new Lookup<TKey, TElement>.Grouping(key);
|
||
|
groupings.Add(key, grouping);
|
||
|
}
|
||
|
grouping.Items.Add(_elementSelector(item));
|
||
|
}
|
||
|
return Enumerator(groupings);
|
||
|
}
|
||
|
|
||
|
IEnumerator IEnumerable.GetEnumerator()
|
||
|
{
|
||
|
return GetEnumerator();
|
||
|
}
|
||
|
|
||
|
private static IEnumerator<IGrouping<TKey, TElement>> Enumerator(IDictionary<TKey, Lookup<TKey, TElement>.Grouping> groupings)
|
||
|
{
|
||
|
foreach (var grouping in groupings.Values)
|
||
|
{
|
||
|
yield return grouping;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|