Adding objects from one list to another - c#

I'm having two lists were one of the lists InboxTemp is automatically is filled with all the objects that exists. The other list NewMessages is randomly (10-60sec) recieving new objects. The problem I have is that I want allList to add the objects in newList without any duplicates.
public List<object> GetNewMessages()
{
if (NewMessages.Count > 0 && InboxTemp.Count > 0)
{
for (int j = 0; j < NewMessages.Count; j++)
{
for (int i = 0; i < InboxTemp.Count; i++)
{
if (InboxTemp[i].ID != NewMessages[j].ID)
{
InboxTemp.Add(NewMessages[j]);
}
}
}
}
NewMessages.Clear();
return InboxTemp;
}
The problem here is that we get duplicates, I just want the new objects to addup with the InboxTemp-list .

var newItems = NewMessages.Where(x => !InboxTemp.Any(z => z.ID == x.ID));
InboxTemp.AddRange(newItems);

If it is possible, please create class implementing IEqualityComparer to compare objects id.
class MyEqualityComparerer : IEqualityComparer<MyMessage>
{
public bool Equals(MyMessage x, MyMessage y)
{
if (x == null && y == null)
{
return true;
}
if (x == null)
{
return false;
}
if (y == null)
{
return false;
}
return x.Id == y.Id;
}
public int GetHashCode(MyMessage obj)
{
if(obj == null)
{
return 0;
}
return obj.Id.GetHashCode();
}
}
Then write get Distinct values from messages and add all not present in the old list to it
var newItems = newList.Distinct(new MyEqualityComparerer()).Except(oldList, new MyEqualityComparerer()).ToList();
oldList.AddRange(newItems);

You could use the modified code based on what you posted:
public List<object> GetNewMessages()
{
if (NewMessages.Count > 0 && InboxTemp.Count > 0)
{
for (int j = 0; j < NewMessages.Count; j++)
{
bool found = false;
int i = 0;
while(!found && i<InboxTemp.Count)
{
if (InboxTemp[i].ID == NewMessages[j].ID)
{
found = true;
}
j++;
}
if(!found)
InboxTemp.Add(NewMessages[j]);
}
}
NewMessages.Clear();
return InboxTemp;
}

Use except:
InboxTemp = InboxTemp.Except(NewMessages);
From MSDN:
Produces the set difference of two sequences by using the default equality comparer to compare values....
...The default equality comparer, Default, is used to compare values of
the types that implement the IEqualityComparer(Of T) generic
interface. To compare a custom data type, you need to implement this
interface and provide your own GetHashCode and Equals methods for the
type.
Source: MSDN Except

