Modify current source using lambda - c#

Lets assume that I have an IEnumerable<MyType> called myList. I would like to remove some items from the list using a lambda expression.
Currently, I do it in the following way:
myList = myList.Where(x=>x.SomeProp == 1);
I would like to do something like:
myList.Remove(x=>x.SomeProp != 1);
I know that Remove() doesn't exist by default, but does a similar method exist? Or do I have to write an extension method?

IEnumerable<T> itself doesn't expose any mutating operations - so you can't expose a Remove operation. If you're trying to remove items from a List<T> you could use List<T>.RemoveAll though.
If you want a sort of "inverse where" which excluded rather than included items, then you could write that very easily. It's not clear that it would really be advantageous to do so though - you can usually invert the logic of a predicate very easily, and while Where is familiar to other programmers, your new extension method wouldn't be.
I definitely wouldn't call such an operation Remove though - that sounds like it's a mutating operation. I'd probably call it something like ExceptWhere - so you'd have:
var adults = people.ExceptWhere(person => person.Age < 18);
as an example.

Related

In what scenario we have to use extension method as AsEnumerable() and ToList() in linq Query

here we are using AsEnumerable() extension method.
IEnumerable<string> strings = grdLookupPracticeMultiple.GridView.GetSelectedFieldValues("ID").Select(s => (string)s).AsEnumerable();
and what mean (string)s
The (string)s in this context is simply saying "cast the variable s as type string" - I'm guessing that GetSelectedFieldValues("ID") returns object, and you want a sequence of strings. There's actually a .Cast<string>() that would have done that for you.
As for when you need AsEnumerable() - pretty rarely, actually - and probably not in this case. The key scenario is when it wouldn't be otherwise. Perhaps you explicitly want to treat IQueryable<T> as IEnumerable<T> (to force LINQ-to-Objects instead of query composition). Or maybe you're dealing with something like DataSet where the .Rows etc aren't actually IEnumerable<T> for any T, and need help.
As for when to use .ToList(): when you want to create a snapshot of the data. At the moment, strings is a deferred execution sequence over the data. It will execute when you foreach over it. So: if the list changes between now and then : you'll get the updated contents. Often, you want the data as it exists now.
edit: actually, in this case GetSelectedFieldValues returns a snapshot, so: it won't actually update in this case, but in many others : it might; so: it needs to be considered.
So: summing up, you probably just want:
var strings = grdLookupPracticeMultiple.GridView.GetSelectedFieldValues("ID")
.Cast<string>().ToList();
However, if this is the dev express grid, you already get a List<T> from GetSelectedFieldValues, so maybe this would be more efficient:
var strings = grdLookupPracticeMultiple.GridView.GetSelectedFieldValues("ID")
.ConvertAll(s => (string)s);

Pre-processing every item in an IQueryable<T> in LINQ to objects

Please have a look at the following code:
var list = new List<MyClass>();
...
var selection = list
.Preprocess()
.Where(x => x.MyProperty = 42)
.ToList();
I want to implement Preprocess in such a way that it can process every item selected by the predicate that follows. So, if list contains thousands of object, I don't want Preprocess to process all of them, but only those that are selected by the Where clause.
I know this sounds like it would be better to move Preprocess to the end of the query, but I have my reasons to do it that way.
Is that possible with IQueryable<T>? Or does it behave like IEnumerable, where the whole LINQ-to-objects pipeline is strictly sequential?
For your purposes, IQueryable here is no different from IEnumerable, which is, as you said, purely sequential. Immediately after you invoke Where(), there's effectively no trace of the original "collection".
Now, you could theoretically do what you want, but that would require rolling "your own LINQ". Preprocess() could return some kind of PreprocessedEnumerable, all the custom operators would attach to it and the final ToList() will do the reordering of the calls.

Count of System.Collections.Generic.List

ViewBag.EquipmentList = myInventoryEntities.p_Configurable_Equipment_Request_Select(Address_ID, false).Select(c => new { Value = c.Quantity + " " + c.Device_Name + " (s)", ID = c.Device_ID.ToString() }).ToList();
In Razor i want to do the following
#ViewBag.EquipmentList.Count
But Count is always == 1
I know i can iterate in a foreach but would rather a more direct approach.
Perhaps I am conceptually off?
EDIT: Okay, so now it seems like you've got past Count not executing:
But Count is always == 1
Sounds like you're always fetching a list with exactly one entry in. If that's unexpected, you should look at why you're only getting a single entry - start with debugging into the code... this doesn't sound like a Razor problem at all.
If your value were really a List<T> for some T, I believe it should be fine. The expression ViewBag.EquipmentList.Count would evaluate dynamically at every point.
In other words, if you're really, really using the assignment code shown, it should be okay.
If, however, the value is just some implementation of IEnumerable<T> which doesn't expose a Count property, then you'd need to use the Enumerable.Count() extension method - and you can't use normal "extension method syntax" with dynamic typing. One simple fix would be to use:
Enumerable.Count(ViewBag.EquipmentList)
... which will still use dynamic typing for the argument to Enumerable.Count, but it won't have to find the Count() method as an extension method.
Alternatively, make sure that your value really is a List<T>. If it's actually an array, you should be able to change the code to use the Length property instead.
#ViewBag.EquipmentListNI.Count()

Write a lambda expression to perform a calulcation on an list

