List(T) RemoveAll() not working as intended...? - c#

Let's say I have a list of User objects with two properties...ID and Name
List<User> lst = List<User>();
I fill it up with a bunch of Users. Ok, now I want to trim my list using RemoveAll() and this function.
private Boolean IsExisting(int id) {
//blah blah
return true;
//blah blah
return false;
}
So I use this statement:
gdvFoo.DataSource = lst.RemoveAll(t => IsExisting(t.ID));
It is my understanding that whenever IsExisting returns true that element should be removed from lst, but what happens, strangely enough, is it returns an integer?, not a truncated list and I received the following error message:
Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource.>

List.RemoveAll method
The method removes all matching instances from the list on which you called it. This modifies the existing list, rather than returning a new one.
The return value is the number of rows removed.

RemoveAll() returns the number of elements removed.
You need to do this:
lst.RemoveAll(t => IsExisting(t.ID));
gdvFoo.DataSource = lst;

The docs are very clear about what's going on:
Return Value
Type: System.Int32
The number of elements removed from the List .
Perhaps the following Linq would be more in line with your expectations?
lst.Except(t => IsExisting(t.ID)).ToList();

Instead of RemoveAll(), you could try using IEnumerable's filter where you would say something like :
var filteredList = lst.Where(item => IsExisting(item.Id))
This makes the code a little more easier to read and focusses on the objective of the task at hand, rather than how to look at implementing it.

List<T>.RemoveAll(...) has a return type of int which is not an IListSource, IEnumerable nor IDataSource

The RemoveAll is modifying the list and returning the number of items removed. You just set your datasource to the list in a second step.
lst.RemoveAll(t => IsExisting(t.ID));
gdvFoo.DataSource = lst;

Related

Strangeness with IEnumerable and OrderBy

I have a piece of code which seems simple but seems it is not.
// Enum values
IEnumerable values = Enum.GetValues(typeof(TVal)).Cast<TVal>();
// Sort them by alpha
if (sortByAlpha)
{
values = (values).OrderBy(i => i.ToString());
}
If I write:
// Get enum values
IEnumerable values = Enum.GetValues(typeof(TVal)).Cast<TVal>();
// Sort them by alpha
if (sortByAlpha)
{
values = Enum.GetValues(typeof(TVal)).Cast<TVal>().OrderBy(i => i.ToString());
}
it works.
Why? values in the first piece of code should be the same?
What I am not seeing?
Running this on .Net 4.5.1
The first code snipped wont compile because OrderBy is an Extensionmethod of IEnumerable<T>, yet you are using IEnumerable.
So in order to make it compile, change your first snippet and do this:
IEnumerable<TVal> values = Enum.GetValues(typeof(TVal)).Cast<TVal>();
// Sort them by alpha
if (sortByAlpha)
{
values = values.OrderBy(i => i.ToString());
}
The difference between IEnumerable and IEnumerable<T> is that the latter is basically a more specialized version that knows what elements its dealing with.
For example thats what the Cast method does, it turns an unspecific IEnumerable into a IEnumerable<T> enabling you to use that OrderBy in the second example.
I recommend you googling abit about this topic.
The other reply already explains very well on what goes wrong, but I wanted to give some clarification on why it goes wrong in your code. To start here's your code:
// Enum values
IEnumerable values = Enum.GetValues(typeof(TVal)).Cast<TVal>();
// ^---- This is where it's going wrong
// Sort them by alpha
if (sortByAlpha)
{
values = (values).OrderBy(i => i.ToString());
}
The Cast<TVal>() returns an IEnumerable<TVal>, but because you save it as an IEnumerable you implicitely cast it. It's similar to doing:
IEnumerable values = (IEnumerable) Enum.GetValues(typeof(TVal)).Cast<TVal>();
This is entirely valid as IEnumerable<T> inherits from IEnumerable so the cast works, but you lose out on all functionality that IEnumerable<T> offers over IEnumerable. One of which is the OrderBy method.

Ordering items that contain double quotes

