internal static IEnumerable <T> MergeBy <T, TKey>(IEnumerable <IEnumerable <T> > lists, Func <T, TKey> keySelector, IComparer <TKey> comparer, bool descending) { var initialItems = lists .Select(l => l.GetEnumerator()) .Where(i => i.MoveNext()); IComparer <T> keyComparer = comparer != null ? new KeySelectorComparer <T, TKey>(keySelector, comparer) : new KeySelectorComparer <T, TKey>(keySelector); var enumerableComparer = new EnumeratorComparer <T>(keyComparer); DHeap <IEnumerator <T> > heap; if (descending) { heap = new MaxDHeap <IEnumerator <T> >(2, initialItems, enumerableComparer); } else { heap = new MinDHeap <IEnumerator <T> >(2, initialItems, enumerableComparer); } while (!heap.IsEmpty) { var i = heap.RemoveRoot(); yield return(i.Current); if (i.MoveNext()) { heap.Insert(i); } } }
private static IEnumerable <T> OrderedMergeHelper <T>(IEnumerable <IEnumerable <T> > sources, IComparer <T> elementComparer) { // Each sequence is expected to be ordered according to // the same comparison logic as elementComparer provides var enumerators = sources.Select(e => e.GetEnumerator()); // Disposing sequence of lazily acquired resources as // a single resource using (var disposableEnumerators = enumerators.AsDisposable()) { // The code below holds the following loop invariant: // - Priority queue contains enumerators that positioned at // sequence element // - The queue at the top has enumerator that positioned at // the smallest element of the remaining elements of all // sequences // Ensures that only non empty sequences participate in merge var nonEmpty = disposableEnumerators.Where(e => e.MoveNext()); // Current value of enumerator is its priority var comparer = new EnumeratorComparer <T>(elementComparer); // Use priority queue to get enumerator with smallest // priority (current value) var queue = new PriorityQueue <IEnumerator <T> >(nonEmpty, comparer); // The queue is empty when all sequences are empty while (queue.Count > 0) { // Dequeue enumerator that positioned at element that // is next in the merged sequence var min = queue.Dequeue(); yield return(min.Current); // Advance enumerator to next value if (min.MoveNext()) { // If it has value that can be merged into resulting // sequence put it into the queue queue.Enqueue(min); } } } }