Skip to content

Instantly share code, notes, and snippets.

@jonelf
Created October 22, 2012 09:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonelf/3930708 to your computer and use it in GitHub Desktop.
Save jonelf/3930708 to your computer and use it in GitHub Desktop.
Merge lists in C# added MergePreserveOrder
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
namespace test
{
class Person
{
public int Number { get; set; }
public string Name { get; set; }
}
class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person p1, Person p2)
{
return p1.Number == p2.Number;
}
public int GetHashCode(Person p)
{
return p.Number;
}
}
static class ExtMethods
{
public static IEnumerable<T> MergePreserveOrder<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
}
}
}
class MainClass
{
public static void Main(string[] args)
{
var nameParts = new[] { "fe", "se", "ra", "ko", "il", "as", "os", "de", "ik" };
var rnd = new Random();
var list1 = Enumerable.Range( 0, 1000).Select(n => new Person() { Number = n, Name = RandomName(rnd, nameParts) }).ToList();
var list2 = Enumerable.Range(500, 1000).Select(n => new Person() { Number = n, Name = RandomName(rnd, nameParts) }).ToList();
var watch = new Stopwatch();
for (int _ = 0; _ < 5; _++)
{
Console.Write("Lists and LINQ merge: ");
watch.Restart();
for (int i = 0; i < 100; i++)
{
var merged = new List<Person>(list1);
merged.AddRange(list2.Where(p2 =>
list1.All(p1 => p1.Number != p2.Number)));
}
Console.WriteLine(watch.ElapsedMilliseconds + "ms");
Console.Write("Dictionary merge: ");
watch.Restart();
for (int i = 0; i < 100; i++)
{
var dict = list2.ToDictionary(p => p.Number);
foreach (var person in list1)
{
dict[person.Number] = person;
}
var merged = dict.Values.ToList();
}
Console.WriteLine(watch.ElapsedMilliseconds + "ms");
Console.Write("LINQ Union and IEqualityComparer: ");
watch.Restart();
for (int i = 0; i < 100; i++)
{
var merged = list1.Union(list2, new PersonComparer()).ToList();
}
Console.WriteLine(watch.ElapsedMilliseconds + "ms");
Console.Write("HashSet and IEqualityComparer: ");
watch.Restart();
for (int i = 0; i < 100; i++)
{
var hs = new HashSet<Person>(list1, new PersonComparer());
hs.UnionWith(list2);
var merged = hs.ToList();
}
Console.WriteLine(watch.ElapsedMilliseconds + "ms\n");
Console.Write("MergePreserveOrder ");
watch.Restart();
for (int i = 0; i < 100; i++)
{
var lists = new List<IEnumerable<Person>>() {list1, list2};
var merged = lists.MergePreserveOrder(p => p.Number).ToList();
}
Console.WriteLine(watch.ElapsedMilliseconds + "ms\n");
}
Console.ReadKey();
}
public static string RandomName(Random rnd, string[] nameParts)
{
return String.Join("", Enumerable.Range(0, 4).Select(_ => nameParts[rnd.Next(nameParts.Length)]).ToArray());
}
}
}
@jonelf
Copy link
Author

jonelf commented Oct 22, 2012

Result:
Lists and LINQ merge: 4774ms
Dictionary merge: 16ms
LINQ Union and IEqualityComparer: 23ms
HashSet and IEqualityComparer: 18ms
MergePreserveOrder 57ms

@jonelf
Copy link
Author

jonelf commented Oct 22, 2012

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment