Another way is to use Enumerator
and write a custom LINQ extensions method.
This method is more efficient because it eliminates multiple enumeration of collection:
public static class LinqExtensions
{
public static IEnumerable<Tuple<T, T>> GroupBy2<T>(this IEnumerable<T> source)
{
var enumerator = source.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
T first = enumerator.Current;
if (!enumerator.MoveNext()) {
yield return new Tuple<T, T>(first, default(T));
yield break;
}
T second = enumerator.Current;
yield return new Tuple<T, T>(first, second);
}
}
finally
{
if (enumerator != null)
enumerator.Dispose();
}
}
}
Usage example:
var data = new List<double>() { 32.00, 95.00, 60.00, 14.00, 62.00 };
// As Tuple<double, double>[] array:
var results = data.OrderByDescending(x => x).GroupBy2().ToArray();
// Iterate through IEnumerable<Tuple<double, double>>:
foreach (var pair in data.OrderByDescending(x => x).GroupBy2())
{
Console.WriteLine($"{pair.Item1} {pair.Item2}");
}
Result:
32 95
60 14
62 0
You can provide another behaviour instead of default(T)
for non-even item.
For example, this implementation returns null
as the second tuple item if pair does not exist, but it doens't work for classes.
public static IEnumerable<Tuple<T, T?>> GroupBy2<T>(this IEnumerable<T> source)
where T : struct
{
var enumerator = source.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
T first = enumerator.Current;
if (!enumerator.MoveNext()) {
yield return new Tuple<T, T?>(first, null);
yield break;
}
T second = enumerator.Current;
yield return new Tuple<T, T?>(first, second);
}
}
finally
{
if (enumerator != null)
enumerator.Dispose();
}
}