Partition list by delimiter - c#

Is there an elegant way (with LINQ, for example) to partition a list into a list of sublists by delimiter? Partioning { 1, 2, delim, delim, 3, delim, 4, 5 } by delim might produce { { 1, 2 }, { 3 }, { 4, 5 } }....

I don't think there is an easy and elegant way of doing so with default LINQ methods. But you can create your own extension method:
public static class MyExtensions
{
public static IEnumerable<IEnumerable<TElement>> SplitBy<TElement>(
this IEnumerable<TElement> source,
TElement split,
bool skipEmptyGroups = true)
where TElement : IEquatable<TElement>
{
var group = new List<TElement>();
foreach (var item in source)
{
if (split.Equals(item))
{
if (group.Count > 0 || !skipEmptyGroups)
{
yield return group;
group = new List<TElement>();
}
}
else
{
group.Add(item);
}
}
if (group.Count > 0 || !skipEmptyGroups)
yield return group;
}
}
Usage is really simple:
var source = new List<int> { 1, 2, 3, 3, 4, 3, 5, 3, 6, 7, 8 };
var result = source.SplitBy(3);
If you want to return empty groups you can pass additional bool parameter:
var resultWithEmptyGroups = source.SplitBy(3, false);

Directly in Linq, I think it will be hard, but you can create a custom operator. Maybe something like this :
List<String> test = new List<String>() { "1", "8", ";", "2", "7", "42", ";", "3" };
var restul = test.StrangePartition(";");
with :
public static class Helper
{
public static IEnumerable<IEnumerable<T>> StrangePartition<T>(this IEnumerable<T> source, T partitionKey)
{
List<List<T>> partitions = new List<List<T>>();
List<T> partition = new List<T>();
foreach (T item in source)
{
if (item.Equals(partitionKey))
{
partitions.Add(partition);
partition = new List<T>();
}
else
{
partition.Add(item);
}
}
partitions.Add(partition);
return partitions;
}
}

No, there isn't anything similar, but it's quite easy to implement them:
public static class LinqEx
{
public static IEnumerable<List<TSource>> Split<TSource>(this IEnumerable<TSource> enu, TSource delimiter, IEqualityComparer<TSource> comparer = null)
{
// list == null handles the case where enu is empty
List<TSource> list = null;
if (comparer == null)
{
// Note how the equality comparer is "selected".
// This is how LINQ methods do it
// (see Enumerable.SequenceEqual<TSource>)
comparer = EqualityComparer<TSource>.Default;
}
foreach (TSource el in enu)
{
if (comparer.Equals(el, delimiter))
{
if (list == null)
{
list = new List<TSource>();
}
yield return list;
// Note that we have to recreate the list every time!
// We can't simply do a list.Clear()
list = new List<TSource>();
continue;
}
if (list == null)
{
list = new List<TSource>();
}
list.Add(el);
}
if (list != null)
{
yield return list;
}
}
public static IEnumerable<List<TSource>> SplitRemoveEmpty<TSource>(this IEnumerable<TSource> enu, TSource delimiter, IEqualityComparer<TSource> comparer = null)
{
var list = new List<TSource>();
if (comparer == null)
{
// Note how the equality comparer is "selected".
// This is how LINQ methods do it
// (see Enumerable.SequenceEqual<TSource>)
comparer = EqualityComparer<TSource>.Default;
}
foreach (TSource el in enu)
{
if (comparer.Equals(el, delimiter))
{
if (list.Count != 0)
{
yield return list;
// Note that we have to recreate the list every time!
// We can't simply do a list.Clear()
list = new List<TSource>();
}
continue;
}
list.Add(el);
}
if (list.Count != 0)
{
yield return list;
}
}
}
There are two variants: the first one (Split) will return even the empty groups, the second one will strip empty groups.
For example:
{ delim, 1, 2, delim, delim, 3, delim, 4, 5, delim }
With Split you will receive
{ { }, {1, 2}, { }, {3}, {4, 5}, { } }
While with SplitRemoveEmpty
{ {1, 2}, {3}, {4, 5} }
Use them like:
var res = new[] { 1, 2, 0, 0, 3, 4, 0, 5 }.SplitRemoveEmpty(0);
foreach (List<int> group in res)
{
// Do something
}
Remember that these split methods are based on the correctness of the Equals method! But there is an optional last parameter where you can give an IEqualityComparer<TSource> that the methods will use instead of the default one.

Although others mentioned it would be easier with an imperative approach, it is of course do-able in LINQ. The Aggregate operator has a lot of uses, but usually a poor readability.
var testCase = new List<String> { "1", "8", ";", "2", "7", "42", ";", "3" };
var result = testCase.Aggregate(new List<List<String>>() { new List<String>() }, (l, s) => {
if (s == ";") {
l.Add(new List<String>());
} else {
l[l.Count - 1].Add(s);
}
return l;
}).ToList();
It will produce the desired output, but its not only hard to comprehend, but it also produces side-effects within the query (not as bad as producing side-effects outside the query, but still ill-advised).
You can put it into your own extension method if you wishes so:
public static IEnumerable<IEnumerable<T>> PartitionBy(this IEnumerable<T> source, T delimiter) {
return source.Aggregate(new List<List<T>>() { new List<T>() }, (l, elem) =>
if(elem.Equals(delimiter)) {
l.Add(new List<T>());
} else {
l[l.Count - 1].Add(elem);
}
return l;
});
}
Again, consider this answer only for the completeness of a LINQ answer.

Related

Merge different length arrays without losing a value using Zip()

