.net core list removeall() method is strange - c#

It seems very simple, but this error happens.
IList<string> list = new List<string>();
list.Add("001");
list.Add("002");
list.Add("003");
list.Add("004");
list.ToList().RemoveAll(s => s == "002");
return list.Count.ToString();
The list Count should be 3, but it will still be 4. Is a bug in RemoveAll() method? If using List rathan than IList declaration, it works well.
Edit
1. If not using ToList() method, there is no RemoveAll() method to call.
How can I avoid this situation to use IList as a argument, list firstly is a reference type. Should I not use IList as a declaration totally? We have used IList everythere in our project.
public string List()
{
IList<string> list = new List<string>();
list.Add("001");
list.Add("002");
list.Add("003");
list.Add("004");
Remove(list);
return list.Count.ToString();
}
private void Remove(IList<string> list)
{
list.ToList().RemoveAll(a => a == "002");
}
If there is a new method to support, it will be better. Thanks everyone here.

When you're using ToList you actually create a shallow copy of the list, which then you apply the RemoveAll on the new list, thus, the original list doesn't been affected.
If you only looking for the count of none-"002" items, then simple count will suffice
list.Count - list.Count(i => i == "002");
Otherwise if you actually want to remove those items from the original list, then you will need to solve it the old fashion way, using for loop.
Anyway, if IList is not that important, you can save the list as List and not IList, and use RemoveAll method.

