List<List<int>> Remove() method - c#

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);

Related

IList<IList<int>> doesn't accept List<List<int>>?

List implements IList so I expect IList will accept a List object
but why IList> doesn't accept List>?
static IList<int> List_1()
{
List<int> list = new List<int> { 1,2,3,3,4,5};
return list;
}
static IList<IList<int>> List_2()
{
List<List<int>> parent = new List<List<int>>();
List<int> list = new List<int> { 1, 2, 3, 3, 4, 5 };
parent.Add(list);
return parent; //compiler error CS0266
}
That's because of
List<T> implements IList<T> but
List<List<T>> does not implement IList<IList<int>>
That's why your first method works as intended and second not.
Just change your declaration of the list in the second method to
List<IList<int>> parent = new List<IList<int>>();
And this is the case of covariance and contravariance.
Generic type parameters support covariance and contravariance but you need to define in that way
By learn.microsoft.com
Covariance and contravariance are terms that refer to the ability to use a more derived type (more specific) or a less derived type (less specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types
Suppose this works. Your client code is:
var result = List_2();
Since the contract allows adding to the result anything that's IList<int>, you could possibly have
public class MyCustomIList : IList<int>
{
...
}
and then
var result = List_2();
result.Add( new MyCustomIList() );
But that's wrong!
Your result is a list of List<int>, you should not be allowed to add anything other than List<int> or its derivatives there. However, you were able to add MyCustomIList which is not related to the List<int>.
If you need a broad picture of the issue, read more on covariance and contravariance.
The fundamental issue in this particular example comes from the Add operation. If you don't need it, the IEnumerable will do
static IEnumerable<IEnumerable<int>> List_2()
{
List<List<int>> parent = new List<List<int>>();
List<int> list = new List<int> { 1, 2, 3, 3, 4, 5 };
parent.Add(list);
return parent; // no error, this works
}
This has been covered already.
Why then does List implement IList?
It is a bit odd, since List for any type other than object does not
fulfill the full contract of IList. It's probably to make it easier on
people who are updating old C# 1.0 code to use generics; those people
were probably already ensuring that only the right types got into
their lists. And most of the time when you're passing an IList around,
it is so the callee can get by-index access to the list, not so that
it can add new items of arbitrary type.
I would suggeste return IEnumerable instead of IList, will simplify your life, since List Fully implements it.
The problem is with your method return type. Modify your method signature to return to IList<List<int>> rather than returning IList<IList<int>>
static IList<List<int>> List_2()
{
List<List<int>> parent = new List<List<int>>();
List<int> list = new List<int> { 1, 2, 3, 3, 4, 5 };
parent.Add(list);
return parent; //no compiler error
}
Now it will work fine as your method now returns an IList of List<int>
I don't know why you want to return exactly IList<IList<int>>, but one way of doing that is to use Cast<T>() method:
static IList<IList<int>> List_2()
{
List<List<int>> parent = new List<List<int>>();
List<int> list = new List<int> { 1, 2, 3, 3, 4, 5 };
parent.Add(list);
return parent.Cast<IList<int>>().ToList();
}
Or ConvertAll() method:
return parent.ConvertAll(x => (IList<int>)x);
Both methods will run over all elemets, and cast/convert them to a given type, so I think it would be better to return IList<List<int>> instead (if that's possible).

How to write generic function processing two Lists holding any kind of objects?

I want to write a function that processs two Lists of the same objects. The function does always the same thing:
Find the objects that are only in List2 but not in List1 -> Do something with them
Find the object that are in both Lists -> Do something different with them.
Now the point is, that I have List pairs holding different kind of objects to which I want to apply this exact process.
Example:
List<Foo1> L11, L12;
List<Foo2> L21, L22;
List<Foo3> L31, L32;
So how do I have to write the code, so that I do not have to repeat the code for each List type ?
Greetings and Thank you
I would prepare a method, like below:
static void Process<T>(IEnumerable<T> list1, IEnumerable<T> list2, Action<T> onlyIn2, Action<T> inBoth)
{
var hash = new HashSet<T>(list1);
foreach (var item2 in list2)
if (hash.Contains(item2))
inBoth(item2);
else
onlyIn2(item2);
}
You can then use it as follows:
var list1 = new List<int> {1, 2, 3, 4, 5};
var list2 = new List<int> {3, 4, 5, 6};
Process(list1, list2, a =>
{
Console.WriteLine("{0} only in 2", a);
}, a =>
{
Console.WriteLine("{0} in both", a);
});
Note that it uses standard comparison rules (for objects reference equality unless Equals is overrided or IEqualityComparer<TKey> is implemented).
LINQ already provides two methods which do this:
// get all members of L11 not present in L12
var except = L11.Except(L12).ToList();
// get members present in both lists
var intersect = L11.Intersect(L12).ToList();
These overloads will use the default comparer for the list element type, so since you want to compare custom classes, you will need to use the overload which accepts a custom IEqualityComparer<T>:
var comparer = new CustomComparer();
var except = L11.Except(L12, comparer).ToList();
var intersect = L11.Intersect(L12, comparer).ToList();
which you need to write yourself:
class CustomComparer : IEqualityComparer<SomeClass>
{
public bool Equals(SomeClass x, SomeClass y)
{
// return true if equal
}
public int GetHashCode(SomeClass obj)
{
// return a hash code for boj
}
}
Your can use the Except/Intersect Linq methods as follows:
void Process<T>(IList<T> list1, IList<T> list2, IEqualityComparer<T> comparer = null) {
//Find the objects that are only in List2 but not in List1
foreach(var item in list2.Except(list1, comparer)) {
// -> Do something with them
}
//Find the object that are in both Lists -> Do something different with them.
foreach(var item in list1.Intersect(list2, comparer)) {
// -> Do something different with them.
}
}

ienumerable except method returning no differences

var list1 = new[] { 1, 2, 3, 4, 5 };
var list2 = new int[] { };
var x = list2.Except(list1).ToList();
This doesnt return all elements from list1, var x is actually an empty list. according to msdn it should return all the elements in list1. Why is it not, and what am I missing.
A sequence that contains the set difference of the elements of two sequences.
It works just fine: returns all items from list2 which do not exist in list1. And because list2 is already empty result list is empty as well.
Look at first parameter description on the msdn:
An IEnumerable<T> whose elements that are not also in second will be returned.
The wording might be confusing on MSDN, but refer to set theory to understand it.
A sequence that contains the set difference of the elements of two sequences.
A set difference is also called a complement and is "is the set of elements in B but not in A".
With that in mind, an empty array .Except anything is still an empty array.
You probably want
var list1 = new[] { 1, 2, 3, 4, 5 };
var list2 = new int[] { };
var x = list2.Union(list1).ToList();
Since you mentioned that you are expecting
should return all the elements in list1

List<List<>>.Contains() not working

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))...