In the following code, I'm merging two arrays of types int and string. The first one's length is bigger than the second one, and as a result, the last index (which is 5) doesn't get merged:
int[] numbers = new[] { 1, 2, 3, 4, 5 };
string[] words = new string[] { "one", "two", "three", "four" };
var numbersAndWords = numbers.Zip(words, (n, w) => new { Number = n, Word = w });
foreach (var nw in numbersAndWords)
{
Console.WriteLine(nw.Number + nw.Word);
}
I would like to know a way of getting it merged. For example, creating a null or empty string after the last one that exists in words and using it to merge with the last numbers index. Couldn't figure it out.
Edit:
Result I get
1one
2two
3three
4four
Result I want
1one
2two
3three
4four
5
Thanks!
Edit: Not a duplicate, my other question is about calling method on a null object.
You can easily write your own LINQ-like extension method that will do it:
public static class MyEnumerable
{
public static IEnumerable<TResult> ZipWithDefault<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> selector)
{
bool firstMoveNext, secondMoveNext;
using (var enum1 = first.GetEnumerator())
using (var enum2 = second.GetEnumerator())
{
while ((firstMoveNext = enum1.MoveNext()) & (secondMoveNext = enum2.MoveNext()))
yield return selector(enum1.Current, enum2.Current);
if (firstMoveNext && !secondMoveNext)
{
yield return selector(enum1.Current, default(TSecond));
while (enum1.MoveNext())
{
yield return selector(enum1.Current, default(TSecond));
}
}
else if (!firstMoveNext && secondMoveNext)
{
yield return selector(default(TFirst), enum2.Current);
while (enum2.MoveNext())
{
yield return selector(default(TFirst), enum2.Current);
}
}
}
}
}
But if your source is always a pair of arrays, it might be easier to simply use for loop:
public static IEnumerable<TResult> ZipWithDefault<TFirst, TSecond, TResult>(this TFirst[] first, TSecond[] second, Func<TFirst, TSecond, TResult> selector)
{
var maxLength = Math.Max(first.Length, second.Length);
for(var i = 0; i < maxLength; i++)
{
var firstItem = i < first.Length ? first[i] : default(TFirst);
var secondItem = i < second.Length ? second[i] : default(TSecond);
yield return selector(firstItem, secondItem);
}
}
You can just extend the smaller of the two collections before zipping them up, e.g. something like this:
int[] numbers = new[] { 1, 2, 3, 4, 5 };
string[] words = new string[] { "one", "two", "three", "four" };
IEnumerable<string> wordsExtended = words;
if(words.Length < numbers.Length)
{
wordsExtended = words.Concat(Enumerable.Repeat("", numbers.Length - words.Length));
}
var numbersAndWords = numbers.Zip(wordsExtended, (n, w) => new { Number = n, Word = w });
foreach (var nw in numbersAndWords)
{
Console.WriteLine(nw.Number + nw.Word);
}
Ideally you want to wrap this up in a utility method and be generic so it works for any collections that are zipped.
Looks like someone wrote a generic implementation already on this Programmers StackExchange answer.
This is an asymmetric solution which uses the first sequence as an anchor, pairing corresponding elements from the second sequence when available, or a default value otherwise. No length calculation is required, nor early forced evaluation of enumerables.
Add an extension to IEnumerable:
public static class EnumerableExtensions {
public static IEnumerable<T> PadRight<T>(this IEnumerable<T> s)
{
foreach (var t in s)
yield return t;
while (true)
yield return default(T);
}
}
Use the extension to "pad" the second sequence before zipping:
int[] numbers = new[] { 1, 2, 3, 4, 5 };
string[] words = new string[] { "one", "two", "three", "four" };
var numbersAndWords = numbers.Zip(words.PadRight(), (n, w) => new { Number = n, Word = w });
foreach (var nw in numbersAndWords)
{
Console.WriteLine(nw.Number + nw.Word ?? string.Empty);
}

Build lists out of dictionary

I've got a dictionary that for each key lists its dependencies:
parent[2] = 1 (2 depends on 1)
parent[3] = 1 (3 depends on 1)
parent[4] = {2,3} (4 depends on 2, or 4 depends on 3)
I want to build lists out of this dictionary:
[4,2,1]
[4,3,1]
I've got the suspicion I should use a recursive algorithm. Any hints?
EDIT: this is what I have so far:
How I call the recursive function:
var result = new List<List<Node<TData, TId>>>();
GetResult(parent, target, result);
return result;
And the recursive function itself:
private static List<Node<TData, TId>> GetResult<TData, TId>(Dictionary<Node<TData, TId>, List<Node<TData, TId>>> parent, Node<TData, TId> index,
List<List<Node<TData, TId>>> finalList)
where TData : IIdentifiable<TId>
where TId : IComparable
{
var newResult = new List<Node<TData, TId>> { index };
if (parent.ContainsKey(index))
{
if (parent[index].Count == 1)
{
return new List<Node<TData, TId>> { index, parent[index].First()};
}
foreach (var child in parent[index])
{
var temp = newResult.Union(GetResult(parent, child, finalList)).ToList();
finalList.Add(temp);
}
}
return newResult;
}
You could try to adapt for your needs the following code:
public static List<List<int>> FindParents(Dictionary<int, List<int>> parents, int index)
{
List<int> prefix = new List<int>();
List<List<int>> results = new List<List<int>>();
FindParentsInternal(parents, index, prefix, results);
return results;
}
private static void FindParentsInternal(Dictionary<int, List<int>> parents, int index,
List<int> prefix, List<List<int>> results)
{
var newPrefix = new List<int>(prefix) { index };
if (!parents.ContainsKey(index))
{
results.Add(newPrefix);
return;
}
parents[index].ForEach(i => FindParentsInternal(parents, i, newPrefix, results));
}
Usage:
Dictionary<int, List<int>> parents = new Dictionary<int, List<int>>
{
{ 2, new List<int> { 1 } },
{ 3, new List<int> { 1 } },
{ 4, new List<int> { 2, 3 } }
};
var t = FindParents(parents, 4);
You can benefit by keeping a dictionary of results - that way you won't need to keep recomputing them.
Dictionary<Int, Set<Int>> results;
Set<Int> getResult(int index) {
Set<Int> dictResult = results.get(index);
if(dictResult != null) {
// result has already been computed
return dictResult;
} else {
// compute result, store in dictResult
Set<Int> newResult = // compute dependency set
dictResult.put(index, newResult);
return newResult;
}
}
As for the // compute dependency list part, you can do something like the following:
Set<Int> newResult = new Set(index);
if(dict.containsKey(index)) {
List<Int> dependencies = dict.get(index);
foreach(int subIndex in dependencies) {
newResult = newResult.union(getResult(subIndex));
}
}
Your base case is when the index is not in dict (i.e. dict.containsKey returns false), e.g. 1 for the data you provided.

