Skip to content

Instantly share code, notes, and snippets.

@benerdin
Last active August 29, 2015 13:56
Show Gist options
  • Select an option

  • Save benerdin/8854043 to your computer and use it in GitHub Desktop.

Select an option

Save benerdin/8854043 to your computer and use it in GitHub Desktop.
An enumerator that collates an IGrouping data structure.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Brian.Federici
{
/// <summary>
/// An object that can iterate over an IGrouping collection.
/// </summary>
/// <typeparam name="T">Value type.</typeparam>
public class CollateEnumerator<T> : IEnumerator<T>
where T : class
{
/// <summary>
/// Constructs the enumerator from the <paramref name="grouping"/>.
/// </summary>
/// <param name="grouping"></param>
public CollateEnumerator(
IEnumerable<IGrouping<string, T>> grouping)
{
// Validate the "grouping" parameter.
if (grouping == null)
throw new ArgumentNullException("grouping");
if (grouping.Count() == 0)
throw new ArgumentException("grouping can't be empty", "grouping");
// Save the grouping.
_grouping = grouping;
// Save how many keys exist.
_keyCount = _grouping.Count();
// Determine which group has the largest number of items.
_maxGroupCount = _grouping.Max(grp => grp.Count());
// Flag if we're at the end.
_atEnd = _maxGroupCount == 0;
// Start before the first item.
_keyIndex = -1;
_positionIndex = 0;
}
/// <summary>
/// Gets the current strongly-typed item.
/// </summary>
T IEnumerator<T>.Current
{
get { return _current; }
}
/// <summary>
/// Disposes of the enumerator.
/// </summary>
void IDisposable.Dispose()
{
_grouping = null;
_current = null;
}
/// <summary>
/// Gets the current item as an object.
/// </summary>
object System.Collections.IEnumerator.Current
{
get { return _current; }
}
/// <summary>
/// Moves to the next item and returns true if it exists; false otherwise.
/// </summary>
/// <returns></returns>
bool System.Collections.IEnumerator.MoveNext()
{
_current = FindNext();
return _current != null;
}
/// <summary>
/// Resets enumerator to start back from beginning.
/// </summary>
void System.Collections.IEnumerator.Reset()
{
_keyIndex = -1;
_positionIndex = 0;
}
/// <summary>
/// Does the work to get the current item.
/// </summary>
/// <returns></returns>
private T GetCurrent()
{
return _grouping
.ElementAt(_keyIndex)
.ElementAtOrDefault(_positionIndex);
}
/// <summary>
/// Does the work to find the next item.
/// </summary>
/// <returns></returns>
private T FindNext()
{
var next = null as T;
do
{
Increment();
next = GetCurrent();
} while (next == null && !_atEnd);
return next;
}
private void Increment()
{
_keyIndex++;
if (_keyIndex >= _keyCount)
{
_keyIndex = 0;
_positionIndex++;
}
if (_positionIndex >= _maxGroupCount)
{
_atEnd = true;
}
}
private int _keyIndex;
private int _positionIndex;
private T _current;
private bool _atEnd;
private IEnumerable<IGrouping<string, T>> _grouping;
private readonly int _keyCount;
private readonly int _maxGroupCount;
}
}
using Beyond.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Brian.Federici
{
/// <summary>
/// Extensions for the IEnumerable interface.
/// </summary>
public static class IEnumerableExtensions
{
/// <summary>
/// Returns a flattened list of <typeparamref name="TValue"/> collated by the IGrouping key.
/// </summary>
/// <typeparam name="TValue">The destination value.</typeparam>
/// <param name="source">The source group.</param>
/// <param name="count">The number of items to return.</param>
/// <returns>A List&lt;TValue&gt;.</returns>
public static List<TValue> ToCollatedList<TValue>(
this IEnumerable<IGrouping<string, TValue>> source,
int count)
where TValue : class
{
// Handle an empty grouping.
if (source.IsNullOrEmpty())
return new List<TValue>(0);
// Using the CollateEnumerator...
using (IEnumerator<TValue> enumerator = new CollateEnumerator<TValue>(source))
{
// ...move to the next item and project it.
return Enumerable
.Range(1, count)
.Select(i =>
{
enumerator.MoveNext();
return enumerator.Current;
})
.ToList();
}
}
}
}
using System;
namespace Brian.Federici
{
class Program
{
public static void Main(string[] args)
{
// -- Example 1 --
// Given some existing grouping...
var grouping = new List<SomeObject>(
GetList()
).GroupBy(so => so.Property);
// ...pass it into the CollateEnumerator...
using (IEnumerator<SomeObject> enumerator = new CollateEnumerator<SomeObject>(grouping))
{
// ...and return a list of 50 items.
var collatedItems = Enumerable
.Range(1, 50)
.Select(i => {
enumerator.MoveNext();
return enumerator.Current;
})
.ToList();
}
// -- Example #2 --
var fromExtension = group.ToCollatedList(50);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment