Getting the exact diff from the Ienumerable.except - c#

I am using LINQ ENumerable.except to get the diff out of two lists of MyType. When I find a diff, I need to retrieve what was different in both base and compare list for the corresponding diff item. LIke,
I have two lists to compare
Base:
Name="1",Type="a",Switch="384347324372432"
Name="1",Type="b",Switch="43432432423"
Name="2",Type="q",Switch="4324324234"
Compare List:
Name="1",Type="a",Switch="aAAAA384347324372432"
Name="1",Type="c",Switch="23432432"
Name="2",Type="q",Switch="3423432423432"
Name="2",Type="q",Switch="4324324234"
Ouuput would be
Found diff.
Base:
Name="1",Type="a",Switch="384347324372432"
Corresponding compare value:
Name="1",Type="a",Switch="aAAAA384347324372432"
etc...
I have written my own class object, say MyType for storing these as items properties
Defined a custom comparer like
class mycomparer: System.Collections.Generic.IEqualityComparer<MyType>
{
public mycomparer() {}
public bool Equals(MyType type1, MyType type2)
{
return ( (type1.Name.ToLower() == type2.Name.ToLower()) && (type1.Type.ToLower() == type2.Type.ToLower()) (type1.Switch.ToLower() == type2.Switch.ToLower())
}
public int GetHashCode(MyType type)
{
return string.concat(type.Name, type.Type, type.Switch).ToLower().GetHashCode();
}
}
In my code, I use
MyComparer mine = new MyComparer();
IEnumerable<MyType> diff = baseList.Except(compareList,mine);
I get the diff correctly. But for the values that are different in baselist, I want to know what the corresponding value in comparelist was.
I do
foreach(Mytype type in diff)
{
Console.writeline(type.Name+type.Type+type.Switch);
}
How to I get the corresponding values for compare list.
I tried something like,
IEnumerable<MyType> compareValue = Comparelist.Where(tempFile => tempFile.Name.ToLower() == type.Name.ToLower() && tempFile.Type.ToLower() == process.Type.ToLower()
(Basically, switch is what could be different here mostly)
But the problem is, in some cases, there may be duplicate Name="" Type="", so the above where retrieves more than one item, so the corresponding comparelist value would be different.
I tried writing the diff values from Equal methods, but didnt work for me.
UPdate:
In the case when duplicate name and type are found and switch has a mismatch, I think diff is calculated correctly, but when the output is written to console, incorrect diff value is written, here is the sample.
Base:
Name="2",Type="q",Switch="4324324234"
Name="2",Type="q",Switch="3423432423432"
Compare List:
Name="2",Type="q",Switch="4324324234"
Name="2",Type="q",Switch="3423432423432dwqdwqdwqdqwdwdq"
base.except(compare) (as used in my sample code or Moho's solution) gets the diff correctly,
but with
diffSwitches.ToList().ForEach
I will get something like
base: Name="2",Type="q",Switch="3423432423432"
Compare: Name="2",Type="q",Switch="4324324234"
You see that is not the corresponding mismatch. Infact Switch="4324324234" is correctly matched. This is the problem I see.
Thanks.

Let both list be named list and list2. This is a solution:
var common = list1.Intersect(list2, new mycomparer());
var result = list1.Concat(list2)
.Except(common, new mycomparer())
.GroupBy (x => new { x.Name, x.Type } );
So both lists are concatenated, then the elements common in both lists are removed, and finally the remaining items are grouped by Name and Type.
Maybe useful to add that if MyType is a struct, you don't need the comparer, because structs are equal when their values are equal.

Try this
baseList.Except( compareList, mine ).Union( compareList.Except( baseList.Except, mine ) )
Edit after new requirements:
var baseList = new List<MyType>()
{
new MyType() { Name="1",Type="a",Switch="384347324372432" },
new MyType() { Name="1",Type="b",Switch="43432432423" },
new MyType() { Name="2",Type="q",Switch="4324324234" }
};
var compareList = new List<MyType>()
{
new MyType() { Name="1",Type="a",Switch="aAAAA384347324372432" },
new MyType() { Name="1",Type="c",Switch="23432432" },
new MyType() { Name="2",Type="q",Switch="3423432423432" },
new MyType() { Name="2",Type="q",Switch="4324324234" }
};
// matched Name/Type w/ different Switch
var diffSwitches = baseList.Join( compareList,
bl => new { bl.Name, bl.Type },
cl => new { cl.Name, cl.Type },
( bl, cl ) => new {
baseItem = bl,
compareItem = cl } )
.Where( o => o.baseItem.Switch != o.compareItem.Switch );
diffSwitches.ToList().ForEach(i => Console.WriteLine("{0}-{1}: {2}; {3}", i.baseItem.Name, i.baseItem.Type, i.baseItem.Switch, i.compareItem.Switch));

I think you can get the results you want with a slight change to your comparer:
class mycomparer : System.Collections.Generic.IEqualityComparer<Item>
{
bool customcomparer = false;
public mycomparer(bool custom = false)
{
customcomparer = custom;
}
public bool Equals(Item type1, Item type2)
{
return GetHashCode(type1) == GetHashCode(type2);
}
public int GetHashCode(Item type)
{
if (customcomparer)
return string.Concat(type.Name, type.Type).ToLower().GetHashCode();
return string.Concat(type.Name, type.Type, type.Switch).ToLower().GetHashCode();
}
}
And your class set up like this:
public class MyType
{
public String Name, Type, Switch;
public MyType()
{
}
public MyType(string _name, string _type, string _switch)
{
Name = _name;
Type = _type;
Switch = _switch;
}
public override string ToString()
{
return "Name = " + this.Name + ",Type = " + this.Type + ",Switch = " + this.Switch;
}
}
And a second IEnumerable like this:
private void button3_Click(object sender, EventArgs e)
{
mycomparer mine = new mycomparer();
mycomparer mine2 = new mycomparer(true);
IEnumerable<MyType> diff = baseList.Except(compareList, mine);
IEnumerable<MyType> diff2 = compareList.Intersect(diff, mine2);
string message = "Base:\n";
foreach (MyType item in diff)
{
message += item.ToString() + "\n";
}
message += "Corresponding compare value:\n";
foreach (MyType item in diff2)
{
message += item.ToString() + "\n";
}
MessageBox.Show(message);
}
I noticed diff returns 2 items Name 1 Type a and Name 1 Type b. This is from your original code so I left it like that.

Related

Linq Except returning entire first set with custom comparer

I have two List<MyObject>, with MyObject like so
public class MyObject
{
public string Item {get; set;};
public string Country {get; set;};
public int Phone {get; set;};
}
I wish to find objects that are in List A but not in List B, however the objects do not share reference so I want to compare by all the properties on the object. I have this which overrides IEqualityComparer
public class MyObjectComparer : IEqualityComparer<MyObject>
{
private PropertyInfo[] publicInstanceProperties;
public MyObjectComparer()
{
publicInstanceProperties = typeof(MyObject).GetProperties(BindingFlags.Public | BindingFlags.Instance);
}
public bool Equals(MyObject x, MyObject y)
{
foreach (PropertyInfo property in publicInstanceProperties)
{
if (property.CanRead && property.GetIndexParameters().Length == 0 && !property.GetValue(x).Equals(property.GetValue(y)))
{
return false;
}
}
return true;
}
public int GetHashCode(MyObject obj)
{
long hashCodes = 0;
foreach (PropertyInfo property in typeof(MyObject).GetProperties())
{
var value = property.GetValue(obj) ?? 0;
hashCodes += value.GetHashCode();
}
return (int)(hashCodes % int.MaxValue);
}
}
And I call it like this:
var comparer = new MyObjectComparer();
var result = listA.Except(listB, comparer);
However when I do this the result is always just the entire contents of list A, even if the properties of an object in list A and list B are identical. For instance, if I have
var listA = new List<MyObject>
{
new MyObject
{
Item = "ItemName",
Country = "Spain",
Phone = 123456789
},
new MyObject
{
Item = "DifferentName",
Country = "Portugal",
Phone = 00000
}
};
var listB = new List<MyObject>
{
new MyObject
{
Item = "ItemName",
Country = "Spain",
Phone = 123456789
}
};
Then my code returns the entire set of listA, even though it should only return the second item in listA. Please let me know if I am failing to understand how this works. Thank you.
Demo on dotnet fiddle
I've run your code and it gives us the result as expected.
var comparer = new MyObjectComparer();
var result = listA.Except(listB, comparer).ToList();
Console.WriteLine("Count: " + result.Count);
Console.WriteLine(JsonConvert.SerializeObject(result[0]));
Output
Count: 1
{"Item":"DifferentName","Country":"Portugal","Phone":0}

how to yield an anonymous class in IEnumerable.GroupBy<T> "on the fly" (without enumerating result) in C# LINQ?

I do like this now (thanks to StackOverflow):
IEnumerable<object> Get()
{
var groups = _myDatas.GroupBy(
data => new { Type = data.GetType(), Id = data.ClassId, Value = data.Value },
(key, rows) => new
{
ClassId = key.Id,
TypeOfObject = key.Type,
Value = key.Value,
Count = rows.Count()
}));
foreach (var item in groups)
{
yield return item;
}
}
IEnumerable<MyData> _myDatas;
But is possible to make faster or more "elegant" by not having last foreach loop, but yielding it when the group/anonymous class instance is created?
I would guess fastest way would be to write it open and:
sort the _myDatas
enumerate it and when group changes yield the last group
But I'm trying to learn some LINQ (and C# features in general) so I don't want to do that.
The rest of example is here:
public abstract class MyData
{
public int ClassId;
public string Value;
//...
}
public class MyAction : MyData
{
//...
}
public class MyObservation : MyData
{
//...
}
You should be able to return groups directly, though you might need to change your return type from IEnumerable<Object> to just IEnumerable.
So:
IEnumerable Get()
{
var groups = _myDatas.GroupBy(
// Key selector
data => new {
Type = data.GetType(),
Id = data.ClassId,
Value = data.Value
},
// Element projector
(key, rows) => new
{
ClassId = key.Id,
TypeOfObject = key.Type,
Value = key.Value,
Count = rows.Count()
}
);
return groups;
}
groups has the type IEnumerable< IGrouping< TKey = Anonymous1, TElement = Anonymous2 > >, so you can return it directly.

IEqualityComparer for Annoymous Type

Firstly I have seen IEqualityComparer for anonymous type and the answers there do not answer my question, for the obvious reason that I need an IEqualityComparer not and IComparer for use with Linq's Distinct() method. I have checked the other answers too and these fall short of a solution...
The Problem
I have some code to manipulate and pull records in from a DataTable
var glext = m_dtGLExt.AsEnumerable();
var cflist =
(from c in glext
orderby c.Field<string>(m_strpcCCType),
c.Field<string>(m_strpcCC),
c.Field<string>(m_strpcCCDesc),
c.Field<string>(m_strpcCostItem)
select new
{
CCType = c.Field<string>(m_strpcCCType),
CC = c.Field<string>(m_strpcCC),
CCDesc = c.Field<string>(m_strpcCCDesc),
CostItem = c.Field<string>(m_strpcCostItem)
}).Distinct();
but I need the distinct method to be case insensitive. What is throwing me here is the use of anonymous types.
Attempted Solution 1
If I had SomeClass which had concrete objects I could obviously do
public class SumObject
{
public string CCType { get; set; }
public string CC { get; set; }
public string CCDesc { get; set; }
public string CostItem { get; set; }
}
I could obviously do this
List<SumObject> lso = new List<SumObject>()
{
new SumObject() { CCType = "1-OCC", CC = "300401", CCDesc = "Rooney", CostItem = "I477" },
new SumObject() { CCType = "1-OCC", CC = "300401", CCDesc = "Zidane", CostItem = "I677" },
new SumObject() { CCType = "1-OCC", CC = "300401", CCDesc = "Falcao", CostItem = "I470" },
};
var e = lso.Distinct(new SumObjectComparer()); // Great :]
where
class SumObjectComparer : IEqualityComparer<SumObject>
{
public bool Equals(SumObject x, SumObject y)
{
if (Object.ReferenceEquals(x, y))
return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.CCType.CompareNoCase(y.CCType) == 0 &&
x.CC.CompareNoCase(y.CC) == 0 &&
x.CCDesc.CompareNoCase(y.CCDesc) == 0 &&
x.CostItem.CompareNoCase(y.CostItem) == 0;
}
public int GetHashCode(SumObject o)
{
if (Object.ReferenceEquals(o, null))
return 0;
int hashCCType = String.IsNullOrEmpty(o.CCType) ?
0 : o.CCType.ToLower().GetHashCode();
int hashCC = String.IsNullOrEmpty(o.CC) ?
0 : o.CC.ToLower().GetHashCode();
int hashCCDesc = String.IsNullOrEmpty(o.CCDesc) ?
0 : o.CCDesc.ToLower().GetHashCode();
int hashCostItem = String.IsNullOrEmpty(o.CostItem) ?
0 : o.CostItem.ToLower().GetHashCode();
return hashCCType ^ hashCC ^ hashCCDesc ^ hashCostItem;
}
}
However, the use of anonymous types in the above Linq query are throwing me.
Attempted Solution 2
To attempt another solution to this (and because I have the same issue elsewhere) I generated the following generic comparer class
public class GenericEqualityComparer<T> : IEqualityComparer<T>
{
Func<T, T, bool> compareFunction;
Func<T, int> hashFunction;
public GenericEqualityComparer(Func<T, T, bool> compareFunction, Func<T, int> hashFunction)
{
this.compareFunction = compareFunction;
this.hashFunction = hashFunction;
}
public bool Equals(T x, T y) { return compareFunction(x, y); }
public int GetHashCode(T obj) { return hashFunction(obj); }
}
so that I could attempt to do
var comparer = new GenericEqualityComparer<dynamic>(
(x, y) => { /* My equality stuff */ },
o => { /* My hash stuff */ });
but this casts the returned value as IEnumerable<dynamic> which in turn effects my forthcoming use of cflist, so that in a following query the join fails.
var cf =
(from o in cflist
join od in glext
on new { o.CCType, o.CC, o.CCDesc, o.CostItem } equals new
{
CCType = od.Field<string>(m_strpcCCType),
CC = od.Field<string>(m_strpcCC),
CCDesc = od.Field<string>(m_strpcCCDesc),
CostItem = od.Field<string>(m_strpcCostItem)
}
into c
select new { ... }
I don't want to get into ugly casting to and from IEnumerable<T>s due to the heavy use of this code...
Question
Is there a way I can create my an IEquailityComparer for my anonymous types?
Thanks for your time.
Is there a way I can create my an IEquailityComparer for my anonymous types?
Sure. You just need to use type inference. For example, you could have something like:
public static class InferredEqualityComparer
{
public static IEqualityComparer<T> Create<T>(
IEnumerable<T> example,
Func<T, T, bool> equalityCheck,
Func<T, int> hashCodeProvider)
{
return new EqualityComparerImpl<T>(equalityCheck, hashCodeProvider);
}
private sealed class EqualityComparerImpl<T> : IEqualityComparer<T>
{
// Implement in the obvious way, remembering the delegates and
// calling them appropriately.
}
}
Then:
var glext = m_dtGLExt.AsEnumerable();
var query = from c in glext
orderby ...
select new { ... };
var comparer = InferredEqualityComparer.Create(query,
(x, y) => { ... },
o => { ... }
);
var distinct = query.Distinct(comparer);
Basically the first parameter to the method is just used for type inference, so that the compiler can work out what type to use for the lambda expression parameters.
You could create the comparer ahead of time by creating a sample of the anonymous type:
var sample = new[] { new { ... } };
var comparer = InferredExqualityComparer.Create(sample, ...);
var distinct = (... query here ... ).Distinct(comparer);
but then any time you change the query you've got to change the sample too.
This post may get what you want. Although for .NET 2.0 it also works for newer versions (see the bottom of this post for how to achieve this). In contrast to Jon Skeets solution we won´t use a factory-method like create. But this is only syntactic sugar I think.

Sort a List<T> by enum where enum is out of order

I have a List of messages.
Each message has a type.
public enum MessageType
{
Foo = 0,
Bar = 1,
Boo = 2,
Doo = 3
}
The enum names are arbitrary and cannot be changed.
I need to return the list sorted as: Boo, Bar, Foo, Doo
My current solution is to create a tempList, add the values in the order I want, return the new list.
List<Message> tempList = new List<Message>();
tempList.AddRange(messageList.Where(m => m.MessageType == MessageType.Boo));
tempList.AddRange(messageList.Where(m => m.MessageType == MessageType.Bar));
tempList.AddRange(messageList.Where(m => m.MessageType == MessageType.Foo));
tempList.AddRange(messageList.Where(m => m.MessageType == MessageType.Doo));
messageList = tempList;
How can I do this with an IComparer?
An alternative to using IComparer would be to build an ordering dictionary.
var orderMap = new Dictionary<MessageType, int>() {
{ MessageType.Boo, 0 },
{ MessageType.Bar, 1 },
{ MessageType.Foo, 2 },
{ MessageType.Doo, 3 }
};
var orderedList = messageList.OrderBy(m => orderMap[m.MessageType]);
So, let's write our own comparer:
public class MyMessageComparer : IComparer<MessageType> {
protected IList<MessageType> orderedTypes {get; set;}
public MyMessageComparer() {
// you can reorder it's all as you want
orderedTypes = new List<MessageType>() {
MessageType.Boo,
MessageType.Bar,
MessageType.Foo,
MessageType.Doo,
};
}
public int Compare(MessageType x, MessageType y) {
var xIndex = orderedTypes.IndexOf(x);
var yIndex = orderedTypes.IndexOf(y);
return xIndex.CompareTo(yIndex);
}
};
How to use:
messages.OrderBy(m => m.MessageType, new MyMessageComparer())
There is a easier way: just create ordereTypes list and use another overload of OrderBy:
var orderedTypes = new List<MessageType>() {
MessageType.Boo,
MessageType.Bar,
MessageType.Foo,
MessageType.Doo,
};
messages.OrderBy(m => orderedTypes.IndexOf(m.MessageType)).ToList();
Hm.. Let's try to take advantages from writing our own IComparer. Idea: write it like our last example but in some other semantic. Like this:
messages.OrderBy(
m => m.MessageType,
new EnumComparer<MessageType>() {
MessageType.Boo,
MessageType.Foo }
);
Or this:
messages.OrderBy(m => m.MessageType, EnumComparer<MessageType>());
Okay, so what we need. Our own comparer:
Must accept enum as generic type (how to solve)
Must be usable with collection initializer syntax (how to)
Must sort by default order, when we have no enum values in our comparer (or some enum values aren't in our comparer)
So, here is the code:
public class EnumComparer<TEnum>: IComparer<TEnum>, IEnumerable<TEnum> where TEnum: struct, IConvertible {
protected static IList<TEnum> TypicalValues { get; set; }
protected IList<TEnum> _reorderedValues;
protected IList<TEnum> ReorderedValues {
get { return _reorderedValues.Any() ? _reorderedValues : TypicalValues; }
set { _reorderedValues = value; }
}
static EnumComparer() {
if (!typeof(TEnum).IsEnum)
{
throw new ArgumentException("T must be an enumerated type");
}
TypicalValues = new List<TEnum>();
foreach (TEnum value in Enum.GetValues(typeof(TEnum))) {
TypicalValues.Add(value);
};
}
public EnumComparer(IList<TEnum> reorderedValues = null) {
if (_reorderedValues == null ) {
_reorderedValues = new List<TEnum>();
return;
}
_reorderedValues = reorderedValues;
}
public void Add(TEnum value) {
if (_reorderedValues.Contains(value))
return;
_reorderedValues.Add(value);
}
public int Compare(TEnum x, TEnum y) {
var xIndex = ReorderedValues.IndexOf(x);
var yIndex = ReorderedValues.IndexOf(y);
// no such enums in our order list:
// so this enum values must be in the end
// and must be ordered between themselves by default
if (xIndex == -1) {
if (yIndex == -1) {
xIndex = TypicalValues.IndexOf(x);
yIndex = TypicalValues.IndexOf(y);
return xIndex.CompareTo(yIndex);
}
return -1;
}
if (yIndex == -1) {
return -1; //
}
return xIndex.CompareTo(yIndex);
}
public void Clear() {
_reorderedValues = new List<TEnum>();
}
private IEnumerable<TEnum> GetEnumerable() {
return Enumerable.Concat(
ReorderedValues,
TypicalValues.Where(v => !ReorderedValues.Contains(v))
);
}
public IEnumerator<TEnum> GetEnumerator() {
return GetEnumerable().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerable().GetEnumerator();
}
}
So, well, let's make sorting more faster. We need to override default OrderBy method for our enums:
public static class LinqEnumExtensions
{
public static IEnumerable<TSource> OrderBy<TSource, TEnum>(this IEnumerable<TSource> source, Func<TSource, TEnum> selector, EnumComparer<TEnum> enumComparer) where TEnum : struct, IConvertible
{
foreach (var enumValue in enumComparer)
{
foreach (var sourceElement in source.Where(item => selector(item).Equals(enumValue)))
{
yield return sourceElement;
}
}
}
}
Yeah, that's lazy. You can google how yield works. Well, let's test speed. Simple benchmark: http://pastebin.com/P8qaU20Y. Result for n = 1000000;
Enumerable orderBy, elementAt: 00:00:04.5485845
Own orderBy, elementAt: 00:00:00.0040010
Enumerable orderBy, full sort: 00:00:04.6685977
Own orderBy, full sort: 00:00:00.4540575
We see, that our own orderBy by is more lazy that standart order by (yeah, it doesn't need to sort everything). And faster even for fullsort.
Problems in this code: it doesn't support ThenBy(). If you need this, you can write your own linq extension that returns IOrderedEnumerable There are a blog post series by Jon Skeet which goes into LINQ to Objects in some depth, providing a complete alternative implementation. The basis of IOrderedEnumerable is covered in part 26a and 26b, with more details and optimization in 26c and 26d.
Instead of using an IComparer, you could also use a SelectMany approach, which should have better performance for large message lists, if you have a fixed number of message types.
var messageTypeOrder = new [] {
MessageType.Boo,
MessageType.Bar,
MessageType.Foo,
MessageType.Doo,
};
List<Message> tempList = messageTypeOrder
.SelectMany(type => messageList.Where(m => m.MessageType == type))
.ToList();
You may avoid writing a completely new type just to implement IComparable. Use the Comparer class instead:
IComparer<Message> comparer = Comparer.Create<Message>((message) =>
{
// lambda that compares things
});
tempList.Sort(comparer);
You can build a mapping dictionary dynamically from the Enum values with LINQ like this:
var mappingDIctionary = new List<string>((string[])Enum.GetNames(typeof(Hexside)))
.OrderBy(label => label )
.Select((i,n) => new {Index=i, Label=n}).ToList();
Now any new values added to the Enum n future will automatically get properly mapped.
Also, if someone decides to renumber, refactor, or reorder the enumeration, everything is handled automatically.
Update:
As pointed out below, Alphabetical ordering was not called for; rather a semi- alphabetical ordering, so essentially random. Although not an answer to this particular question, this technique might be useful to future visitors, so I will leave it standing.
No need to have the mapping. This should give you the list and order based on the enum. You don't have to modify anything even when you change the enum's order or and new items...
var result = (from x in tempList
join y in Enum.GetValues(typeof(MessageType)).Cast<MessageType>()
on x equals y
orderby y
select y).ToList();
If you are about to get this working with Entity Framework (EF), you would have to spread out your enum in your OrderBy as such:
messageList.OrderBy(m =>
m.MessageType == MessageType.Boo ? 0 :
m.MessageType == MessageType.Bar ? 1 :
m.MessageType == MessageType.Foo ? 2 :
m.MessageType == MessageType.Doo ? 3 : 4
);
This creates a sub select with CASE WHEN, then ORDER BY on that temporary column.

Do a correct union on two List<SelectListItem>

retval.AddRange(oldList.Union(newList));
Both the oldList and newList are declared here
List<SelectListItem> oldList = new List<SelectListItem>();
List<SelectListItem> newList = new List<SelectListItem>();
I want to union these two lists, removing duplicate items that have the same "text" property. If there is a dupe between newList and oldList on the text property, it should keep the text/value pair of the newList.
The above union doesn't seem to be doing anything besides concat'ing the lists almost, don't know why.
What am I doing wrong?
I want to union these two lists, removing duplicate items that have the same "text" property.
The Union method will not handle this requirement. You could do this via:
retVal.AddRange(newList); // Since you want to keep all newList items, do this first
// Add in all oldList items where there isn't a match in new list
retVal.AddRange(oldList.Where(i => !newList.Any(n => i.Text == n.Text)));
From reading the documentation it appears that A.Union(B) in effect just adds to A those items from B that aren't already in A. That is:
When the object returned by this
method is enumerated, Union enumerates
first and second in that order and
yields each element that has not
already been yielded.
A quick test bears that out. So oldList.Union(newList) will keep the old values whereas newList.Union(oldList) will give you the new values.
Here's my test code:
class MyThing
{
public string Text { get; private set; }
public int Version { get; private set; }
public MyThing(string t, int v)
{
Text = t;
Version = v;
}
public override int GetHashCode()
{
return Text.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
MyThing other = obj as MyThing;
if (other == null)
return false;
return this.Text.Equals(other.Text);
}
}
static List<MyThing> oldList = new List<MyThing>()
{
new MyThing("Foo", 0),
new MyThing("Bar", 0),
new MyThing("Fooby", 0),
};
static List<MyThing> newList = new List<MyThing>()
{
new MyThing("Barby", 1),
new MyThing("Bar", 1)
};
static void DoIt()
{
var unionOldNew = oldList.Union(newList);
Console.WriteLine("oldList.Union(newList)");
foreach (var t in unionOldNew)
{
Console.WriteLine("{0}, {1}", t.Text, t.Version);
}
Console.WriteLine();
var unionNewOld = newList.Union(oldList);
Console.WriteLine("newList.Union(oldList)");
foreach (var t in unionNewOld)
{
Console.WriteLine("{0}, {1}", t.Text, t.Version);
}
}

Categories

Resources