Combine entries from two lists by position using LINQ

Say I have two lists with following entries
List<int> a = new List<int> { 1, 2, 5, 10 };
List<int> b = new List<int> { 6, 20, 3 };
I want to create another List c where its entries are items inserted by position from two lists. So List c would contain the following entries:
List<int> c = {1, 6, 2, 20, 5, 3, 10}
Is there a way to do it in .NET using LINQ? I was looking at .Zip() LINQ extension, but wasn't sure how to use it in this case.
Thanks in advance!
To do it using LINQ, you can use this piece of LINQPad example code:
void Main()
{
List<int> a = new List<int> { 1, 2, 5, 10 };
List<int> b = new List<int> { 6, 20, 3 };
var result = Enumerable.Zip(a, b, (aElement, bElement) => new[] { aElement, bElement })
.SelectMany(ab => ab)
.Concat(a.Skip(Math.Min(a.Count, b.Count)))
.Concat(b.Skip(Math.Min(a.Count, b.Count)));
result.Dump();
}
Output:
This will:
Zip the two lists together (which will stop when either runs out of elements)
Producing an array containing the two elements (one from a, another from b)
Using SelectMany to "flatten" this out to one sequence of values
Concatenate in the remainder from either list (only one or neither of the two calls to Concat should add any elements)
Now, having said that, personally I would've used this:
public static IEnumerable<T> Intertwine<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
using (var enumerator1 = a.GetEnumerator())
using (var enumerator2 = b.GetEnumerator())
{
bool more1 = enumerator1.MoveNext();
bool more2 = enumerator2.MoveNext();
while (more1 && more2)
{
yield return enumerator1.Current;
yield return enumerator2.Current;
more1 = enumerator1.MoveNext();
more2 = enumerator2.MoveNext();
}
while (more1)
{
yield return enumerator1.Current;
more1 = enumerator1.MoveNext();
}
while (more2)
{
yield return enumerator2.Current;
more2 = enumerator2.MoveNext();
}
}
}
Reasons:
It doesn't enumerate a nor b more than once
I'm skeptical about the performance of Skip
It can work with any IEnumerable<T> and not just List<T>
I'd create an extension method to do it.
public static List<T> MergeAll<T>(this List<T> first, List<T> second)
{
int maxCount = (first.Count > second. Count) ? first.Count : second.Count;
var ret = new List<T>();
for (int i = 0; i < maxCount; i++)
{
if (first.Count < maxCount)
ret.Add(first[i]);
if (second.Count < maxCount)
ret.Add(second[i]);
}
return ret;
}
This would iterate through both lists once. If one list is bigger than the other it will continue to add until it's done.
You could try this code:
List<int> c = a.Select((i, index) => new Tuple<int, int>(i, index * 2))
.Union(b.Select((i, index) => new Tuple<int, int>(i, index * 2 + 1)))
.OrderBy(t => t.Second)
.Select(t => t.First).ToList();
It makes a union of two collections and then sorts that union using index. Elements from the first collection have even indices, from the second - odd ones.
Just wrote a little extension for this:
public static class MyEnumerable
{
public static IEnumerable<T> Smash<T>(this IEnumerable<T> one, IEnumerable<T> two)
{
using (IEnumerator<T> enumeratorOne = one.GetEnumerator(),
enumeratorTwo = two.GetEnumerator())
{
bool twoFinished = false;
while (enumeratorOne.MoveNext())
{
yield return enumeratorOne.Current;
if (!twoFinished && enumeratorTwo.MoveNext())
{
yield return enumeratorTwo.Current;
}
}
if (!twoFinished)
{
while (enumeratorTwo.MoveNext())
{
yield return enumeratorTwo.Current;
}
}
}
}
}
Usage:
var a = new List<int> { 1, 2, 5, 10 };
var b = new List<int> { 6, 20, 3 };
var c = a.Smash(b); // 1, 6, 2, 20, 5, 3, 10
var d = b.Smash(a); // 6, 1, 20, 2, 3, 5, 10
This will work for any IEnumerable so you can also do:
var a = new List<string> { "the", "brown", "jumped", "the", "lazy", "dog" };
var b = new List<string> { "quick", "dog", "over" };
var c = a.Smash(b); // the, quick, brown, fox, jumped, over, the, lazy, dog
You could use Concat and an anonymous type which you order by the index:
List<int> c = a
.Select((val, index) => new { val, index })
.Concat(b.Select((val, index) => new { val, index }))
.OrderBy(x => x.index)
.Select(x => x.val)
.ToList();
However, since that's not really elegant and also less efficient than:
c = new List<int>(a.Count + b.Count);
int max = Math.Max(a.Count, b.Count);
int aMax = a.Count;
int bMax = b.Count;
for (int i = 0; i < max; i++)
{
if(i < aMax)
c.Add(a[i]);
if(i < bMax)
c.Add(b[i]);
}
I wouldn't use LINQ at all.
Sorry for adding a third extension method inspired by the other two, but I like it shorter:
static IEnumerable<T> Intertwine<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
using (var enumerator1 = a.GetEnumerator())
using (var enumerator2 = b.GetEnumerator()) {
bool more1 = true, more2 = true;
do {
if (more1 && (more1 = enumerator1.MoveNext()))
yield return enumerator1.Current;
if (more2 && (more2 = enumerator2.MoveNext()))
yield return enumerator2.Current;
} while (more1 || more2);
}
}

Interleaving multiple (more than 2) irregular lists using LINQ