You can use Enumerable.Except to find all new messages.
public List<Msg> GetNewMessages()
{
var newMsg = NewMessages.Except(InboxTemp, new MsgComparer()).ToList();
foreach(var msg in newMsg)
InboxTemp.Add(msg);
return newMsg;
}
But you need to create a custom IEqualityComparer<T> for your object (i'll call it Msg):
public class MsgComparer: IEqualityComparer<Msg>
{
public bool Equals(Msg x1, Msg x2)
{
if (object.ReferenceEquals(x1, x2))
return true;
if (x1 == null || x2 == null)
return false;
return x1.ID.Equals(x2.ID);
}
public int GetHashCode(Msg obj)
{
return obj.ID.GetHashCode();
}
}

Related

c# Stabilise complicated sort [duplicate]

I want an alphabetic sort with one exception.
There is a Group with a Name = "Public" and an ID = "0" that I want first.
(would rather use ID = 0)
After that then sort the rest by Name.
This does not return public first.
public IEnumerable<GroupAuthority> GroupAuthoritysSorted
{
get
{
return GroupAuthoritys.OrderBy(x => x.Group.Name);
}
}
What I want is:
return GroupAuthoritys.Where(x => x.ID == 0)
UNION
GroupAuthoritys.Where(x => x.ID > 0).OrderBy(x => x.Group.Name);
GroupAuthority has a public property Group and Group has Public properties ID and Name.
I used basically the accepted answer
using System.ComponentModel;
namespace SortCustom
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
TestSort();
}
private void TestSort()
{
List<CustomSort> LCS = new List<CustomSort>();
LCS.Add(new CustomSort(5, "sss"));
LCS.Add(new CustomSort(6, "xxx"));
LCS.Add(new CustomSort(4, "xxx"));
LCS.Add(new CustomSort(3, "aaa"));
LCS.Add(new CustomSort(7, "bbb"));
LCS.Add(new CustomSort(0, "pub"));
LCS.Add(new CustomSort(2, "eee"));
LCS.Add(new CustomSort(3, "www"));
foreach (CustomSort cs in LCS) System.Diagnostics.Debug.WriteLine(cs.Name);
LCS.Sort();
foreach (CustomSort cs in LCS) System.Diagnostics.Debug.WriteLine(cs.Name);
}
}
public class CustomSort : Object, INotifyPropertyChanged, IComparable<CustomSort>
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null) PropertyChanged(this, e);
}
private Int16 id;
private string name;
public Int16 ID { get { return id; } }
public String Name { get { return name; } }
public int CompareTo(CustomSort obj)
{
if (this.ID == 0) return -1;
if (obj == null) return 1;
if (obj is CustomSort)
{
CustomSort comp = (CustomSort)obj;
if (comp.ID == 0) return 1;
return string.Compare(this.Name, comp.Name, true);
}
else
{
return 1;
}
}
public override bool Equals(Object obj)
{
// Check for null values and compare run-time types.
if (obj == null) return false;
if (!(obj is CustomSort)) return false;
CustomSort comp = (CustomSort)obj;
return (comp.ID == this.ID);
}
public override int GetHashCode()
{
return (Int32)ID;
}
public CustomSort(Int16 ID, String Name)
{
id = ID;
name = Name;
}
}
}
You need to use a comparison function, they are functions that from two instances of your type return an integer that return 0 if both are equals, a negative value if the first is less than the second and a positive value if the first is greater than the second.
MSDN has a nice table that is easier to follow than text (StackOverflow still doesn't support tables in 2014)
IComparer<T>
Most sort methods accept a custom comparer implementation of type IComparer<T> you should create one encapsulating your custom rules for Group :
class GroupComparer : IComparer<Group>
{
public int Compare(Group a, Group b)
{
if (a != null && b != null && (a.Id == 0 || b.Id == 0))
{
if (a.Id == b.Id)
{
// Mandatory as some sort algorithms require Compare(a, b) and Compare(b, a) to be consistent
return 0;
}
return a.Id == 0 ? -1 : 1;
}
if (a == null || b == null)
{
if (ReferenceEquals(a, b))
{
return 0;
}
return a == null ? -1 : 1;
}
return Comparer<string>.Default.Compare(a.Name, b.Name);
}
}
Usage:
items.OrderBy(_ => _, new GroupAuthorityComparer());
IComparable<T>
If it is the only way to compare Group instances you should make it implement IComparable<T> so that no aditional code is needed if anyone want to sort your class :
class Group : IComparable<Group>
{
...
public int CompareTo(Group b)
{
if (b != null && (Id == 0 || b.Id == 0))
{
if (Id == b.Id)
{
// Mandatory as some sort algorithms require Compare(a, b) and Compare(b, a) to be consistent
return 0;
}
return Id == 0 ? -1 : 1;
}
return Comparer<string>.Default.Compare(Name, b.Name);
}
}
Usage:
items.OrderBy(_ => _.Group);
The choice between one way or the other should be done depending on where this specific comparer is used: Is it the main ordering for this type of item or just the ordering that should be used in one specific case, for example only in some administrative view.
You can even go one level up and provide an IComparable<GroupAuthority> implementation (It's easy once Group implement IComparable<Group>):
class GroupAuthority : IComparable<GroupAuthority>
{
...
public int CompareTo(GroupAuthority b)
{
return Comparer<Group>.Default.Compare(Group, b.Group);
}
}
Usage:
items.OrderBy(_ => _);
The advantage of the last one is that it will be used automatically, so code like: GroupAuthoritys.ToList().Sort() will do the correct thing out of the box.
You can try something like this
list.Sort((x, y) =>
{
if (x.Id == 0)
{
return -1;
}
if (y.Id == 0)
{
return 1;
}
return x.Group.Name.CompareTo(y.Group.Name);
});
Where list is List<T>.
This method takes advantage of custom sort option provided by List<T> using Comparison<T> delegate.
Basically what this method does is, it just adds special condition for comparison when Id, If it is zero it will return a value indicating the object is smaller which makes the object to come in top of the list. If not, it sorts the object using its Group.Name property in ascending order.
public IEnumerable<GroupAuthority> GroupAuthoritysSorted
{
get
{
return GroupAuthoritys.OrderBy(x => x.Group.ID == 0)
.ThenBy(x => x.Group.Name);
}
}

How can I create a generic method to compare two list of any type. The type may be a List of class as well

Following is a class
public class Attribute
{
public string Name { get; set; }
public string Value { get; set; }
}
Following is the code in my main method
{
var test = new List<Attribute>();
test.Add(new Attribute { Name = "Don", Value = "21" });
test.Add(new Attribute { Value = "34", Name = "Karthik" });
var test1 = new List<Attribute>();
test1.Add(new Attribute { Name = "Don", Value = "21" });
test1.Add(new Attribute { Value = "34", Name = "Karthik" });
var obj = new Program();
var areEqual1 = obj.CompareList<List<Attribute>>(test, test1);
}
I have a ComapreList method
public bool CompareList<T>(T firstList, T secondList) where T : class
{
var list1 = firstList as IList<T>;
return true;
}
Now, list1 has null. I know that .net does not allow us to do this. But is there any other way where I can cast this generic list. My purpose is to compare each property value of these two list. I am using reflection to get the property but it works only if I can convert the firstlist/secondlist to something enumerable. if I directly use the name of the class in the IList<> (firstList as IList<Attribute>) it works, but not if I give <T>. Please help.
Just create method parameterized by type of lists items type. Even more, you can create method which compares any type of collections:
public bool CompareSequences<T> (IEnumerable<T> first, IEnumerable<T> second,
Comparer<T> comparer = null)
{
comparer = comparer ?? Comparer<T>.Default;
if (first == null)
throw new ArgumentNullException(nameof(first));
if (second == null)
throw new ArgumentNullException(nameof(second));
var firstIterator = first.GetEnumerator();
var secondIterator = second.GetEnumerator();
while(true)
{
bool firstHasItem = firstIterator.MoveNext();
bool secondHasItem = secondIterator.MoveNext();
if (firstHasItem != secondHasItem)
return false;
if (!firstHasItem && !secondHasItem)
return true;
if (comparer.Compare(firstIterator.Current, secondIterator.Current) != 0)
return false;
}
}
If collection items are primitive types, you can use default comparer. But if collections contain custom items, you need either IComparable to be implemented by collection items type:
public class Attribute : IComparable<Attribute>
{
public string Name { get; set; }
public string Value { get; set; }
public int CompareTo (Attribute other)
{
int result = Name.CompareTo(other.Name);
if (result == 0)
return Value.CompareTo(other.Value);
return result;
}
}
Or you can create and pass comparer instance. You can create comparer which is using reflection to compare fields/properties of some type. But it's not as simple as you might think - properties can be complex type or collections.
Usage:
var areEqual1 = obj.CompareSequences(test, test1);
If you don't need to compare objects with complex structure (which have inner collections and other custom objects) then you can use comparer like this one:
public class SimplePropertiesComparer<T> : Comparer<T>
{
public override int Compare (T x, T y)
{
Type type = typeof(T);
var flags = BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance;
foreach (var property in type.GetProperties(flags))
{
var propertyType = property.PropertyType;
if (!typeof(IComparable).IsAssignableFrom(propertyType))
throw new NotSupportedException($"{propertyType} props are not supported.");
var propertyValueX = (IComparable)property.GetValue(x);
var propertyValueY = (IComparable)property.GetValue(y);
if (propertyValueX == null && propertyValueY == null)
continue;
if (propertyValueX == null)
return -1;
int result = propertyValueX.CompareTo(property.GetValue(y));
if (result == 0)
continue;
return result;
}
return 0;
}
}
And pass it to sequence comparer
var equal = obj.CompareSequences(test, test1, new SimplePropertiesComparer<Attribute>());
Change the signature of your method and remove the then redundant cast:
public bool CompareList<T>(IList<T> firstList, IList<T> secondList) where T : class
{
var list1 = firstList as IList<T>; // Cast is not necessary any more
return true;
}
public bool CompareGenericLists<T, U>(List<T> list1, List<U> list2)
{
try
{
if (typeof(T).Equals(typeof(U)))
{
//For checking null lists
if (list1 == null && list2 == null)
return true;
if (list1 == null || list2 == null)
throw new Exception("One of the Lists is Null");
if (list1.Count.Equals(list2.Count))
{
Type type = typeof(T);
//For primitive lists
if (type.IsPrimitive)
{
int flag = 0;
for (int i = 0; i < list1.Count; i++)
{
if (list1.ElementAt(i).Equals(list2.ElementAt(i)))
flag++;
}
if (flag != list1.Count)
throw new Exception("Objects values are not same");
}
//For Reference List
else
{
for (int i = 0; i < list1.Count; i++)
{
foreach (System.Reflection.PropertyInfo property in type.GetProperties())
{
string Object1Value = string.Empty;
string Object2Value = string.Empty;
Object1Value = type.GetProperty(property.Name).GetValue(list1.ElementAt(i)).ToString();
Object2Value = type.GetProperty(property.Name).GetValue(list2.ElementAt(i)).ToString();
if (Object1Value != Object2Value)
{
throw new Exception("Objects values are not same");
}
}
}
}
}
else
throw new Exception("Length of lists is not Same");
}
else
throw new Exception("Different type of lists");
}
catch(Exception ex)
{
throw ex;
}
return true;
}
this method can be used for both primitive and reference lists.try this method.It will compare type,counts,and members of lists.

Compare 2 List<> without using Linq

Im working with 2 List, i want to see if the main contains the same types. The 2 lists do not need to contain the same count or order, just have all of the matching Types. I know this is very possible with Linq, however i cannot use that.
private static bool ContentsMatch(List<Type> list1, List<Type> list2)
{
if (list1.Count != list2.Count)
return false;
for (int i = 0; i < list1.Count; i++)
{
if (!list1[i].Equals(list2[i]))
return false;
}
return true;
}
The above method i tried will only return true if they are in the same order.
Code for algorithm provided in the comments.
Does not depend on order or count or duplicate items. Also generic and abstracted.
bool IsSameSet<T>(IEnumerable<T> l1, IEnumerable<T> l2)
{
return IsSubSet(l1, l2) && IsSubSet(l2, l1);
}
bool IsSubSet<T>(IEnumerable<T> l1, IEnumerable<T> l2)
{
var lookup = new Dictionary<T, bool>();
foreach (var e in l1)
lookup[e] = true;
foreach (var e in l2)
if (!lookup.ContainsKey(e))
return false;
return true;
}
Usage:
Type[] l1 = { typeof(object), typeof(int), typeof(long), typeof(object) };
Type[] l2 = { typeof(int), typeof(long), typeof(object) };
var result = IsSameSet(l1, l2);
Console.WriteLine(result); // prints true
Exercise for the user:
Add an additional parameter to provide an IEqualityComparer<T> to be passed to the dictionary.
To compare any user defined customized types, we need to override Equals & GetHashCode.
Below is the code snippet you could refer to :
public class CustomizedDataType
{
private int field1;
private string field2;
public CustomizedDataType(int field1,string field2)
{
this.field1 = field1;
this.field2 = field2;
}
public override bool Equals(object obj)
{
CustomizedDataType dataType = obj as CustomizedDataType;
if (this.field1 == dataType.field1 && this.field2 == dataType.field2)
{
return true;
}
return false;
}
public override int GetHashCode()
{
return (this.field1.GetHashCode() + this.field2.GetHashCode());
}
Sample code to execute :
static void Main(string[] args)
{
//Test Data
List<CustomizedDataType> dataTypeContaineer1 = new List<CustomizedDataType>();
dataTypeContaineer1.Add(new CustomizedDataType(10,"Test10"));
dataTypeContaineer1.Add(new CustomizedDataType(11, "Test11"));
dataTypeContaineer1.Add(new CustomizedDataType(12, "Test12"));
//Test Data
List<CustomizedDataType> dataTypeContaineer2 = new List<CustomizedDataType>();
dataTypeContaineer2.Add(new CustomizedDataType(100, "Test10"));
dataTypeContaineer2.Add(new CustomizedDataType(11, "Test11"));
dataTypeContaineer2.Add(new CustomizedDataType(12, "Test120"));
//Checking if both the list contains the same types.
if (dataTypeContaineer1.GetType() == dataTypeContaineer2.GetType())
{
//Checking if both the list contains the same count
if (dataTypeContaineer1.Count == dataTypeContaineer2.Count)
{
//Checking if both the list contains the same data.
for (int index = 0; index < dataTypeContaineer1.Count; index++)
{
if(!dataTypeContaineer1[index].Equals(dataTypeContaineer2[index]))
{
Console.WriteLine("Mismatch # Index {0}", index);
}
}
}
}
}
Output :
You can use the C# keyword 'is' to see if an object is compatible with a given type.
http://msdn.microsoft.com/en-us/library/vstudio/scekt9xw.aspx
Assuming you mean that that two List<T> both have matching T, you could use:
private static Boolean MatchingBaseType(IEnumerable a, IEnumerable b)
{
return GetIListBaseType(a) == GetIListBaseType(b);
}
private static Type GetIListBaseType(IEnumerable a)
{
foreach (Type interfaceType in a.GetType().GetInterfaces())
{
if (interfaceType.IsGenericType &&
(interfaceType.GetGenericTypeDefinition() == typeof(IList<>) ||
interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>) ||
interfaceType.GetGenericTypeDefinition() == typeof(ICollection<>))
)
{
return interfaceType.GetGenericArguments()[0];
}
}
return default(Type);
}
You say count doesn't matter (though you're checking .Count()--why?) But this should return if the two lists have the same types in them.

How to remove duplicate combinations from a List<string> using LINQ

I'm having a List of String like
List<string> MyList = new List<string>
{
"A-B",
"B-A",
"C-D",
"C-E",
"D-C",
"D-E",
"E-C",
"E-D",
"F-G",
"G-F"
};
I need to remove duplicate from the List i.e, if "A-B" and "B-A" exist then i need to keep only "A-B" (First entry)
So the result will be like
"A-B"
"C-D"
"C-E"
"D-E"
"F-G"
Is there any way to do this using LINQ?
Implement IEqualityComparer witch returns true on Equals("A-B", "B-A"). And use Enumerable.Distinct method
This returns the sequence you look for:
var result = MyList
.Select(s => s.Split('-').OrderBy(s1 => s1))
.Select(a => string.Join("-", a.ToArray()))
.Distinct();
foreach (var str in result)
{
Console.WriteLine(str);
}
In short: split each string on the - character into two-element arrays. Sort each array, and join them back together. Then you can simply use Distinct to get the unique values.
Update: when thinking a bit more, I realized that you can easily remove one of the Select calls:
var result = MyList
.Select(s => string.Join("-", s.Split('-').OrderBy(s1 => s1).ToArray()))
.Distinct();
Disclaimer: this solution will always keep the value "A-B" over "B-A", regardless of the order in which the appear in the original sequence.
You can use the Enumerable.Distinct(IEnumerable<TSource>, IEqualityComparer<TSource>) overload.
Now you just need to implement IEqualityComparer. Here's something for you to get started:
class Comparer : IEqualityComparer<String>
{
public bool Equals(String s1, String s2)
{
// will need to test for nullity
return Reverse(s1).Equals(s2);
}
public int GetHashCode(String s)
{
// will have to implement this
}
}
For a Reverse() implementation, see this question
You need to implement the IEqualityComparer like this:
public class CharComparer : IEqualityComparer<string>
{
#region IEqualityComparer<string> Members
public bool Equals(string x, string y)
{
if (x == y)
return true;
if (x.Length == 3 && y.Length == 3)
{
if (x[2] == y[0] && x[0] == y[2])
return true;
if (x[0] == y[2] && x[2] == y[0])
return true;
}
return false;
}
public int GetHashCode(string obj)
{
// return 0 to force the Equals to fire (otherwise it won't...!)
return 0;
}
#endregion
}
The sample program:
class Program
{
static void Main(string[] args)
{
List<string> MyList = new List<string>
{
"A-B",
"B-A",
"C-D",
"C-E",
"D-C",
"D-E",
"E-C",
"E-D",
"F-G",
"G-F"
};
var distinct = MyList.Distinct(new CharComparer());
foreach (string s in distinct)
Console.WriteLine(s);
Console.ReadLine();
}
}
The result:
"A-B"
"C-D"
"C-E"
"D-E"
"F-G"
Very basic, but could be written better (but it's just working):
class Comparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
return (x[0] == y[0] && x[2] == y[2]) || (x[0] == y[2] && x[2] == y[0]);
}
public int GetHashCode(string obj)
{
return 0;
}
}
var MyList = new List<String>
{
"A-B",
"B-A",
"C-D",
"C-E",
"D-C",
"D-E",
"E-C",
"E-D",
"F-G",
"G-F"
}
.Distinct(new Comparer());
foreach (var s in MyList)
{
Console.WriteLine(s);
}
int checkID = 0;
while (checkID < MyList.Count)
{
string szCheckItem = MyList[checkID];
string []Pairs = szCheckItem.Split("-".ToCharArray());
string szInvertItem = Pairs[1] + "-" + Pairs[0];
int i=checkID+1;
while (i < MyList.Count)
{
if((MyList[i] == szCheckItem) || (MyList[i] == szInvertItem))
{
MyList.RemoveAt(i);
continue;
}
i++;
}
checkID++;
}

