Search for a particular object in a list - c#

I have a list of objects and I want to see if a particular object is in this list. When I use the Contains() or IndexOf() methods on the list i get incorrect results however, since this uses the Equals() method of the object which is not what i need. I want to find a particular instance and not an object that seems to have equal property values.

If you wish to match the references, you can use:
if (object.ReferenceEquals(item1, item2))
...
to force it to compare references instead of using Equals()
Or:
int index = list.FindIndex(item=>ReferenceEquals(item, target));
(See the MSDN Documentation for List.FindIndex() for more details.)

Can you use the hashcode?
list.where(w => w.GetHashCode() == object.GetHashCode())

Related

Default comparer when using OrderBy extension

Out of curiosity: What comparer is used when sorting a bunch of objects using the following extension method?
OrderBy(x=> x)
Background: I have to check wether two ISet< T > instances contain the same elements and considered to use the
bool setsEqual = MySet.SequenceEqual(OtherSet);
method. As the order of those elements contained in the sets are not defined and may differ, a SequenceEqual would fail in those cases where the internal order is not the same. So i would have to explictly define an order. As the order algo for itself is completely irrelevant as long as it´s stable, i just used an "Identity" lambda expression:
bool setsEqual = MySet.OrderBy(x => x).SequenceEqual(OtherSet.OrderBy(x => x);
But what does "Compare the objects themselves" mean to the code? As this OrderBy extension method is a generic one, there must be a default compare algo in place that is able to sort objects without knowing anything more about it, and that would mean a comparison for sorting had to be delegated to the type of the set elements itself. Is there an interface that the elements´ type would have to support, or is there a default comparer (may be comparing internal memory addresses of objects) in place?
To answer the question of sorting: sorting uses IComparable<T> or IComperable if that isn't implemented. The IComperable interfaces force you to implement a int CompareTo(object) method (or int CompareTo(T) method if you used the typed version).
The order of your elements is determined by the sign of the int. The value returned is interpreted as follows:
0: the two objects are equivalent (i.e. the same)
-1: the compared object precedes this object (i.e. comes before this object)
1: the compared object follows this object (i.e. comes after this object)
The actual value is ignored, the sign is all that matters. If you implement your own IComparable interface, you have to choose the semantics for sort order.
Many objects already implement IComparable already, like all your numbers, strings, etc. You'll need to implement it explicitly if you need to sort objects you've created yourself. It's not a bad practice if you intend those objects to be displayed in a list on screen at all.
As to your specific case, where you just need to determine if a set and another IEnumerable are equivalent, then you would use the ISet<T>.SetEquals(IEnumerable<T>) method which is implemented in the standard library set implementations. Sets, by definition, only guarantee the values are unique, so as long as the number of elements are the same, you only need to detect that all the elements in one IEnumerable can be found in the set.
The method used the IComparable<T>-or the IComparable-interface depending on which of both are implemented. If none is implemented the order is arbitrary.
However you won´t need to order you instances before comparing the sets. Simply loop one set and check if all of its elements are contained in the other set. Or use this:
var areEqual = firstSet.All(x => secondSet.Contains(x)) && secondSet.All(x => firstSet.Contains(x));
Or even simpler:
var areEqual = !firstSet.Except(secondSet).Any() && !secondSet.Except(firstSet).Any();
Both ways perform much faster than your appraoch as the iteration of elements stops when the first element is found that does not fit. Using OrderBy you´d loop all elements, regardless if there was already a mismatch.
Unlike for equality, there's no 'default' comparer for objects in general.
It seems that Comparer<TKey>.Default always returns a comparer, for any type TKey. If no sensible comparison method can be determined, you get an exception, but only once the comparer is used.
At least one object must implement IComparable.

How can I be sure that List.Contains works for a list of DataTables?

If I have this:
List<DataTable> listDataTables = functionToAddSomeDataTables();
and I want to do a comparison like this:
if(listDataTables.Contains(aDataTable))
{
//do something.
}
How can I know if it is comparing the reference or the schema or the content or all of the above?
Do I need to write my own IEquatable.Equals to make sure it works properly or can I trust that the built in .Equals for DataTable works as I would hope?
Is there a general rule or observation for knowing when .Contains, or similar comparisons are by reference or by value?
Thanks in advance :)
List<T>.Contains uses the object's object.Equals(object) method. Since DataTable's documentation says that its Equals was inherited from Object.Equals, the default Object.Equals implementation of reference comparison is what will be used. If you want the comparison by something else, include that equality comparer by using LINQ's Contains method.
(as an example, compare DataTable Methods and Decimal Methods: only Decimal lists Equals on the list on the left, and says "(Overrides ValueType.Equals(Object).)" instead of "(Inherited from Object.)")
You have to write your own Equals method and compare the needed properties where. The built in (default) Contains() method will check values for value types (string, int...) and references for reference types (your class is a reference type)

