Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 180 additions & 0 deletions ShittyLINQ/GroupBy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
using System;
using System.Collections.Generic;

namespace ShittyLINQ
{
public static partial class Extensions
{
/// <summary>
/// Groups the elements of a sequence.
/// </summary>
/// <param name="source">An IEnumerable<T> whose elements to group.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <typeparam name="TSource">Source type</typeparam>
/// <typeparam name="TKey">Key type</typeparam>
/// <returns> A collection of elements where each element represents a projection over a group and its key.</returns>
/// <exception cref="ArgumentNullException">If source, keySelector or element Selector is null</exception>
public static Dictionary<TKey, IEnumerable<TSource>> GroupBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector)
{
return GroupBy(source, keySelector, t => t, EqualityComparer<TKey>.Default);
}


/// <summary>
/// Groups the elements of a sequence.
/// </summary>
/// <param name="source">An IEnumerable<T> whose elements to group.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <param name="comparer">An IEqualityComparer<T> to compare keys with.</param>
/// <typeparam name="TSource">Source type</typeparam>
/// <typeparam name="TKey">Key type</typeparam>
/// <returns> A collection of elements where each element represents a projection over a group and its key.</returns>
/// <exception cref="ArgumentNullException">If source, keySelector or element Selector is null</exception>
public static Dictionary<TKey, IEnumerable<TSource>> GroupBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> comparer)
{
return GroupBy(source, keySelector, t => t, comparer);
}


/// <summary>
/// Groups the elements of a sequence.
/// </summary>
/// <param name="source">An IEnumerable<T> whose elements to group.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <param name="elementSelector">A function to map each source element to an element in an IGrouping<TKey,TElement></param>
/// <typeparam name="TSource">Source type</typeparam>
/// <typeparam name="TKey">Key type</typeparam>
/// <typeparam name="TElement">Value type</typeparam>
/// <returns> A collection of elements where each element represents a projection over a group and its key.</returns>
/// <exception cref="ArgumentNullException">If source, keySelector or element Selector is null</exception>
public static Dictionary<TKey, IEnumerable<TElement>> GroupBy<TSource, TKey, TElement>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector)
{
return GroupBy(source, keySelector, elementSelector, EqualityComparer<TKey>.Default);
}


/// <summary>
/// Groups the elements of a sequence.
/// </summary>
/// <param name="source">An IEnumerable<T> whose elements to group.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <param name="elementSelector">A function to map each source element to an element in an IGrouping<TKey,TElement></param>
/// <typeparam name="TSource">Source type</typeparam>
/// <typeparam name="TKey">Key type</typeparam>
/// <typeparam name="TElement">Value type</typeparam>
/// <param name="comparer">An IEqualityComparer<T> to compare keys with.</param>
/// <returns> A collection of elements where each element represents a projection over a group and its key.</returns>
/// <exception cref="ArgumentNullException">If source, keySelector or element Selector is null</exception>
public static Dictionary<TKey, IEnumerable<TElement>> GroupBy<TSource, TKey, TElement>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector,
IEqualityComparer<TKey> comparer)
{
if (source == null || keySelector == null || elementSelector == null)
throw new ArgumentNullException();

Dictionary<TKey, IEnumerable<TElement>> res = new Dictionary<TKey, IEnumerable<TElement>>(comparer ?? EqualityComparer<TKey>.Default);

foreach (TSource item in source)
{
var subset = source.Where(b => keySelector(b).Equals(keySelector(item))).Select(a => elementSelector(a)).ToList();
if (!res.ContainsKey(keySelector(item)))
{
res.Add(keySelector(item), subset);
}
}
return res;
}

/// <summary>
/// Groups the elements of a sequence.
/// </summary>
/// <param name="source">An IEnumerable<T> whose elements to group.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <param name="resultSelector">A function to create a result value from each group.</param>
/// <param name="comparer">An IEqualityComparer<T> to compare keys with.</param>
/// <typeparam name="TSource">Source type</typeparam>
/// <typeparam name="TKey">Key type</typeparam>
/// <typeparam name="TResult">The type of the result value returned by resultSelector</typeparam>
/// <returns> A collection of elements of type TResult where each element represents a projection over a group and its key.</returns>
/// <exception cref="ArgumentNullException">If source, keySelector or element Selector is null</exception>
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, t => t, resultSelector, EqualityComparer<TKey>.Default);
}