How would I order a list of items where some of the items contain double quotes?
Advance
Access
“Chain free” deal
Binding
Broker
Doing this FaqData = repo.FaqData.OrderBy(q => q.Description) results in the following
“Chain free” deal
Advance
Access
Binding
Broker
Tried this as well
FaqData = repo.FaqData.OrderBy(q => q.QuestionDescription.Replace("”", ""))
FaqData = repo.FaqData.OrderBy(q => q.Description.Replace(#"""",""))
OrderBy calls the delegate once per item contained in the list being sorted. The delegate should return a value which, when compared to values obtained in the same way for other items in the list, will provide a value that can be sorted.
Typically the value returned in the delegate is a property of the listed item - but because its code, it could return anything you like, including values that arn't anything to do with the items in the list.
In this example instead of returning the list property ".Description" the code is returning a new string value derived from the ".Description" property. The derivation is simply to use the .net String.Replace to replace all double-quote values with an empty string.
This means the sorting algorithm sorts on the ".Description" with double-quotes removed.
This is not very efficient if you call this sorting code many times, and could easily be done differently; either by adding a new property to the class being sorted as such;
public string PlainTextDescription
{
get {
return this.Description.Replace(#"""","");
}
}
and sorting like this;
FaqData = repo.FaqData.OrderBy(q => q.PlainTextDescription)
or by pre-populating the PlainTextDescription field using the logic, but only when the .Description value changes; this would be much more efficient because the String.Replace would only be called once each time the .Description changes - with the example above, the String.Replace code must be called every time the sorter needs to evaluate the PlainTextDescription field, which means we're doing the String.Replace many times instead of once.
I'm thinking you not only want to ignore quotes but anything that isn't A-Z.
All you need to do is include a function to Where that strips out anything you don't want. To do that you can use a regular expression, like this:
var filtered = Regex.Replace(s, #"[^A-Za-z0-9]","")
Now to put it in a Where statement:
var tests = new[] { "Advance","Access","Binding","Broker",#"""Chain free"" deal","`Twas the night before Christams","#NotAllMen","Zenit","Quickly"};
var sorted = tests.OrderBy(s => Regex.Replace(s, #"[^A-Za-z0-9]",""));
foreach (var s in sorted)
{
Console.WriteLine(s);
}
Output:
Access
Advance
Binding
Broker
"Chain free" deal
#NotAllMen
Quickly
`Twas the night before Christams
Zenit
Code on DotNetFiddle

Is this loop possible to do using simple LINQ?

I got a class called BG which has a property called Name Code.
I instantiate an object called bgList.
Now I am trying to get all the Code of the objects which have their 'Crop' property set to cropName.
I would like to convert the following working code to linq but for the life of me am unable to do that - am quite sure that I am missing something:
List<string> breedingGroupsAndRoles = new List<string>();
for (int i = 0; i < bgList.Count; i++)
{
if (bgList[i].Crop == cropName)
breedingGroupsAndRoles.Add(bgList.[i].Code);
}
The closest I came was this but it only nets me the first item:
breedingGroupsAndRoles.Add(bgrList.Find(c => c.Crop == cropName).Role);
List<string> breedingGroupsAndRoles = bgList
.Where(bg => bg.Crop == cropName)
.Select(bg => bg.Code)
.ToList();
Just for the sake of completeness, the Find method you tried calling on bgList is not part of LINQ, it's a member of the generic List class itself. It only returns the first element matched by the predicate you provide, which is why you were only getting one result. You probably wanted the FindAll method, which returns a list of all matching elements:
List<BG> breedingGroups = bgList.FindAll(c => c.Crop == cropName);
Note that this produces a list of matching BG instances rather than just their Role properties. Depending on how you're processing the results this may be sufficient, otherwise you'll still need LINQ or a loop and a second list to extract the Role values. In any case, an all-LINQ solution such #Tim Schmelter's is likely the better way to go.

How to remove items in IEnumerable<MyClass>?

How do I remove items from a IEnumerable that match specific criteria?
RemoveAll() does not apply.
You can't; IEnumerable as an interface does not support removal.
If your IEnumerable instance is actually of a type that supports removal (such as List<T>) then you can cast to that type and use the Remove method.
Alternatively you can copy items to a different IEnumerable based on your criteria, or you can use a lazy-evaluated query (such as with Linq's .Where) to filter your IEnumerable on the fly. Neither of these will affect your original container, though.
This will produce a new collection rather than modifying the existing one however I think it is the idiomatic way to do it with LINQ.
var filtered = myCollection.Where(x => x.SomeProp != SomValue);
Another option would be to use Where to produce a new IEnumerable<T> with references to the objects you want removed then pass that to a Remove call on the original collection. Of course that would actually consume more resources.
You can't remove items from an IEnumerable<T>. You can remove items from an ICollection<T> or filter items from an IEnumerable<T>.
// filtering example; does not modify oldEnumerable itself
var filteredEnumerable = oldEnumerable.Where(...);
// removing example
var coll = (ICollection<MyClass>)oldEnumerable;
coll.Remove(item);
You don't remove items from an IEnumerable. It's not possible. It's just a sequence of items. You can remove items from some underlying source that generates the sequences, for example if the IEnumerable is based on a list you can remove items from that list.
The other option you have is to create a new sequence, based on this one, that never shows the given items. You can do that using Where, but it's important to realize this isn't removing items, but rather choosing to show items based on a certain condition.
As everyone has already stated, you can't remove from IEnumerable because that is not what the interface is describing. Consider the following example:
public IEnumerable<string> GetSomeStrings()
{
yield return "FirstString";
yield return "Another string";
}
Clearly, removing an element from this IEnumerable is not something you can reasonably do, instead you'd have to make a new enumeration without the ones you don't want.
The yield keywork provides other examples, for example, you can have infinite lists:
public IEnumberable<int> GetPowersOf2()
{
int value = 1;
while(true)
{
yield return value;
value = value * 2;
}
}
Items cannot be removed from an IEnumerable<T>. From the documentation:
Exposes the enumerator, which supports a simple iteration over a collection of a specified type.
You can cast it and use the List<T>.RemoveAll(Predicate<T> match) this is exactly what you need.
This is how i do,
IEnumerable<T> myVar=getSomeData(); // Assume mayVar holds some data
myVar=myVar.Where(d=>d.Id>10); // thats all, i want data with Id>10 only
How about trying Enumerable.Empty i.e.
T obj = new T();
IEnumerable<T> myVar = new T[]{obj} //Now myVar has an element
myVar = Enumerable.Empty<T>(); //Now myVar is empty

How to get an empty list of a collection?

I have a collection of anonymous class and I want to return an empty list of it.
What is the best readable expression to use?
I though of the following but I don't think they are readably enough:
var result = MyCollection.Take(0).ToList();
var result = MyCollection.Where(p => false).ToList();
Note: I don't want to empty the collection itself.
Any suggestion!
Whats about:
Enumerable.Empty<T>();
This returns an empty enumerable which is of type T. If you really want a List so you are free to do this:
Enumerable.Empty<T>().ToList<T>();
Actually, if you use a generic extension you don't even have to use any Linq to achieve this, you already have the anonymous type exposed through T
public static IList<T> GetEmptyList<T>(this IEnumerable<T> source)
{
return new List<T>();
}
var emp = MyCollection.GetEmptyList();
Given that your first suggestion works and should perform well - if readability is the only issue, why not create an extension method:
public static IList<T> CreateEmptyCopy(this IEnumerable<T> source)
{
return source.Take(0).ToList();
}
Now you can refactor your example to
var result = MyCollection.CreateEmptyCopy();
For performance reasons, you should stick with the first option you came up with.
The other one would iterate over the entire collection before returning an empty list.
Because the anonymous type there is no way, in source code, to create a list. There is, however, a way to create such list through reflection.

Categories

Resources