Can two arrays which contain the same elements not be equal?

I encountered a stunning problem today where I'm trying to find if an object is contained in an List collection. The problem is that the list doesn't find the object and returns index as -1 when I can see it right there already. I then created a custom Index Finder to look for the object by comparing the properties rather than a direct equality where I discovered that one of the object's properties, a ushort array which was identical was returning false when compared, but they contain exactly the same element.
The array is as follows:
{ushort[1]} [0]13
and they're exactly the same in both except that one of the object is contained in a List while the other one is on it's own. What could be the cause of this problem? I've tried all types of different ways to get around the problem but I can't just figure out what the problem is. In this particular case what is causing the comparison between the two arrays to return false, I've tied using Object.equals as well as the normal == comparer. Thanks
For arrays, Equals will return true only if you compare two references that point to the same array. To compare different arrays by content, you can use:
Enumerable.SequenceEqual(a1, a2)
Also, if collection contains objects of your custom type, make sure that these types override Equals, equality operator(==) and GetHashCode.

list sorting in c#

i have list of objects i need to sort the list based on object's property1 and i need to sort again the resultant list with object's property2 without loosing the grouping done in first sorting ....
for ex:
obj has 2 property name and location
i need to have a final list of objects which has been sorted with region and objects of same region should be sorted by name...
(Assuming you don't have LINQ available to you, which makes this trivial.)
If you look in MiscUtil, you'll find two useful classes: ProjectionComparer and LinkedComparer (IIRC).
ProjectionComparer basically implements the LINQ "order by" concept - you specify how to convert a source element to a key value, and the comparer will order by those key values.
LinkedComparer takes two comparers and returns a new comparer which uses the "primary" comparer first, and the "secondary" comparer if values are equal with respect to the primary one.
Create two projection comparers (one for each property) and then a linked comparer with the two of them, then pass that to List<T>.Sort. Let me know if you need a full code sample, but it would be something like (using C# 3):
var comparer = new LinkedComparer<Foo>
(ProjectionComparer<Foo>.Create(x => x.FirstProperty),
ProjectionComparer<Foo>.Create(x => x.SecondProperty));
(In C# 2 you could use anonymous methods, they'd just be a bit more long-winded.)
Sounds like you want to use LINQ's orderby and thenby syntax.
A List has a Sort method which takes a Comparision delegate as an argument.
There are also overloads where you can pass in your own comparer.
So, you can write a class which implements IComparer. Then, in the implementation of this class, you write the code where you compare the 2 objects on the properties you want.

DataRow comparison not working as expected

I'm trying to compare two DataRows in a loop. However, the following if statement doesn't return true:
if (dt1.Rows[0]["Name"] == dt2.Rows[b]["Name"]) {
// This never executes
}
However, if I add .ToString() to the end of each DataRow, the if statement returns true:
if (dt1.Rows[0]["Name"].ToString() == dt2.Rows[b]["Name"].ToString()) {
// This now executes
}
The column "Name" is from the same table/column. So the question is quite simple... What am I doing wrong?
Thanks
Stephen
Those cells hold objects so you are doing an object comparison, which just does a reference comparison, which is different from a value comparison. It asks the question "Are these two objects really the same object?", essentially are they referring to the same object, hence "reference comparison". When you do the ToString() call, you are then doing string comparison. That is why it works.
Here's a link to MS's discussion of Operator== and comparison.
As itsmatt has said, your first snippet is doing a reference comparison. An alternative to calling ToString is to use Object.Equals:
if (Object.Equals(dt1.Rows[0]["Name"], dt2.Rows[b]["Name"])) {
// stuff
}
The reason for using the static method instead of the instance method is to avoid problems with null references.
The == operator, if not overloaded, is identical to ReferenceEquals() -- that is, it determines whether two given objects are the same instances.
The call to ToString() returns an object of string class, which has overloaded == operator, which does string comparison.

Categories

Resources