List.Contains doesn't work properly - c#

I have a list which contains objects but these objests aren't unique in the list. I wrte this code to make unique them in another list:
foreach (CategoryProductsResult categoryProductsResult in categoryProductsResults.Where(categoryProductsResult => !resultSet.Contains(categoryProductsResult)))
{
resultSet.Add(categoryProductsResult);
}
But at the end resultSet is the same with categoryProductsResults.
categoryProductsResult's second row :
resultSet first row:
As you can see resultSet's first row and categoryProductsResult's second row is the same but it adds the second row to resultSet.
Do you have any suggestion?

Contains uses the default comparer which is comparing references since your class does not override Equals and GetHashCode.
class CategoryProductsResult
{
public string Name { get; set; }
// ...
public override bool Equals(object obj)
{
if(obj == null)return false;
CategoryProductsResult other = obj as CategoryProductsResult;
if(other == null)return false;
return other.Name == this.Name;
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
Now you can simply use:
resultSet = categoryProductsResults.Distinct().ToList();

List uses the comparer returned by EqualityComparer.Default and according to the documentation for that:
The Default property checks whether type T implements the
System.IEquatable(Of T) interface and, if so, returns an
EqualityComparer(Of T) that uses that implementation. Otherwise, it
returns an EqualityComparer(Of T) that uses the overrides of
Object.Equals and Object.GetHashCode provided by T.
So you can either implement IEquatable on your custom class, or override the Equals (and GetHashCode) methods to do the comparison by the properties you require. Alternatively you could use linq:
bool contains = list.Any(i => i.Id == obj.Id);

Each categoryProductsResult is different to each other. It's like something your can see here. If you want a simpler one and the ProductId is your unique identifier. Just do the code below:
foreach (CategoryProductsResult categoryProductsResult in categoryProductsResults.Where(categoryProductsResult => resultSet.ProductId !=categoryProductsResult.ProductId)
{
resultSet.Add(categoryProductsResult);
}

Reference objects in a list are indexed by their hash code. So, Contains will never find a reference object with the same hash code (unless you override the GetHashCode and Equals implementation in the class.
This SO answer explains.

You need to check if your current item is contained in your target list for each iteration. Currently you check once at the start of the loop, which means none of your items is in the target list.
I think Distinct is already doing what you want, you might want to use this extension instead of your own loop.

Related

How to Make Collection Stop Calling Equals when Performing Collection.Remove

I have a Class Test which has a overriden method for "Equals" method and then I have a TestCollection class which is implemented using ICollection<Test> & IEnumerable<Test> in the Collection I have implemented Remove method which just removes the item from the current TestCollection object.
Whenever I class remove method for the TestCollection object, this internally calls "Equals" method which is overridden at Test class.
For one of my scenario, I do not want this Equals to be called, what are the other ways where I can remove the item from my collection without calling Equals
Below is the sample code for better understanding.
Test Class
public class Test
{
public int Id { get; set; }
private Collection<Test> _entities = new Collection<Test>();
public bool Remove(Test item)
{
return this._entities.Remove(item);
}
public override bool Equals(object obj)
{
Console.WriteLine("Equals inside Test Object");
return true;
}
}
TestCollection class
public class TestCollection : ICollection<Test>, IEnumerable<Test>
{
public TestCollection() : base() { }
private Collection<Test> _entities = new Collection<Test>();
public TestCollection(IList<Test> entityList)
{
this._entities = new Collection<Test>(entityList);
}
public bool Remove(Test item)
{
return this._entities.Remove(item);
}
public override bool Equals(object obj)
{
Console.WriteLine("Equals inside Test Collection Object");
return true;
}
}
I think you are missing the point here. Equals method is implementing the arithmetic relation of equivalence, like having attributes of being reflexive, symmetric and transitive. There are no two distinct ways to say that two objects are equal, you see?
Solution for you is to remove implementation of the Equals method. This method is intended to be overridden if and only if there is exactly one definition of equivalence for a class - like integer equality - there is exactly one way to test whether two integers are equal.
Also, that is the reason why Remove method does not accept an additional parameter such as an IComparer or IEqualityComparer - that wouldn't make sense.
On a related note: Entities should never override Equals. There is no equality relation (in mathematical terms) defined for objects that can change their state over time, and entity is defined as an object with lifetime. The trouble there is that you can pick two versions of the same entity and ask whether they are equal. Well, they are both equal (that is the same entity) and not equal (those are two versions of it). Therefore, Equals method is not the way to check equality of entities.
The short answer is that you cannot.
The way that an item is removed from a list is done by doing an equality check for the item in question on each of the entries in the list.
There may be some way to do it, however, but I doubt it's a good practice, or even desirable code.
You could wrap the list into another list that uses a custom IEqualityComparer implementation. Allow that comparer to have two different modes (pass through to object.Equals, or don't) and switch them before remove (and switch back afterwards).
You could find the index of the item you want to remove (not use its Equal) and call RemoveAt

C# Using .Remove In List<Category>

Trying to use .remove to remove stuff from my List.. but it's not working, giving me the error "Argument type 'string' is not assignable to parameter type 'System.Predicate'
public void ManiPulateCategory(string categoryToManipulate)
{
createCategories = repository.Load(path);
foreach (Category item in createCategories)
{
createCategories.Remove(item.CategoryName)
}
}
I think that you are trying to remove from your list of categories ALL items that have a CategoryName which matches categoryToManipulate.
If that's what you want, you can do it using List.RemoveAll() like so:
createCategories.RemoveAll
(
catagory => catagory.CategoryName == categoryToManipulate
);
This is using a method on List, NOT something from IEnumerable. If you only have an IEnumerable available, you would have to use a different solution - but you said you have a List, so this should work.
This works by you passing what's known as a predicate to RemoveAll(). A predicate is simply a method takes a single parameter of a certain type and that returns true or false. It will be called once for each element of the list, being passed that element as a parameter. If it returns true, that element will be removed.
In this case, I didn't create a separate method for the predicate; instead I wrote an inline lambda expression to implement it.
createCategories.RemoveAll(x=>x.CategoryName == categoryToManipulate)
Something like that? (Fixed to reflect comments but initially proposed by Matthew Watson above)
Also have a look at this Answer
In order to find the right item in a list you have to override Equals and GetHashCode from Object.
class Category {
public override bool Equals(object obj)
{
if (!(obj is Category)) return false;
return this.CategoryName == ((Category)obj).CategoryName;
}
public override int GetHashCode()
{
this.CategoryName.GetHashCodeU();
}
}
Now you may call createCategories.remove(item) within your loop.

Why do we need GetHashCode() function in the Object Model Project? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why is it important to override GetHashCode when Equals method is overriden in C#?
I was looking into the following class in my Object Model and could not understand the significance of adding GetHashCode() in the Class.
Sample Class
public class SampleClass
{
public int ID { get; set; }
public String Name { get; set; }
public String SSN_Number { get; set; }
public override bool Equals(Object obj)
{
if (obj == null || GetType() != obj.GetType())
return false;
SampleClass cls = (SampleClass)obj;
return (ID == cls.ID) &&
(Name == cls.Name) &&
(SSN_Number == cls.SSN_Number);
}
public override int GetHashCode()
{
return ID.GetHashCode() ^ Name.GetHashCode() ^ SSN_Number.GetHashCode();
}
}
Suppose I have a list of Sample Class Object and I want to get a specific index. Then Equals() can help me to get that record. Why should I use GetHashCode() ?
You need to handle both, because GetHashCode() is used by many collection implementations (like Dictionary) in concert with the Equals method. The important thing is that if you override the implementation of Equals, then you must override GetHashCode in such a way that any two objects that are Equal according to your new implementation also must return an identical Hash Code.
If they don't, then they will not work in Dictionary's properly. It's generally not that hard. One way that I often times do this is by taking the Properties of an object that I use for equality, and joining them together in a String object, and then return String.GetHashCode.
String has a pretty good implementation of GetHashCode that returns a wide range of integers for various values that make for good spreads in a sparse collection.
It is necessary to provide an override to GetHashCode, when your custom class overrides Equals. If you omit GetHashCode, you will get a compiler warning saying "A public type overrides System.Object.Equals but does not override System.Object.GetHashCode".
GetHashCode returns a value based on the current instance that is suited for hashing algorithms and data structures such as a hash table. Two objects that are the same type and are equal must return the same hash code to ensure that instances of System.Collections.HashTable and System.Collections.Generic.Dictionary<TKey, TValue> work correctly.
Suppose it was not necessary to override the GetHashCode in your custom class, the hash based collections would have to then use the base class' Object.GetHashCode which might not give correct results for all instances of your custom class.
If you observe the code you have posted, your Equals method compares
ID, Name and SSN for the 2 instances to return equality result
and the same attributes are being used for the hashing algorithm
(ID^Name^SSN) inside your GetHashCode method.

How to eliminate duplicates from a list?

class infoContact
{
private string contacts_first_nameField;
private string contacts_middle_nameField;
private string contacts_last_nameField;
private Phonenumber[] phone_numbersField;
private Emailaddress[] emailField;
}
I have a List<infoContact> The list contains almost 7000 which I get from some other program. In the list out of 7000, 6500 are duplicates. I am looking for a way how to eliminate duplicates.
A infoContact is duplicate if first_name, last_name, emailaddresses, phone numbers are same.
I thought of using a HashSet<infoContact> and override getHashCode() of infoContact.
I am just curious to know if that is the best way to do. If this is not a good way what is the better way?
You can use the Distinct extension method that takes an IEqualityComparer<T>. Just write a class that implements that interface, and does the comparison, and then you can just do something like this:
var filteredList = oldList.Distinct(new InfoContactComparer());
override an equals method with the parametres you want so you can compare objects through equals
i created a remove deducted items from list class before here is the key for it ,
List<string> list = new List<string>();
foreach (string line in File.ReadAllLines(somefile.txt))
{
if (!list.Contains(line))
{
list.Add(line);
}
}
Firstly think of extracting the unique values. You could use the Distinct() Linq method with a comparer like:
public class infoContactComparer : IEqualityComparer<infoContact>
{
public bool Equals(infoContact x, infoContact y)
{
return x.contacts_first_nameField == y.contacts_first_nameField
&& x.contacts_last_nameField == y.contacts_last_nameField
&& ...
}
public int GetHashCode(infoContact obj)
{
return obj.contacts_first_nameField.GetHashCode();
}
}
Two options: override GetHashCode and Equals if you control the source of infoContact and your overrides will be true for any particular use of the class.
Otherwise, define a class implementing IEqualityComparer<infoContact>, which also allows you to define proper Equals and GetHashCode methods, and then pass an instance of this into a HashSet<infoContact> constructor or into a listOfContacts.Distinct method call (using Linq).
Note: your question seems to be based on the idea that GetHashCode should determine equality or uniqueness. It shouldn't! It's part of the tool that allows a HashSet to do its job, but it is not required to return unique values for unequal instances. The values should be well distributed, but they can ultimately overlap.
In short, two equal instances should have the same hash code, but two instances sharing the same hash code are not necessarily equal. For more on guidelines for GetHashCode, please visit this blog.
The right way is to ovveride the equals method!
In this way, when you add new element in the list, the element don't will be added!
Implement your class infoContact as a derivate of IEquatable<infoContact>:
class InfoContact : IEquatable<InfoContact> {
string contacts_first_nameField;
string contacts_last_nameField;
object[] phone_numbersField;
object[] emailField;
// other fields
public bool Equals(InfoContact other) {
return contacts_first_nameField.Equals(other.contacts_first_nameField)
&& contacts_last_nameField.Equals(other.contacts_last_nameField)
&& phone_numbersField.Equals(other.phone_numbersField)
&& emailField.Equals(other.emailField);
}
}
and use Linqs Enumerable.Distinct method in order to filter the duplicates:
var infoContacts = GetInfoContacts().Distinct();

Checking if same object is already present in a list

Here is the story:
Im trying to make a list of different clusters... I only want to have the necessary clusters... And Clusters can be the same.
How can I add this to a list by checking if the list contains the object (I know objects cant be passed here)
This is my sample quote:
foreach (Cluster cluster in clustersByProgramme)
{
if (!clusterList.Contains(cluster))
{
clusterList.Add(cluster);
}
}
Your code should work; if it hasn't, you might be using different object instances that represent the same actual cluster, and you perhaps haven't provided a suitable Equals implementation (you should also update GetHashCode at the same time).
Also - in .NET 3.5, this could be simply:
var clusterList = clustersByProgramme.Distinct().ToList();
As an example of class that supports equality tests:
class Cluster // possibly also IEquatable<Cluster>
{
public string Name { get { return name; } }
private readonly string name;
public Cluster(string name) { this.name = name ?? ""; }
public override string ToString() { return Name; }
public override int GetHashCode() { return Name.GetHashCode(); }
public override bool Equals(object obj)
{
Cluster other = obj as Cluster;
return obj == null ? false : this.Name == other.Name;
}
}
Your example is about as simple as it is going to get. The only thing I could possibly recommend is that you use the Exists method:
The Predicate is a delegate to a
method that returns true if the object
passed to it matches the conditions
defined in the delegate. The elements
of the current List are individually
passed to the Predicate delegate, and
processing is stopped when a match is
found.
This method performs a linear search;
therefore, this method is an O(n)
operation, where n is Count.
If you're using .NET 3.5, use a HashSet to do this.
HashSet<Cluster> clusterList = new HashSet<Cluster>();
foreach (Cluster cluster in clustersByProgramme)
{
clusterList.Add(cluster);
}
In this case, also make sure that if cluster1 == cluster2, then
cluster1.Equals(cluster2);
cluster2.Equals(cluster1); //yeah, could be different depending on your impl
cluster1.GetHashCode() == cluster2.GetHashCode();
Your code is correct, but it is not very efficient. You could instead use a HashSet<T> like this:
HashSet<Cluster> clusterSet = new HashSet<T>();
foreach (Cluster cluster in clustersByProgramme)
clusterSet.Add(cluster);
In this case, also make sure that if cluster1 == cluster2, then
cluster1.Equals(cluster2);
cluster2.Equals(cluster1); //yeah, could be different depending on your impl
cluster1.GetHashCode() == cluster2.GetHashCode();
why not just use dictionary?
It is n(1) as long as your items have a good hash.
Seems a simple solution
Ie dictionary.Contains(key) is n(1)
you can then update existing if at all or add new

Categories