/// <summary>
/// Groups the elements of a sequence.
/// </summary>
/// <param name="source">An IEnumerable<T> whose elements to group.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <param name="resultSelector">A function to create a result value from each group.</param>
/// <param name="comparer">An IEqualityComparer<T> to compare keys with.</param>
/// <typeparam name="TSource">Source type</typeparam>
/// <typeparam name="TKey">Key type</typeparam>
/// <typeparam name="TResult">The type of the result value returned by resultSelector</typeparam>
/// <returns> A collection of elements of type TResult where each element represents a projection over a group and its key.</returns>
/// <exception cref="ArgumentNullException">If source, keySelector or element Selector is null</exception>
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 GroupBy(source, keySelector, t => t, resultSelector, comparer);
}




/// <summary>
/// Groups the elements of a sequence.
/// </summary>
/// <param name="source">An IEnumerable<T> whose elements to group.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <param name="elementSelector">A function to map each source element to an element in an IGrouping<TKey,TElement></param>
/// <param name="resultSelector">A function to create a result value from each group.</param>
/// <param name="comparer">An IEqualityComparer<T> to compare keys with.</param>
/// <typeparam name="TSource">Source type</typeparam>
/// <typeparam name="TKey">Key type</typeparam>
/// <typeparam name="TElement">Value type</typeparam>
/// <typeparam name="TResult">The type of the result value returned by resultSelector</typeparam>
/// <returns> A collection of elements of type TResult where each element represents a projection over a group and its key.</returns>
/// <exception cref="ArgumentNullException">If source, keySelector or element Selector is null</exception>
public static IEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(
IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector,
Func<TKey, IEnumerable<TElement>, TResult> resultSelector,
IEqualityComparer<TKey> comparer)
{
if (source == null || keySelector == null || elementSelector == null || resultSelector == null || comparer == null)
throw new ArgumentNullException();

List<TResult> res = new List<TResult>();

foreach (TSource item in source)
{
var subset = source.Where(b => keySelector(b).Equals(keySelector(item))).Select(a => elementSelector(a)).ToList();
if (!res.Contains(resultSelector(keySelector(item), subset)))
{
res.Add(resultSelector(keySelector(item), subset));
}
}
return res;
}
}
}
47 changes: 47 additions & 0 deletions ShittyLinqTests/GroupByTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ShittyLINQ;
using ShittyTests.TestHelpers;

namespace ShittyTests
{
[TestClass]
public class GroupByTests
{

[TestMethod]
public void GroupBy_Person()
{
var adam = new Person("Adam", 20, "Arquitech", "Amber");
var brian = new Person("Brian", 45, "Arquitech", "Blue");
var charles = new Person("Charles", 33, "Arquitech", "Cyan");
var dani = new Person("Dani", 33, "Developer", "Deep Purple");
IEnumerable<Person> people = new[] { adam, brian, charles, dani };

var result = people.GroupBy(person => person.Age);

Assert.AreEqual(result.Count, 3);
Assert.AreEqual(result.GetValueOrDefault(20).First(), adam);
}

[TestMethod]
public void GroupBy_Person_WithResultSelector()
{
var adam = new Person("Adam", 20, "Arquitech", "Amber");
var brian = new Person("Brian", 45, "Arquitech", "Blue");
var charles = new Person("Charles", 33, "Arquitech", "Cyan");
var dani = new Person("Dani", 33, "Developer", "Deep Purple");
IEnumerable<Person> people = new[] { adam, brian, charles, dani };

var result = people.GroupBy(person => person.Age,
(baseAge, ages) => new
{
Key = baseAge,
Count = (int)ages.Count()
});

Assert.AreEqual(result.ToList().Count, 3);
Assert.AreEqual(result.ToList().Max(a => a.Count), 2);
}
}
}