Last active
August 29, 2015 13:56
-
-
Save benerdin/8854043 to your computer and use it in GitHub Desktop.
An enumerator that collates an IGrouping data structure.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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<TValue>.</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(); | |
| } | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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