C# Function Chaining

Why do i receive error in the following declaration ?
List<int> intrs = new List<int>().AddRange(new int[]{1,2,3,45});
Error :Can not convert type void to List ?
Because AddRange function does not return a value. You might need to perform this in two steps:
List<int> intrs = new List<int>();
intrs.AddRange(new int[]{1,2,3,45});
You could also use a collection initializer (assuming C# 3.0+).
List<int> intrs = new List<int> { 1, 2, 3, 45 };
Edit by 280Z28: This works for anything with an Add method. The constructor parenthesis are optional - if you want to pass thing to a constructor such as the capacity, you can do so with List<int>(capacity) instead of just List<int> written above.
Here's an MSDN reference for details on the Object and Collection Initializers.
Dictionary<string, string> map = new Dictionary<string, string>()
{
{ "a", "first" },
{ "b", "second" }
};
Because AddRange modifies the specified list instead of returning a new list with the added items. To indicate this, it returns void.
Try this:
List<int> intrs = new List<int>();
intrs.AddRange(new int[]{1,2,3,45});
If you want to create a new list without modifying the original list, you can use LINQ:
List<int> intrs = new List<int>();
List<int> newIntrs = intrs.Union(new int[]{1,2,3,45}).ToList();
// intrs is unchanged
AddRange does not return the list it has added items to (unlike StringBuilder). You need to do something like this:
List<int> intrs = new List<int>();
intrs.AddRange(new int[]{1,2,3,45});
AddRange() is declared as:
public void AddRange(object[]);
It does not return the list.
By the way in C# 3.x (not sure about 2.0) you can do either of
List<int> intrs = new List<int>{1,2,3,45};
List<int> intrs = new []{1,2,3,45}.ToList(); // with Linq extensions
Besides other answers, you can add your own extension method that will add range and return list (not that it's a good practice).
BTW, if you had already declared intrs, you could have done it with parentheses:
(intrs = new List<int>()).AddRange(new int[] { 1, 2, 3, 45 });
However, I like the initialization syntax better.
Although others have already mentioned that AddRange does not return a value, based on the samples given for alternatives it should also be remembered that the constructor of List will take an IEnumerable of T as well in addition to the code previously mentioned that is .NET 3.5+
For example:
List<int> intrs = new List<int>(new int[]{2,3,5,7});
There is yet another way.
List<int> intrs = new List<int>
{
1,
2,
3,
45
};

Categories

Resources