Say I have the following data
IEnumerable<IEnumerable<int>> items = new IEnumerable<int>[] {
new int[] { 1, 2, 3, 4 },
new int[] { 5, 6 },
new int[] { 7, 8, 9 }
};
What would be the easiest way to return a flat list with the items interleaved so I'd get the result:
1, 5, 7, 2, 6, 8, 3, 9, 4
Note: The number of inner lists is not known at runtime.
What you're describing is essentially a Transpose Method where overhanging items are included and the result is flattened. Here's my attempt:
static IEnumerable<IEnumerable<T>> TransposeOverhanging<T>(
this IEnumerable<IEnumerable<T>> source)
{
var enumerators = source.Select(e => e.GetEnumerator()).ToArray();
try
{
T[] g;
do
{
yield return g = enumerators
.Where(e => e.MoveNext()).Select(e => e.Current).ToArray();
}
while (g.Any());
}
finally
{
Array.ForEach(enumerators, e => e.Dispose());
}
}
Example:
var result = items.TransposeOverhanging().SelectMany(g => g).ToList();
// result == { 1, 5, 7, 2, 6, 8, 3, 9, 4 }
The solution below is very straight forward. As it turns out, it is also nearly twice as fast as the solution proposed by dtb.
private static IEnumerable<T> Interleave<T>(this IEnumerable<IEnumerable<T>> source )
{
var queues = source.Select(x => new Queue<T>(x)).ToList();
while (queues.Any(x => x.Any())) {
foreach (var queue in queues.Where(x => x.Any())) {
yield return queue.Dequeue();
}
}
}
Here's my attempt, based on dtb's answer. It avoids the external SelectMany and internal ToArray calls.
public static IEnumerable<T> Interleave<T>(this IEnumerable<IEnumerable<T>> source)
{
var enumerators = source.Select(e => e.GetEnumerator()).ToArray();
try
{
bool itemsRemaining;
do
{
itemsRemaining = false;
foreach (var item in
enumerators.Where(e => e.MoveNext()).Select(e => e.Current))
{
yield return item;
itemsRemaining = true;
}
}
while (itemsRemaining);
}
finally
{
Array.ForEach(enumerators, e => e.Dispose());
}
}
Disposed all enumerators, even when exceptions are thrown
Evaluates the outer sequence eagerly, but uses lazy evaluation for the inner sequences.
public static IEnumerable<T> Interleave<T>(IEnumerable<IEnumerable<T>> sequences)
{
var enumerators = new List<IEnumerator<T>>();
try
{
// using foreach here ensures that `enumerators` contains all already obtained enumerators, in case of an expection is thrown here.
// this ensures proper disposing in the end
foreach(var enumerable in sequences)
{
enumerators.Add(enumerable.GetEnumerator());
}
var queue = new Queue<IEnumerator<T>>(enumerators);
while (queue.Any())
{
var enumerator = queue.Dequeue();
if (enumerator.MoveNext())
{
queue.Enqueue(enumerator);
yield return enumerator.Current;
}
}
}
finally
{
foreach(var enumerator in enumerators)
{
enumerator.Dispose();
}
}
}
Though its not as elegant as "dtb"'s answer, but it also works and its a single liner :)
Enumerable.Range(0, items.Max(x => x.Count()))
.ToList()
.ForEach(x =>
{
items
.Where(lstChosen => lstChosen.Count()-1 >= x)
.Select(lstElm => lstElm.ElementAt(x))
.ToList().ForEach(z => Console.WriteLine(z));
});

Most efficient algorithm for merging sorted IEnumerable<T>