What is the best way to check two List<T> lists for equality in C#

There are many ways to do this but I feel like I've missed a function or something.
Obviously List == List will use Object.Equals() and return false.
If every element of the list is equal and present in the same location in the opposite list then I would consider them to be equal. I'm using value types, but a correctly implemented Data object should work in the same fashion (i.e I'm not looking for a shallow copied list, only that the value of each object within is the same).
I've tried searching and there are similar questions, but my question is an equality of every element, in an exact order.
Enumerable.SequenceEqual<TSource>
MSDN
Evil implementation is
if (List1.Count == List2.Count)
{
for(int i = 0; i < List1.Count; i++)
{
if(List1[i] != List2[i])
{
return false;
}
}
return true;
}
return false;
I put together this variation:
private bool AreEqual<T>(List<T> x, List<T> y)
{
// same list or both are null
if (x == y)
{
return true;
}
// one is null (but not the other)
if (x== null || y == null)
{
return false;
}
// count differs; they are not equal
if (x.Count != y.Count)
{
return false;
}
for (int i = 0; i < x.Count; i++)
{
if (!x[i].Equals(y[i]))
{
return false;
}
}
return true;
}
The nerd in me also crawled out so I did a performance test against SequenceEquals, and this one has a slight edge.
Now, the question to ask; is this tiny, almost measurable performance gain worth adding the code to the code base and maintaining it? I very much doubt it ;o)
I knocked up a quick extension method:
namespace ExtensionMethods
{
public static class MyExtensions
{
public static bool Matches<T>(this List<T> list1, List<T> list2)
{
if (list1.Count != list2.Count) return false;
for (var i = 0; i < list1.Count; i++)
{
if (list1[i] != list2[i]) return false;
}
return true;
}
}
}
One can write a general purpose IEqualityComparer<T> for sequences. A simple one:
public class SequenceEqualityComparer<T> : IEqualityComparer<IEnumerable<T>>
{
public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(IEnumerable<T> obj)
{
return unchecked(obj.Aggregate(397, (x, y) => x * 31 + y.GetHashCode()));
}
}
A more fleshed out version: which should be better performing.
public class SequenceEqualityComparer<T> : EqualityComparer<IEnumerable<T>>,
IEquatable<SequenceEqualityComparer<T>>
{
readonly IEqualityComparer<T> comparer;
public SequenceEqualityComparer(IEqualityComparer<T> comparer = null)
{
this.comparer = comparer ?? EqualityComparer<T>.Default;
}
public override bool Equals(IEnumerable<T> x, IEnumerable<T> y)
{
// safer to use ReferenceEquals as == could be overridden
if (ReferenceEquals(x, y))
return true;
if (x == null || y == null)
return false;
var xICollection = x as ICollection<T>;
if (xICollection != null)
{
var yICollection = y as ICollection<T>;
if (yICollection != null)
{
if (xICollection.Count != yICollection.Count)
return false;
var xIList = x as IList<T>;
if (xIList != null)
{
var yIList = y as IList<T>;
if (yIList != null)
{
// optimization - loops from bottom
for (int i = xIList.Count - 1; i >= 0; i--)
if (!comparer.Equals(xIList[i], yIList[i]))
return false;
return true;
}
}
}
}
return x.SequenceEqual(y, comparer);
}
public override int GetHashCode(IEnumerable<T> sequence)
{
unchecked
{
int hash = 397;
foreach (var item in sequence)
hash = hash * 31 + comparer.GetHashCode(item);
return hash;
}
}
public bool Equals(SequenceEqualityComparer<T> other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return this.comparer.Equals(other.comparer);
}
public override bool Equals(object obj)
{
return Equals(obj as SequenceEqualityComparer<T>);
}
public override int GetHashCode()
{
return comparer.GetHashCode();
}
}
This has a few features:
The comparison is done from bottom to top. There is more probability for collections differing at the end in typical use-cases.
An IEqualityComparer<T> can be passed to base the comparison for items in the collection.
Use linq SequenceEqual to check for sequence equality because Equals method checks for reference equality.
bool isEqual = list1.SequenceEqual(list2);
The SequenceEqual() method takes a second IEnumerable<T> sequence as a parameter, and performs a comparison, element-by-element, with the target (first) sequence. If the two sequences contain the same number of elements, and each element in the first sequence is equal to the corresponding element in the second sequence (using the default equality comparer) then SequenceEqual() returns true. Otherwise, false is returned.
Or if you don't care about elements order use Enumerable.All method:
var isEqual = list1.All(list2.Contains);
The second version also requires another check for Count because it would return true even if list2 contains more elements than list1.

Categories