I have a List/IEnumerable of objects and I'd like to perform a calculation on some of them.
e.g.
myList.Where(f=>f.Calculate==true).Calculate();
to update myList, based on the Where clause, so that the required calulcation is performed and the entire list updated as appropriate.
The list contains "lines" where an amount is either in Month1, Month2, Month3...Month12, Year1, Year2, Year3-5 or "Long Term"
Most lines are fixed and always fall into one of these months, but some "lines" are calulcated based upon their "Maturity Date".
Oh, and just to complicate things! the list (at the moment) is of an anonymous type from a couple of linq queries. I could make it a concrete class if required though, but I'd prefer not to if I can avoid it.
So, I'd like to call a method that works on only the calculated lines, and puts the correct amount into the correct "month".
I'm not worried about the calculation logic, but rather how to get this into an easily readable method that updates the list without, ideally, returning a new list.
[Is it possible to write a lambda extension method to do both the calculation AND the where - or is this overkill anyway as Where() already exists?]
Personally, if you want to update the list in place, I would just use a simple loop. It will be much simpler to follow and maintain:
for (int i=0;i<list.Count;++i)
{
if (list[i].ShouldCalculate)
list[i] = list[i].Calculate();
}
This, at least, is much more obvious that it's going to update. LINQ has the expectation of performing a query, not mutating the data.
If you really want to use LINQ for this, you can - but it will still require a copy if you want to have a List<T> as your results:
myList = myList.Select(f => f.ShouldCalculate ? f.Calculate() : f).ToList();
This would call your Calculate() method as needed, and copy the original when not needed. It does require a copy to create a new List<T>, though, as you mentioned that was a requirement (in comments).
However, my personal preference would still be to use a loop in this case. I find the intent much more clear - plus, you avoid the unnecessary copy operation.
Edit #2:
Given this comment:
Oh, and just to complicate things! the list (at the moment) is of an anonymous type from a couple of linq queries
If you really want to use LINQ style syntax, I would recommend just not calling ToList() on your original queries. If you leave them in their original, IEnumerable<T> form, you can easily do my second option above, but on the original query:
var myList = query.Select(f => f.ShouldCalculate ? f.Calculate() : f).ToList();
This has the advantage of only constructing the list one time, and preventing the copy, as the original sequence will not get evaluated until this operation.
LINQ is mostly geared around side-effect-free queries, and anonymous types themselves are immutable (although of course they can maintain references to mutable types).
Given that you want to mutate the list in place, LINQ isn't a great fit.
As per Reed's suggestion, I would use a straight for loop. However, if you want to perform different calculations at different points, you could encapsulate this:
public static void Recalculate<T>(IList<T> list,
Func<T, bool> shouldCalculate,
Func<T, T> calculation)
{
for (int i = 0; i < list.Count; i++)
{
if (shouldCalculate(items[i]))
{
items[i] = calculation(items[i]);
}
}
}
If you really want to use this in a fluid way, you could make it return the list - but I would personally be against that, as it would then look like it was side-effect-free like LINQ.
And like Reed, I'd also prefer to do this by creating a new sequence...
Select doesn't copy or clone the objects it passes to the passed delegate, any state changes to that object will be reflected through the reference in the container (unless it is a value type).
So updating reference types is not a problem.
To replace the objects (or when working with value types1) this are more complex and there is no inbuilt solution with LINQ. A for loop is clearest (as with the other answers).
1 Remembering, of course, that mutable value types are evil.

Resharper: Possible Multiple Enumeration of IEnumerable

I'm using the new Resharper version 6. In several places in my code it has underlined some text and warned me that there may be a Possible multiple enumeration of IEnumerable.
I understand what this means, and have taken the advice where appropriate, but in some cases I'm not sure it's actually a big deal.
Like in the following code:
var properties = Context.ObjectStateManager.GetObjectStateEntry(this).GetModifiedProperties();
if (properties.Contains("Property1") || properties.Contains("Property2") || properties.Contains("Property3")) {
...
}
It's underlining each mention of properties on the second line, warning that I am enumerating over this IEnumerable multiple times.
If I add .ToList() to the end of line 1 (turning properties from a IEnumerable<string> to a List<string>), the warnings go away.
But surely, if I convert it to a List, then it will enumerate over the entire IEnumerable to build the List in the first place, and then enumerate over the List as required to find the properties (i.e. 1 full enumeration, and 3 partial enumerations). Whereas in my original code, it is only doing the 3 partial enumerations.
Am I wrong? What is the best method here?
I don't know exactly what your properties really is here - but if it's essentially representing an unmaterialized database query, then your if statement will perform three queries.
I suspect it would be better to do:
string[] propertiesToFind = { "Property1", "Property2", "Property3" };
if (properties.Any(x => propertiesToFind.Contains(x))
{
...
}
That will logically only iterate over the sequence once - and if there's a database query involved, it may well be able to just use a SQL "IN" clause to do it all in the database in a single query.
If you invoke Contains() on a IEnumerable, it will invoke the extension method which will just iterate through the items in order to find it. IList has real implementation for Contains() that probably are more efficient than a regular iteration through the values (it might have a search tree with hashes?), hence it doesn't warn with IList.
Since the extension method will only be aware that it's an IEnumerable, it probably can not utilize any built-in methods for Contains() even though it would be possible in theory to identify known types and cast them accordingly in order to utilize them.

Categories

Resources