List<List<>>.Contains() not working - c#

I have the following code:
List<List<int>> list = new List<List<int>>();
list.Add(new List<int> { 0, 1 });
if (list.Contains(new List<int> { 0, 1 })) // false
...
I'm trying to check whether the list contains {0,1}, but the condition is false (I don't know why, maybe because the 'new' keyword).
If this is not the proper way, I'd like to know how to check that.
Thanks!

List<T>.Contains calls the Equals() method to compare objects.
Since the inner List<T> doesn't override Equals, you get reference equality.
You can fix this by creating a custom IEqualityComparer<List<T>> that compares by value and passing it to Contains().
You can also just use LINQ:
if (list.Any(o => o.SequenceEqual(new[] { 0, 1 }))

You're checking to see if list contains List #2 you've made, when you added List #1. Contains ordinarily checks to see if the object is contained by using the Equals method, but List does not override this method. This means that in this case, it does a reference comparison.
It is clear from your code that the two will not refer to the same List, even if their values are the same.

Manual:
This method determines equality by using the default equality comparer, as defined by the object's implementation of the IEquatable.Equals method for T (the type of values in the list).
The following code shows why your code doesn't work:
var list1 = new List<int> { 0, 1 };
var list2 = new List<int> { 0, 1 };
Console.WriteLine(list1.Equals(list2)); // prints false
List<T> itself does not implement Equals, so it compares using object.Equals, which checks on reference equality, which do not match in above and your case.
You could for example create an own implementation that extends List<List<int>> and override the Equals method.

The contains is checking references, not the contents on the contained list. The following would work
List<List<int>> list = new List<List<int>>();
var b = new List<int> { 0, 1 }
list.Add(b);
if (list.Contains(b))...

Related

Enumerable.Repeat perform badly with for loop on initializing List<> [duplicate]

I have a question about Enumerable.Repeat function.
If I will have a class:
class A
{
//code
}
And I will create an array, of that type objects:
A [] arr = new A[50];
And next, I will want to initialize those objects, calling Enumerable.Repeat:
arr = Enumerable.Repeat(new A(), 50);
Will those objects have the same address in memory?
If I will want to check their hash code, for example in that way:
bool theSameHashCode = questions[0].GetHashCode() == questions[1].GetHashCode();
This will return me true, and if I will change one object properties, all other objects will change it too.
So my question is: is that properly way, to initialize reference type objects? If not, then what is a better way?
Using Enumerable.Repeat this way will initialize only one object and return that object every time when you iterate over the result.
Will those objects have the same address in memory?
There is only one object.
To achieve what you want, you can do this:
Enumerable.Range(1, 50).Select(i => new A()).ToArray();
This will return an array of 50 distinct objects of type A.
By the way, the fact that GetHashCode() returns the same value does not imply that the objects are referentially equal (or simply equal, for that matter). Two non-equal objects can have the same hash code.
Just to help clarify for Camilo, here's some test code that shows the issue at hand:
void Main()
{
var foos = Enumerable.Repeat(new Foo(), 2).ToArray();
foos[0].Name = "Jack";
foos[1].Name = "Jill";
Console.WriteLine(foos[0].Name);
}
public class Foo
{
public string Name;
}
This prints "Jill". Thus it shows that Enumerable.Repeat is only creating one instance of the Foo class.
When using the following code to create an array:
var foos = Enumerable.Repeat(new Foo(), 2).ToArray();
The reason why each location in the array is the same is because you are passing an object, and not a function that creates an object, the code above is the same as:
var foo = new Foo();
var foos = Enumerable.Repeat(foo , 2).ToArray();
The reason above also explains why using a Select statement, like in the code below, creates a new object for each entry, because you are passing a function that dictates how each object is created, rather than the object itself.
Enumerable.Range(1, 2).Select(i => new Foo()).ToArray();
I would use a simple for loop to populate an array with new reference types.

Generating a new instance of a List in C#

I have a problem with using C#, if I initialize a certain list, lets say List<T> exampleList using another pre-existing list, lets say toModify like this: List<T> exampleList = new List<T>(toModify). When I later modify toModify list the newly created list also modifies itself. If it passes the value by reference shouldn't the value of exampleList stay the same since it was generated from the other one?
TLDR: Value of a list I initialize using another list(second list) changes when I change the second list. I come from a Java background and can't understand why this happens. Will I always have to use clone?
Let us use this example :
List<A> firstList = new List<A>()
{
new A() { Id = 3 },
new A() { Id = 5 }
};
List<A> secondList = new List<A>(firstList);
secondList[1].Id = 999;
Console.WriteLine(firstList[1].Id);
Output : 999
The main reason for this is that even though we created a new List<T> that points to a new memory allocated on heap it still works with references the point to same objects.
To create a list that points to new (!) objects with the same values we'd need to clone these elements somehow, one way to do it is to use LINQ .Select() method in order to create new objects and then a ToList() method to copy the list itself:
List<A> firstList = new List<A>()
{
new A() { Id = 3 },
new A() { Id = 5 }
};
List<A> secondList = firstList.Select(el => new A() { Id = el.Id }).ToList();
secondList[1].Id = 999;
Console.WriteLine(firstList[1].Id);
Output : 5
Yes.
You're creating a new list containing the same items as the old list. If you clear the first list, the items in the second stay.
But if you change a property for one of the items in the first list, then it is the same object in the second list.
So, both list are referencing the same items in memory. When you write list1[0].SomeProperty = 1 you're changing that using object reference that is the same in list2, so changes are reflected in the second list.
For how to clone a List and generate new references for items, check this SO Answer.
In the following line:
List<T> exampleList = new List<T>(toModify)
you create a list of T calling List<T>'s constructor that takes one argument of type IEnumerable<T>. For further info on the latter, please have a look here.
Method's arguments in C# are passed by default by value and not by reference. They can be passed by reference, but you have to explicitly state this in the signature of the corresponding method using the ref keyword and at the point you call this method, using again the same keyword. So the toModify is passed by value to the constructor of List<T>.
What's the importance of this?
In C# types can be divided into two categories (despite the fact that all types inherit from the System.Object):
Value types
Reference types
When we pass a value type as an argument, we pass a copy of it's value. Each modification we make in either the original value or in the copy of the original value is not reflected to one another. On the other hand, when we pass a reference type as an argument, we pass a copy of that reference. So now we have two references (pointers) that point to the same location in memory. That being said, it's clear that if we change any property of the object in which both references points to, this would be visible by both of them.
In your case, this is what is happening. toModify is a list of reference types (under the hood you have an array, whose items are references to other objects). So any change to the items of the initial list, toModify, is reflected to the list you construct based on this list.
A simple example that you could use to verify the above is the following:
public class Point
{
public int X { get; set; }
public int Y { get; set; }
public override string ToString() => $"X: {X}, Y: {Y}";
}
class Program
{
static void Main(string[] args)
{
var listA = new List<int> {1, 2, 3};
var listB = new List<int>(listA);
// Before the modification
Console.WriteLine(listA[0]); // prints 1
Console.WriteLine(listB[0]); // prints 1
listA[0] = 2;
// After the mofication
Console.WriteLine(listA[0]); // prints 2
Console.WriteLine(listB[0]); // prints 1
Console.ReadKey();
var pointsA = new List<Point>
{
new Point {X = 3, Y = 4},
new Point {X = 4, Y = 5},
new Point {X = 6, Y = 8},
};
var pointsB = new List<Point>(pointsA);
// Before the modification
Console.WriteLine(pointsA[0]); // prints X: 3, Y: 4
Console.WriteLine(pointsB[0]); // prints X: 3, Y: 4
pointsA[0].X = 4;
pointsA[0].Y = 3;
// After the modification
Console.WriteLine(pointsA[0]); // prints X: 4, Y: 3
Console.WriteLine(pointsB[0]); // prints X: 4, Y: 3
Console.ReadKey();
}
}

Except method returns all the members instead of returning only the different ones

In the following snippet of code, the Except method returns just all members of the list A instead of the ones which exists in A and not in B!
List<Process> ListA = new List<Process>();
List<Process> ListB = new List<Process>();
var ListC = ListA.Except(ListB).ToList();
ListC will get everything that exists inside ListA, without taking ListB into account at all!
What is wrong here?
Some information to be noted: ListB is a copy of ListA, with this rule that ListB remains intact but ListA gets Updated, and the idea is to get only those processes that are run since ListB is being initialized.
Except uses the default equality comparer, which by default uses the Object.Equals() method, which by default checks for reference equality.
Do you consider two Process objects to be equivalent even if they are two different objects (e.g. two different objects having the same Id?)
The easiest way to solve it is to override Object.Equals(object obj) in your Process class to use whatever equivalence you have. When implementing Object.Equals() you should also implement GetHashCode() so that it returns the same code for any two objects that are equivalent according to the Equals() implementation.
I'm willing to bet you're not implementing Equals correctly. Can you show your custom implementation?
As you can see, it should work just fine:
void Main()
{
List<int> ListA = new List<int> { 5, 9 };
List<int> ListB = new List<int> {3, 5 };
var ListC = ListA.Except(ListB).ToList();
Console.WriteLine (ListC);
}
Output
9

How do you check a list in C# for contents before an insert/add? What is the predicate class for?

when working with the List class, i have noticed that the boolean i was looking for was:
if(lstInts.Exists(x)){...}
X is a Predicate of T the same as lstInts. I was confused as to why you just cant pass int the int in this case, and why X's type isnt of type T.
Example i was testing:
List<int> listInt = new List<int>();
int akey = Convert.toInt32(myMatch.Value);
Predicate<int> pre = new Predicate<int>(akey); //akey is not the correct constructor param.
if(listInt.Exists(pre)){
listInt.add(akey);
}
Is there a reason for having the additional Step of Predicate, or.... if i am going about the logic incorrectly?
I also noticed that the predicate constructure does not take an item of type T. Sort of confused as to how this is suppose to work.
You could also use Contains() method
List<int> listInt = new List<int>();
int akey = Convert.toInt32(myMatch.Value);
if(listInt.Contains(akey)){
listInt.add(akey);
}
Or alternately use Any()
if(listInt.Any(I => I == akey)) {
// Do your logic
}
Predicate<T> is a delegate (returning bool) that allows you to find an item matching some condition (that's why item being checked is passed into it ad an argument).
This would be a good use for the HashSet<T> collection type, which does not allow duplicates (just silently ignores them).
Well, for your scenario, you should use the Contains method on the List class.
So what's the purpose of exists you might ask? Well, the Contains method uses the Equals method on the object to determine if the item you are checking is contained in the list or not. This only works if the class has overridden the Equals method for equality checking. If it hasn't, well then two separate instances of something that you consider to be equal will not be considered equal.
In addition to that, perhaps you want to use different logic that the Equals method provides. Now, the only way to determine if something is in the list is to either iterate it on your own, or write your own EqualityComparer to checks the equality of an instance.
So, what the list class does is expose some methods like Exists so that you can provide your own logic in an easy way, while doing the boilerplate iteration for you.
Example
Consider you have a list of Dog types. Now, the dog class has overridden the Equals method, so there is no way to check if a dog is equal to another, but they have some information about the dog like it's name and it's owner. So consider the following
List<Dog> dogs = new List<Dog> {
new Dog { Name = "Fido", Owner = "Julie" },
new Dog { Name = "Bruno", Owner = "Julie" },
new Dog { Name = "Fido", Owner = "George" }
};
Dog fido = new Dog { Name = "Fido", Owner = "Julie" };
List.Contains(fido)
Returns false (since Equals method has not been overridden)
List.Exists(x => fido.Name == x.Name && fido.Owner == x.Owner)
Returns true since you are checking equality on the properties which, being strings, have equality overridden.
If you were to go look at the source code for the list class, you would likely see something like this.
public bool Exists(Predicate<Dog> predicate) {
foreach (Dog item in Items) {
if (predicate(item))
return true;
}
return false;
}
Now, if you fill in the predicate I had above, the method would look like this
public bool Exists(Dog other) {
foreach (Dog item in Items) {
if (item.Name == other.Name && item.Owner == other.Owner)
return true;
}
return false;
}

List<List<int>> Remove() method

I'd like to use Remove() method on list of lists, but it's not working for me.
Simple example should say everything:
List<List<int>> list = new List<List<int>>();
list.Add(new List<int> { 0, 1, 2 });
list.Add(new List<int> { 1, 2 });
list.Add(new List<int> { 4 });
list.Add(new List<int> { 0, 1, });
list.Remove(new List<int> { 1, 2 });
If I use RemoveAt(1) it works fine but Remove() not.
It is obviously the same reason that this code returns false:
List<int> l1 = new List<int>();
List<int> l2 = new List<int>();
l1.Add(1);
l2.Add(1);
bool b1 = l1 == l2; // returns False
bool b2 = l1.Equals(l2); // returns False too
So it seems to me that I cannot simply compare two lists or even arrays. I can use loops instead of Remove(), but there must be easier way.
Thanks in advance.
The problem is that List<T> doesn't override Equals and GetHashCode, which is what List<T> will use when trying to find an item. (In fact, it will use the default equality comparer, which means it'll use the IEquatable<T> implementation if the object implements it, and fall back to object.Equals/GetHashCode if necessary). Equals will return false as you're trying to remove a different object, and the default implementation is to just compare references.
Basically you'd have write a method to compare two lists for equality, and use that to find the index of the entry you want to remove. Then you'd remove by index (using RemoveAt). EDIT: As noted, Enumerable.SequenceEqual can be used to compare lists. This isn't as efficient as it might be, due to not initially checking whether the counts are equal when they can be easily computed. Also, if you only need to compare List<int> values, you can avoid the virtual method call to an equality comparer.
Another alternative is to avoid using a List<List<int>> in the first place - use a List<SomeCustomType> where SomeCustomType includes a List<int>. You can then implement IEquatable<T> in that type. Note that this may well also allow you to encapsulate appropriate logic in the custom type too. I often find that by the type you've got "nested" collection types, a custom type encapsulates the meaning of the inner collection more effectively.
First approach:
List<int> listToRemove = new List<int> { 1, 2 };
list.RemoveAll(innerList => innerList.Except(listToRemove).Count() == 0);
This also removes the List { 2, 1 }
Second approach (preferred):
List<int> listToRemove = new List<int> { 1, 2 };
list.RemoveAll(innerList => innerList.SequenceEqual(listToRemove));
This removes all lists that contain the same sequence as the provided list.
List equality is reference equality. It won't remove the list unless it has the same reference as a list in the outer list. You could create a new type that implements equality as set equality rather than reference equality (or you do care about order as well?). Then you could make lists of this type instead.
This simply won't work because you're tying to remove a brand new list (the new keyword kind of dictates such), not one of the ones you just put in there. For example, the following code create two different lists, inasmuch as they are not the same list, however much they look the same:
var list0 = new List<int> { 1, 2 };
var list1 = new List<int> { 1, 2 };
However, the following creates one single list, but two references to the same list:
var list0 = new List<int> { 1, 2 };
var list1 = list0;
Therefore, you ought to keep a reference to the lists you put in there should you want to act upon them with Remove in the future, such that:
var list0 = new List<int> { 1, 2 };
listOfLists.Remove(list0);
They are different objects. Try this:
List<int> MyList = new List<int> { 1, 2 };
List<List<int>> list = new List<List<int>>();
list.Add(new List<int> { 0, 1, 2 });
list.Add(MyList);
list.Add(new List<int> { 4 });
list.Add(new List<int> { 0, 1, });
list.Remove(MyList);
You need to specify the reference to the list you want to remove:
list.Remove(list[1]);
which, really, is the same as
list.RemoveAt(1);

Categories

Resources