ToList() returns a shallow copy of the original list (ie, it's a new instance and modifications made to it will not be reflected back in list).
Since you need to use the RemoveAll method, I recommend storing the variable as List instead of IList:
List<string> list = new List<string>();
list.Add("001");
list.Add("002");
list.Add("003");
list.Add("004");
list.RemoveAll(s => s == "002");
Console.WriteLine(list.Count()); // 3
You can also achieve the same thing using LINQ operators:
List<string> list = new List<string>();
list.Add("001");
list.Add("002");
list.Add("003");
list.Add("004");
list = list.Where(s => s != "002").ToList();
Console.WriteLine(list.Count());
More succinctly:
List<string> list = new List<string>(new[] {
"001",
"002",
"003",
"004"
});
// or
//List<string> list = new[] {
// "001",
// "002",
// "003",
// "004"
//}.ToList();
Console.WriteLine(list.Count(s => s != "002")); // 3

Related

Collect indexes from list

Is there a linq function in c# which enables you to collect IEnumerables from a specific range of indexes?
An example would be
var objectArray = new string[] { "Bill", "Bob", "Joe", "Phil", "Tom", "Paul" };
var indexArray = new int[] { 1, 3, 5 };
var list = objectArray.Where(SOME_FUNCTION_TO_GET_INDEXES ??).ToList();
//output would be list:
//Bob
//Phil
//Paul
Just use Select with your indexArray and return the item from objectArray via indexing.
var list = indexArray.Select(i => objectArray[i]);
Note that this works very efficiently for any collection that allows indexing (for example, Array and List<T>). In the more general case of having an IEnumerable or ICollection, you wouldn't be able to index directly. In which case you'd need to see Jon's answer. Depending on the sizes of the lists involved, and how many items you need to look up, it might be worth converting your IEnumerable to an Array or List (using ToArray for example) first.
If the original datasource is already accessible by index, such as for a list or an array, you can just use indexArray.Select as Matt showed.
If you've got an IEnumerable<T> instead, you can use the Where overload which provides the index as well as the value. So:
var list = objectArray.Where((value, index) => indexArray.Contains(index))
.ToList();

To ToList() or not to ToList()?

Given an in memory (not LINQ to SQL) list of classes:
List<MyClass> myItems = /*lots and lots of items*/;
which I am grouping using a GroupBy() statement:
myItems.GroupBy(g => g.Ref)
and then immediately consuming in a foreach loop is there any difference in calling .ToList() on the "group" or should I just use an IEnumerable.
So full code examples:
With ToList()
List<List<MyClass>> groupedItemsA = new List<List<MyClass>>();
List<List<MyClass>> groupedItemsB = new List<List<MyClass>>();
List<MyClass> myItems = /*lots and lots of items*/;
List<IGrouping<string, MyClass>> groupedItems = myItems.GroupBy(g => g.Ref).ToList();
foreach(IGrouping<string, MyClass> item in groupedItems)
{
if (/*check something*/)
{
groupedItemsA.Add(item.ToList());
}
else
{
groupedItemsB.Add(item.ToList());
}
}
or
Using IEnumerable
List<List<MyClass>> groupedItemsA = new List<List<MyClass>>();
List<List<MyClass>> groupedItemsB = new List<List<MyClass>>();
List<MyClass> myItems = /*lots and lots of items*/;
IEnumerable<IGrouping<string, MyClass>> groupedItems = myItems.GroupBy(g => g.Ref);
foreach(IGrouping<string, MyClass> item in groupedItems)
{
if (/*check something*/)
{
groupedItemsA.Add(item.ToList());
}
else
{
groupedItemsB.Add(item.ToList());
}
}
Is there any difference in the execution plan of these "under the hood"? Would either of these be more efficient or does it not really matter?
I am not using the groupedItems list after this.
Yes there is a difference and it can be significant.
ToList() will iterate and append each iterated item into a new list. This has the effect of creating a temporary list which consumes memory.
Sometimes you might want to take the memory penalty especially if you intend on iterating the list multiple times and the original list is not in memory.
In your particular example using the ToList() you actually end up iterating twice - once to build the list and a second time in your foreach. Depending on the size of the list and your application this may or may not be a concern.
If you are sure you'll use groupedItems only once, then using .ToList() has a single advantage: if there is an exception (for example because your code is doing funny things for calculating .Ref) during the grouping, the exception will be in the .ToList() row instead of being inside the foreach... I don't think it is a big advantage (and perhaps it is a disadvantage).
To clarify:
public class MyClass
{
public string Ref
{
get
{
// sometimes I like to throw an exception!
if (DateTime.Now.Ticks % 10 == 0) throw new Exception();
return "Foo";
}
}
}
Note that you have explicitly tagged this question as IEnumerable, and in your example myItems is a List<>, so I won't discuss of the difference of doing ToList() or not when you are reading data from a database through Entity Framework/Linq to SQL.
Is there any difference?
Yes, .ToList() creates a new list from iterating the grouped collection:
The ToList<TSource>(IEnumerable<TSource>) method forces immediate query evaluation and returns a List that contains the query results.
Whether this is noticeable should be benchmarked by you.
If you only iterate the grouped collection once, the .ToList() step is unnecessary and will be relatively slower than directly enumerating the GroupBy() result.
If you were to use the IEnumerable rather than the List() each time you enumerate it, the GroupBy expression would be re-evaluated against your myItems List.
This means if you add another item to myItems then enumerate it, that item will be included in the GroupBy expression.
When you call ToList however you create a new List and any changes to myItems will not be included in groupedItems.

Remove items from list 1 not in list 2

I am learning to write lambda expressions, and I need help on how to remove all elements from a list which are not in another list.
var list = new List<int> {1, 2, 2, 4, 5};
var list2 = new List<int> { 4, 5 };
// Remove all list items not in List2
// new List Should contain {4,5}
// The lambda expression is the Predicate.
list.RemoveAll(item => item. /*solution expression here*/ );
// Display results.
foreach (int i in list)
{
Console.WriteLine(i);
}
You can do this via RemoveAll using Contains:
list.RemoveAll( item => !list2.Contains(item));
Alternatively, if you just want the intersection, using Enumerable.Intersect would be more efficient:
list = list.Intersect(list2).ToList();
The difference is, in the latter case, you will not get duplicate entries. For example, if list2 contained 2, in the first case, you'd get {2,2,4,5}, in the second, you'd get {2,4,5}.
Solution for objects (maybe easier than horaces solution):
If your list contains objects, rather than scalars, it is that simple, by removing by one selected property of the objects:
var a = allActivePatientContracts.RemoveAll(x => !allPatients.Select(y => y.Id).Contains(x.PatientId));
list = list.Except(list2).ToList();
This question has been marked as answered, but there is a catch. If your list contains an object, rather than a scalar, you need to do a bit more work.
I tried this over and over with Remove() and RemoveAt() and all sorts of things and none of them worked correctly. I couldn't even get a Contains() to work correctly. Never matched anything. I was stumped until I got the suspicion that maybe it could not match up the item correctly.
When I realized this, I refactored the item class to implement IEquatable, and then it started working.
Here is my solution:
class GenericLookupE : IEquatable<GenericLookupE>
{
public string ID { get; set; }
public bool Equals( GenericLookupE other )
{
if ( this.ID == other.ID ) return true;
return false;
}
}
After I did this, the above RemoveAll() answer by Reed Copsey worked perfectly for me.
See: http://msdn.microsoft.com/en-us/library/bhkz42b3.aspx

Remove item from List<T>

List<object> A = new List<object>;
List<object> B = new List<object>;
List<object> C = new List<object>;
C.Add(item);
B.Add(C);
A.Add(B);
Finally I have List A than contains List B and List B contains List C.
I want to remove a item from list C.
How can I do this with LINQ or lambda?
LINQ is not intended to be used for in-place changes to collections. Use old-school Remove / RemoveAll:
((List<object>)((List<object>)A[0])[0]).Remove(item);
((List<object>)((List<object>)A[0])[0]).RemoveAll(o => ((MyClass)o).Id == 5);
Note: the number of casts required in this code snippet indicates that your way of using List<T> may not be optimal for your use case. I strongly recommend you think about specifying a more specific generic argument than object.
Have a look at Remove, RemoveAt and RemoveRange.

Union two List in C#

I want to union, merge in a List that contains both references, so this is my code, how can I define a list ready for this porpouses?
if (e.CommandName == "AddtoSelected")
{
List<DetalleCita> lstAux = new List<DetalleCita>();
foreach (GridViewRow row in this.dgvEstudios.Rows)
{
var GridData = GetValues(row);
var GridData2 = GetValues(row);
IList AftList2 = GridData2.Values.Where(r => r != null).ToList();
AftList2.Cast<DetalleCita>();
chkEstudio = dgvEstudios.Rows[index].FindControl("ChkAsignar") as CheckBox;
if (chkEstudio.Checked)
{
IList AftList = GridData.Values.Where(r => r != null).ToList();
lstAux.Add(
new DetalleCita
{
codigoclase = Convert.ToInt32(AftList[0]),
nombreestudio = AftList[1].ToString(),
precioestudio = Convert.ToDouble(AftList[2]),
horacita = dt,
codigoestudio = AftList[4].ToString()
});
}
index++;
//this line to merge
lstAux.ToList().AddRange(AftList2);
}
dgvEstudios.DataSource = lstAux;
dgvEstudios.DataBind();
}
this is inside a rowcommand event.
If you want to add all entries from AftList2 to lstAux you should define AftList2 as IEnumerable<> with elements of type DetalleCita (being IEnumerable<DetalleCita> is enough to be used as parameter of AddRange() on List<DetalleCita>). For example like this:
var AftList2 = GridData2.Values.Where(r => r != null).Cast<DetalleCita>();
And then you can add all its elements to lstAux:
lstAux.AddRange(AftList2);
Clarification:
I think you are misunderstanding what extension method ToList() does. It creates new list from IEnumerable<T> and its result is not connected with original IEnumerable<T> that it is applied to.
That is why you are just do nothing useful trying to do list.ToList().AddRange(...) - you are copying list to (another newly created by ToList()) list, update it and then basically throwing away it (because you are not even doing something like list2 = var1.ToList(), original var1 stays unchanged after that!!! you most likely want to save result of ToList() if you are calling it).
Also you don't usually need to convert one list to another list, ToList() is useful when you need list (List<T>) but have IEnumerable<T> (that is not indexable and you may need fast access by index, or lazy evaluates but you need all results calculated at this time -- both situations may arise while trying to use result of LINQ to objects query for example: IEnumerable<int> ints = from i in anotherInts where i > 20 select i; -- even if anotherInts was List<int> result of query ints cannot be cast to List<int> because it is not list but implementation of IEnumerable<int>. In this case you could use ToList() to get list anyway: List<int> ints = (from i in anotherInts where i > 20 select i).ToList();).
UPDATE:
If you really mean union semantics (e.g. for { 1, 2 } and { 1, 3 } union would be something like { 1, 2, 3 }, with no duplication of equal elements from two collections) consider switching to HashSet<T> (it most likely available in your situation 'cause you are using C# 3.0 and I suppose yoou have recent .NET framework) or use Union() extension method instead of AddRange (I don't think this is better than first solution and be careful because it works more like ToList() -- a.Union(b) return new collection and does NOT updates either a or b).

Categories