I have several huge sorted enumerable sequences that I want to merge. Theses lists are manipulated as IEnumerable but are already sorted. Since input lists are sorted, it should be possible to merge them in one trip, without re-sorting anything.
I would like to keep the defered execution behavior.
I tried to write a naive algorithm which do that (see below). However, it looks pretty ugly and I'm sure it can be optimized. It may exist a more academical algorithm...
IEnumerable<T> MergeOrderedLists<T, TOrder>(IEnumerable<IEnumerable<T>> orderedlists,
Func<T, TOrder> orderBy)
{
var enumerators = orderedlists.ToDictionary(l => l.GetEnumerator(), l => default(T));
IEnumerator<T> tag = null;
var firstRun = true;
while (true)
{
var toRemove = new List<IEnumerator<T>>();
var toAdd = new List<KeyValuePair<IEnumerator<T>, T>>();
foreach (var pair in enumerators.Where(pair => firstRun || tag == pair.Key))
{
if (pair.Key.MoveNext())
toAdd.Add(pair);
else
toRemove.Add(pair.Key);
}
foreach (var enumerator in toRemove)
enumerators.Remove(enumerator);
foreach (var pair in toAdd)
enumerators[pair.Key] = pair.Key.Current;
if (enumerators.Count == 0)
yield break;
var min = enumerators.OrderBy(t => orderBy(t.Value)).FirstOrDefault();
tag = min.Key;
yield return min.Value;
firstRun = false;
}
}
The method can be used like that:
// Person lists are already sorted by age
MergeOrderedLists(orderedList, p => p.Age);
assuming the following Person class exists somewhere:
public class Person
{
public int Age { get; set; }
}
Duplicates should be conserved, we don't care about their order in the new sequence. Do you see any obvious optimization I could use?
Here is my fourth (thanks to #tanascius for pushing this along to something much more LINQ) cut at it:
public static IEnumerable<T> MergePreserveOrder3<T, TOrder>(
this IEnumerable<IEnumerable<T>> aa,
Func<T, TOrder> orderFunc)
where TOrder : IComparable<TOrder>
{
var items = aa.Select(xx => xx.GetEnumerator()).Where(ee => ee.MoveNext())
.OrderBy(ee => orderFunc(ee.Current)).ToList();
while (items.Count > 0)
{
yield return items[0].Current;
var next = items[0];
items.RemoveAt(0);
if (next.MoveNext())
{
// simple sorted linear insert
var value = orderFunc(next.Current);
var ii = 0;
for ( ; ii < items.Count; ++ii)
{
if (value.CompareTo(orderFunc(items[ii].Current)) <= 0)
{
items.Insert(ii, next);
break;
}
}
if (ii == items.Count) items.Add(next);
}
else next.Dispose(); // woops! can't forget IDisposable
}
}
Results:
for (int p = 0; p < people.Count; ++p)
{
Console.WriteLine("List {0}:", p + 1);
Console.WriteLine("\t{0}", String.Join(", ", people[p].Select(x => x.Name)));
}
Console.WriteLine("Merged:");
foreach (var person in people.MergePreserveOrder(pp => pp.Age))
{
Console.WriteLine("\t{0}", person.Name);
}
List 1:
8yo, 22yo, 47yo, 49yo
List 2:
35yo, 47yo, 60yo
List 3:
28yo, 55yo, 64yo
Merged:
8yo
22yo
28yo
35yo
47yo
47yo
49yo
55yo
60yo
64yo
Improved with .Net 4.0's Tuple support:
public static IEnumerable<T> MergePreserveOrder4<T, TOrder>(
this IEnumerable<IEnumerable<T>> aa,
Func<T, TOrder> orderFunc) where TOrder : IComparable<TOrder>
{
var items = aa.Select(xx => xx.GetEnumerator())
.Where(ee => ee.MoveNext())
.Select(ee => Tuple.Create(orderFunc(ee.Current), ee))
.OrderBy(ee => ee.Item1).ToList();
while (items.Count > 0)
{
yield return items[0].Item2.Current;
var next = items[0];
items.RemoveAt(0);
if (next.Item2.MoveNext())
{
var value = orderFunc(next.Item2.Current);
var ii = 0;
for (; ii < items.Count; ++ii)
{
if (value.CompareTo(items[ii].Item1) <= 0)
{ // NB: using a tuple to minimize calls to orderFunc
items.Insert(ii, Tuple.Create(value, next.Item2));
break;
}
}
if (ii == items.Count) items.Add(Tuple.Create(value, next.Item2));
}
else next.Item2.Dispose(); // woops! can't forget IDisposable
}
}
One guess I would make that might improve clarity and performance is this:
Create a priority queue over pairs of T, IEnumerable<T> ordered according to your comparison function on T
For each IEnumerable<T> being merged, add the item to the priority queue annotated with a reference to the IEnumerable<T> where it originated
While the priority queue is not empty
Extract the minimum element from the priority queue
Advance the IEnumerable<T> in its annotation to the next element
If MoveNext() returned true, add the next element to the priority queue annotated with a reference to the IEnumerable<T> you just advanced
If MoveNext() returned false, don't add anything to the priority queue
Yield the dequeued element
How many lists do you expect to need to merge? It looks like your algorithm will not be efficient if you have many different lists to merge. This line is the issue:
var min = enumerators.OrderBy(t => orderBy(t.Value)).FirstOrDefault();
This will be run once for each element in all the lists, so your runtime will be O(n * m), where n is the TOTAL number of elements in all the lists, and n is the number of lists. Expressed in terms of the average length of a list in the list of lists, the runtime is O(a * m^2).
If you are going to need to merge a lot of lists, I would suggest using a heap. Then each iteration you can remove the smallest value from the heap, and add the next element to the heap from the list that the smallest value came from.
Here's a solution with NO SORTING ... just the minimum number of comparisons. (I omitted the actual order func passing for simplicity). Updated to build a balanced tree:-
/// <summary>
/// Merge a pair of ordered lists
/// </summary>
public static IEnumerable<T> Merge<T>(IEnumerable<T> aList, IEnumerable<T> bList)
where T:IComparable<T>
{
var a = aList.GetEnumerator();
bool aOK = a.MoveNext();
foreach (var b in bList)
{
while (aOK && a.Current.CompareTo(b) <= 0) {yield return a.Current; aOK = a.MoveNext();}
yield return b;
}
// And anything left in a
while (aOK) { yield return a.Current; aOK = a.MoveNext(); }
}
/// <summary>
/// Merge lots of sorted lists
/// </summary>
public static IEnumerable<T> Merge<T>(IEnumerable<IEnumerable<T>> listOfLists)
where T : IComparable<T>
{
int n = listOfLists.Count();
if (n < 2)
return listOfLists.FirstOrDefault();
else
return Merge (Merge(listOfLists.Take(n/2)), Merge(listOfLists.Skip(n/2)));
}
public static void Main(string[] args)
{
var sample = Enumerable.Range(1, 5).Select((i) => Enumerable.Range(i, i+5).Select(j => string.Format("Test {0:00}", j)));
Console.WriteLine("Merged:");
foreach (var result in Merge(sample))
{
Console.WriteLine("\t{0}", result);
}
Here is a solution that has very good complexity analysis and that is considerably shorter than the other solutions proposed.
public static IEnumerable<T> Merge<T>(this IEnumerable<IEnumerable<T>> self)
where T : IComparable<T>
{
var es = self.Select(x => x.GetEnumerator()).Where(e => e.MoveNext());
var tmp = es.ToDictionary(e => e.Current);
var dict = new SortedDictionary<T, IEnumerator<T>>(tmp);
while (dict.Count > 0)
{
var key = dict.Keys.First();
var cur = dict[key];
dict.Remove(key);
yield return cur.Current;
if (cur.MoveNext())
dict.Add(cur.Current, cur);
}
}
Here is my solution:
The algorithm takes the first element of each list and puts them within a small helper class (a sorted list that accepts mutliple elements with the same value). This sorted list uses a binary insert.
So the first element in this list is the element we want to return next. After doing so we remove it from the sorted list and insert the next element from its original source list (at least as long as this list contains any more elements). Again, we can return the first element of our sorted list. When the sorted list is empty once, we used all element from all different source lists and are done.
This solution uses less foreach statements and no OrderBy in each step - which should improve the runtime behaviour. Only the binary insert has to be done again and again.
IEnumerable<T> MergeOrderedLists<T, TOrder>( IEnumerable<IEnumerable<T>> orderedlists, Func<T, TOrder> orderBy )
{
// Get an enumerator for each list, create a sortedList
var enumerators = orderedlists.Select( enumerable => enumerable.GetEnumerator() );
var sortedEnumerators = new SortedListAllowingDoublets<TOrder, IEnumerator<T>>();
// Point each enumerator onto the first element
foreach( var enumerator in enumerators )
{
// Missing: assert true as the return value
enumerator.MoveNext();
// Initially add the first value
sortedEnumerators.AddSorted( orderBy( enumerator.Current ), enumerator );
}
// Continue as long as we have elements to return
while( sortedEnumerators.Count != 0 )
{
// The first element of the sortedEnumerator list always
// holds the next element to return
var enumerator = sortedEnumerators[0].Value;
// Return this enumerators current value
yield return enumerator.Current;
// Remove the element we just returned
sortedEnumerators.RemoveAt( 0 );
// Check if there is another element in the list of the enumerator
if( enumerator.MoveNext() )
{
// Ok, so add it to the sorted list
sortedEnumerators.AddSorted( orderBy( enumerator.Current ), enumerator );
}
}
My helper class (using a simple binary insert):
private class SortedListAllowingDoublets<TOrder, T> : Collection<KeyValuePair<TOrder, T>> where T : IEnumerator
{
public void AddSorted( TOrder value, T enumerator )
{
Insert( GetSortedIndex( value, 0, Count - 1 ), new KeyValuePair<TOrder, T>( value, enumerator ) );
}
private int GetSortedIndex( TOrder item, int startIndex, int endIndex )
{
if( startIndex > endIndex )
{
return startIndex;
}
var midIndex = startIndex + ( endIndex - startIndex ) / 2;
return Comparer<TOrder>.Default.Compare( this[midIndex].Key, item ) < 0 ? GetSortedIndex( item, midIndex + 1, endIndex ) : GetSortedIndex( item, startIndex, midIndex - 1 );
}
}
What's not implemented right now: check for an empty list, which will cause problems.
And the SortedListAllowingDoublets class could be improved to take a comparer instead of using the Comparer<TOrder>.Default on its own.
Here is a Linq friendly solution based on the Wintellect's OrderedBag:
public static IEnumerable<T> MergeOrderedLists<T, TOrder>(this IEnumerable<IEnumerable<T>> orderedLists, Func<T, TOrder> orderBy)
where TOrder : IComparable<TOrder>
{
var enumerators = new OrderedBag<IEnumerator<T>>(orderedLists
.Select(enumerable => enumerable.GetEnumerator())
.Where(enumerator => enumerator.MoveNext()),
(x, y) => orderBy(x.Current).CompareTo(orderBy(y.Current)));
while (enumerators.Count > 0)
{
IEnumerator<T> minEnumerator = enumerators.RemoveFirst();
T minValue = minEnumerator.Current;
if (minEnumerator.MoveNext())
enumerators.Add(minEnumerator);
else
minEnumerator.Dispose();
yield return minValue;
}
}
If you use any Enumerator based solution, don't forget to call Dispose()
And here is a simple test:
[Test]
public void ShouldMergeInOrderMultipleOrderedListWithDuplicateValues()
{
// given
IEnumerable<IEnumerable<int>> orderedLists = new[]
{
new [] {1, 5, 7},
new [] {1, 2, 4, 6, 7}
};
// test
IEnumerable<int> merged = orderedLists.MergeOrderedLists(i => i);
// expect
merged.ShouldAllBeEquivalentTo(new [] { 1, 1, 2, 4, 5, 6, 7, 7 });
}
My version of sixlettervariables's answer. I reduced the number of calls to orderFunc (each element only passes through orderFunc once), and in the case of ties, sorting is skipped. This is optimized for small numbers of sources, larger numbers of elements within each source and possibly an expensive orderFunc.
public static IEnumerable<T> MergePreserveOrder<T, TOrder>(
this IEnumerable<IEnumerable<T>> sources,
Func<T, TOrder> orderFunc)
where TOrder : IComparable<TOrder>
{
Dictionary<TOrder, List<IEnumerable<T>>> keyedSources =
sources.Select(source => source.GetEnumerator())
.Where(e => e.MoveNext())
.GroupBy(e => orderFunc(e.Current))
.ToDictionary(g => g.Key, g => g.ToList());
while (keyedSources.Any())
{
//this is the expensive line
KeyValuePair<TOrder, List<IEnumerable<T>>> firstPair = keyedSources
.OrderBy(kvp => kvp.Key).First();
keyedSources.Remove(firstPair.Key);
foreach(IEnumerable<T> e in firstPair.Value)
{
yield return e.Current;
if (e.MoveNext())
{
TOrder newKey = orderFunc(e.Current);
if (!keyedSources.ContainsKey(newKey))
{
keyedSources[newKey] = new List<IEnumerable<T>>() {e};
}
else
{
keyedSources[newKey].Add(e);
}
}
}
}
}
I'm betting this could be further improved by a SortedDictionary, but am not brave enough to try a solution using one without an editor.
Here is a modern implementation that is based on the new and powerful PriorityQueue<TElement, TPriority> class (.NET 6). It combines the low overhead of user7116's solution, with the O(log n) complexity of tanascius's solution (where N is the number of sources). It outperforms most of the other implementations presented in this question (I didn't measure them all), either slightly for small N, or massively for large N.
public static IEnumerable<TSource> MergeSorted<TSource, TKey>(
this IEnumerable<IEnumerable<TSource>> sortedSources,
Func<TSource, TKey> keySelector,
IComparer<TKey> comparer = default)
{
List<IEnumerator<TSource>> enumerators = new();
try
{
foreach (var source in sortedSources)
enumerators.Add(source.GetEnumerator());
var queue = new PriorityQueue<IEnumerator<TSource>, TKey>(comparer);
foreach (var enumerator in enumerators)
{
if (enumerator.MoveNext())
queue.Enqueue(enumerator, keySelector(enumerator.Current));
}
while (queue.TryPeek(out var enumerator, out _))
{
yield return enumerator.Current;
if (enumerator.MoveNext())
queue.EnqueueDequeue(enumerator, keySelector(enumerator.Current));
else
queue.Dequeue();
}
}
finally
{
foreach (var enumerator in enumerators) enumerator.Dispose();
}
}
In order to keep the code simple, all enumerators are disposed at the end of the combined enumeration. A more sophisticated implementation would dispose each enumerator immediately after its completion.
This looks like a terribly useful function to have around so i decided to take a stab at it. My approach is a lot like heightechrider in that it breaks the problem down into merging two sorted IEnumerables into one, then taking that one and merging it with the next in the list. There is most likely some optimization you can do but it works with my simple testcase:
public static IEnumerable<T> mergeSortedEnumerables<T>(
this IEnumerable<IEnumerable<T>> listOfLists,
Func<T, T, Boolean> func)
{
IEnumerable<T> l1 = new List<T>{};
foreach (var l in listOfLists)
{
l1 = l1.mergeTwoSorted(l, func);
}
foreach (var t in l1)
{
yield return t;
}
}
public static IEnumerable<T> mergeTwoSorted<T>(
this IEnumerable<T> l1,
IEnumerable<T> l2,
Func<T, T, Boolean> func)
{
using (var enumerator1 = l1.GetEnumerator())
using (var enumerator2 = l2.GetEnumerator())
{
bool enum1 = enumerator1.MoveNext();
bool enum2 = enumerator2.MoveNext();
while (enum1 || enum2)
{
T t1 = enumerator1.Current;
T t2 = enumerator2.Current;
//if they are both false
if (!enum1 && !enum2)
{
break;
}
//if enum1 is false
else if (!enum1)
{
enum2 = enumerator2.MoveNext();
yield return t2;
}
//if enum2 is false
else if (!enum2)
{
enum1 = enumerator1.MoveNext();
yield return t1;
}
//they are both true
else
{
//if func returns true then t1 < t2
if (func(t1, t2))
{
enum1 = enumerator1.MoveNext();
yield return t1;
}
else
{
enum2 = enumerator2.MoveNext();
yield return t2;
}
}
}
}
}
Then to test it:
List<int> ws = new List<int>() { 1, 8, 9, 16, 17, 21 };
List<int> xs = new List<int>() { 2, 7, 10, 15, 18 };
List<int> ys = new List<int>() { 3, 6, 11, 14 };
List<int> zs = new List<int>() { 4, 5, 12, 13, 19, 20 };
List<IEnumerable<int>> lss = new List<IEnumerable<int>> { ws, xs, ys, zs };
foreach (var v in lss.mergeSortedEnumerables(compareInts))
{
Console.WriteLine(v);
}
I was asked this question as an interview question this evening and did not have a great answer in the 20 or so minutes allotted. So I forced myself to write an algorithm without doing any searches. The constraint was that the inputs were already sorted. Here's my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Merger
{
class Program
{
static void Main(string[] args)
{
int[] a = { 1, 3, 6, 102, 105, 230 };
int[] b = { 101, 103, 112, 155, 231 };
var mm = new MergeMania();
foreach(var val in mm.Merge<int>(a, b))
{
Console.WriteLine(val);
}
Console.ReadLine();
}
}
public class MergeMania
{
public IEnumerable<T> Merge<T>(params IEnumerable<T>[] sortedSources)
where T : IComparable
{
if (sortedSources == null || sortedSources.Length == 0)
throw new ArgumentNullException("sortedSources");
//1. fetch enumerators for each sourc
var enums = (from n in sortedSources
select n.GetEnumerator()).ToArray();
//2. fetch enumerators that have at least one value
var enumsWithValues = (from n in enums
where n.MoveNext()
select n).ToArray();
if (enumsWithValues.Length == 0) yield break; //nothing to iterate over
//3. sort by current value in List<IEnumerator<T>>
var enumsByCurrent = (from n in enumsWithValues
orderby n.Current
select n).ToList();
//4. loop through
while (true)
{
//yield up the lowest value
yield return enumsByCurrent[0].Current;
//move the pointer on the enumerator with that lowest value
if (!enumsByCurrent[0].MoveNext())
{
//remove the first item in the list
enumsByCurrent.RemoveAt(0);
//check for empty
if (enumsByCurrent.Count == 0) break; //we're done
}
enumsByCurrent = enumsByCurrent.OrderBy(x => x.Current).ToList();
}
}
}
}
Hope it helps.
An attempt to improve on #cdiggins's answer.
This implementation works correctly if two elements that compare as equal are contained in two different sequences (i. e. doesn't have the flaw mentioned by #ChadHenderson).
The algorithm is described in Wikipedia, the complexity is O(m log n), where n is the number of lists being merged and m is the sum of the lengths of the lists.
The OrderedBag<T> from Wintellect.PowerCollections is used instead of a heap-based priority queue, but it doesn't change the complexity.
public static IEnumerable<T> Merge<T>(
IEnumerable<IEnumerable<T>> listOfLists,
Func<T, T, int> comparison = null)
{
IComparer<T> cmp = comparison != null
? Comparer<T>.Create(new Comparison<T>(comparison))
: Comparer<T>.Default;
List<IEnumerator<T>> es = listOfLists
.Select(l => l.GetEnumerator())
.Where(e => e.MoveNext())
.ToList();
var bag = new OrderedBag<IEnumerator<T>>(
(e1, e2) => cmp.Compare(e1.Current, e2.Current));
es.ForEach(e => bag.Add(e));
while (bag.Count > 0)
{
IEnumerator<T> e = bag.RemoveFirst();
yield return e.Current;
if (e.MoveNext())
{
bag.Add(e);
}
}
}
Each list being merged should be already sorted. This method will locate the equal elements with respect to the order of their lists. For example, if elements Ti == Tj, and they are respectively from list i and list j (i < j), then Ti will be in front of Tj in the merged result.
The complexity is O(mn), where n is the number of lists being merged and m is the sum of the lengths of the lists.
public static IEnumerable<T> Merge<T, TOrder>(this IEnumerable<IEnumerable<T>> TEnumerable_2, Func<T, TOrder> orderFunc, IComparer<TOrder> cmp=null)
{
if (cmp == null)
{
cmp = Comparer<TOrder>.Default;
}
List<IEnumerator<T>> TEnumeratorLt = TEnumerable_2
.Select(l => l.GetEnumerator())
.Where(e => e.MoveNext())
.ToList();
while (TEnumeratorLt.Count > 0)
{
int intMinIndex;
IEnumerator<T> TSmallest = TEnumeratorLt.GetMin(TElement => orderFunc(TElement.Current), out intMinIndex, cmp);
yield return TSmallest.Current;
if (TSmallest.MoveNext() == false)
{
TEnumeratorLt.RemoveAt(intMinIndex);
}
}
}
/// <summary>
/// Get the first min item in an IEnumerable, and return the index of it by minIndex
/// </summary>
public static T GetMin<T, TOrder>(this IEnumerable<T> self, Func<T, TOrder> orderFunc, out int minIndex, IComparer<TOrder> cmp = null)
{
if (self == null) throw new ArgumentNullException("self");
IEnumerator<T> selfEnumerator = self.GetEnumerator();
if (!selfEnumerator.MoveNext()) throw new ArgumentException("List is empty.", "self");
if (cmp == null) cmp = Comparer<TOrder>.Default;
T min = selfEnumerator.Current;
minIndex = 0;
int intCount = 1;
while (selfEnumerator.MoveNext ())
{
if (cmp.Compare(orderFunc(selfEnumerator.Current), orderFunc(min)) < 0)
{
min = selfEnumerator.Current;
minIndex = intCount;
}
intCount++;
}
return min;
}
I've took a more functional approach, hope this reads well.
First of all here is the merge method itself:
public static IEnumerable<T> MergeSorted<T>(IEnumerable<IEnumerable<T>> xss) where T :IComparable
{
var stacks = xss.Select(xs => new EnumerableStack<T>(xs)).ToList();
while (true)
{
if (stacks.All(x => x.IsEmpty)) yield break;
yield return
stacks
.Where(x => !x.IsEmpty)
.Select(x => new { peek = x.Peek(), x })
.MinBy(x => x.peek)
.x.Pop();
}
}
The idea is that we turn each IEnumerable into EnumerableStack that has Peek(), Pop() and IsEmpty members.
It works just like a regular stack. Note that calling IsEmpty might enumerate wrapped IEnumerable.
Here is the code:
/// <summary>
/// Wraps IEnumerable in Stack like wrapper
/// </summary>
public class EnumerableStack<T>
{
private enum StackState
{
Pending,
HasItem,
Empty
}
private readonly IEnumerator<T> _enumerator;
private StackState _state = StackState.Pending;
public EnumerableStack(IEnumerable<T> xs)
{
_enumerator = xs.GetEnumerator();
}
public T Pop()
{
var res = Peek(isEmptyMessage: "Cannot Pop from empty EnumerableStack");
_state = StackState.Pending;
return res;
}
public T Peek()
{
return Peek(isEmptyMessage: "Cannot Peek from empty EnumerableStack");
}
public bool IsEmpty
{
get
{
if (_state == StackState.Empty) return true;
if (_state == StackState.HasItem) return false;
ReadNext();
return _state == StackState.Empty;
}
}
private T Peek(string isEmptyMessage)
{
if (_state != StackState.HasItem)
{
if (_state == StackState.Empty) throw new InvalidOperationException(isEmptyMessage);
ReadNext();
if (_state == StackState.Empty) throw new InvalidOperationException(isEmptyMessage);
}
return _enumerator.Current;
}
private void ReadNext()
{
_state = _enumerator.MoveNext() ? StackState.HasItem : StackState.Empty;
}
}
Finally, here is the MinBy extension method in case haven't written one on your own already:
public static T MinBy<T, TS>(this IEnumerable<T> xs, Func<T, TS> selector) where TS : IComparable
{
var en = xs.GetEnumerator();
if (!en.MoveNext()) throw new Exception();
T max = en.Current;
TS maxVal = selector(max);
while(en.MoveNext())
{
var x = en.Current;
var val = selector(x);
if (val.CompareTo(maxVal) < 0)
{
max = x;
maxVal = val;
}
}
return max;
}
This is an alternate solution:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;
using System.Text.RegularExpressions;
namespace ConsoleApplication1
{
class Person
{
public string Name
{
get;
set;
}
public int Age
{
get;
set;
}
}
public class Program
{
public static void Main()
{
Person[] persons1 = new Person[] { new Person() { Name = "Ahmed", Age = 20 }, new Person() { Name = "Ali", Age = 40 } };
Person[] persons2 = new Person[] { new Person() { Name = "Zaid", Age = 21 }, new Person() { Name = "Hussain", Age = 22 } };
Person[] persons3 = new Person[] { new Person() { Name = "Linda", Age = 19 }, new Person() { Name = "Souad", Age = 60 } };
Person[][] personArrays = new Person[][] { persons1, persons2, persons3 };
foreach(Person person in MergeOrderedLists<Person, int>(personArrays, person => person.Age))
{
Console.WriteLine("{0} {1}", person.Name, person.Age);
}
Console.ReadLine();
}
static IEnumerable<T> MergeOrderedLists<T, TOrder>(IEnumerable<IEnumerable<T>> orderedlists, Func<T, TOrder> orderBy)
{
List<IEnumerator<T>> enumeratorsWithData = orderedlists.Select(enumerable => enumerable.GetEnumerator())
.Where(enumerator => enumerator.MoveNext()).ToList();
while (enumeratorsWithData.Count > 0)
{
IEnumerator<T> minEnumerator = enumeratorsWithData[0];
for (int i = 1; i < enumeratorsWithData.Count; i++)
if (((IComparable<TOrder>)orderBy(minEnumerator.Current)).CompareTo(orderBy(enumeratorsWithData[i].Current)) >= 0)
minEnumerator = enumeratorsWithData[i];
yield return minEnumerator.Current;
if (!minEnumerator.MoveNext())
enumeratorsWithData.Remove(minEnumerator);
}
}
}
}
I'm suspicious LINQ is smart enough to take advantage of the prior existing sort order:
IEnumerable<string> BiggerSortedList = BigListOne.Union(BigListTwo).OrderBy(s => s);

Categories

Resources