Lambda Expression - c#

Can I simplify this statement with a lambda expression?
var project = from a in accounts
from ap in a.AccountProjects
where ap.AccountProjectID == accountProjectId
select ap;

var project = accounts.SelectMany(a => a.AccountProjects)
.Where(x => x.AccountProjectID == accountProjectId);
Whether this is actually simpler is a matter of taste.

Honestly, it looks pretty clear to me. I think that a lambda in this case may be less readable, i.e., something like Brandon posted below.
(Stolen from Brandon's post)
var project = accounts.Select(a => a.AccountProjects)
.Where(x => x.AccountProjectID == accountProjectId);
As far as readability is concerned, I think that a couple of loops is preferable to the lambda solution, and I think that your solution is preferable to the loops.

I agree with Ed Swangren. This looks concise and readable enough.
Actually the answer to your question depends on 3 things:
What you want to achieve - better readability? better performance? etc.
The type of 'accounts'
How the resulting collection is going to be used.
If you want better performance, and in case 'accounts' is a List, and the resulting collection will be iterated or passed to another method for iterating soon enough after these lines of code, I would do something like that:
List<Account> filteredAccounts = new List<Account>();
accounts.ForEach(a => { if (a.AccountProjectID == accountProjectId) filteredAccounts.Add(a); });
Surely it's less readable then your LINQ statement, but I would use these 2 lines rather than accounts.Select.......
And surely it's much better optimized for performance, which is always important I believe.

accounts
.SelectMany (
a => AccountProjects,
(a, ct) =>
new
{
a = a,
ap = ap
}
)
.Where (t => (t.ap.AccountProjectID == t.a.accountProjectId))
.Select (t => t.ap)

Related

Optimize Linq in C#

I have columns list in which I need to assign Isselected as true for all except for two columns. (Bug and feature). I have used this following code to achieve it and working fine, but is there any quick or easy way to achieve the same?
DisplayColumns.ToList().ForEach(a => a.IsSelected = true);
DisplayColumns.ToList().Where(a => a.ColumnName == "Bug" || a.ColumnName == "Feature").ToList().ForEach(a => a.IsSelected = false);
Thanks in advance
I have used this following code to achieve it and working fine, but is there any quick or easy way to achieve the same?
Well there's a cleaner way to achieve it in my view - just don't use lambdas etc at all:
foreach (var item in DisplayColumns)
{
item.IsSelected = item.ColumnName != "Bug" && item.ColumnName != "Feature";
}
You can make the decision in one go - it's false if the column name is either "bug" or "feature"; it's true otherwise. And you don't need to call ToList and use ForEach when the C# language has a perfectly good foreach loop construct for when you want to execute some code using each item in a collection.
I love LINQ - it's fantastic - but its sweet spot is querying (hence the Q) rather than manipulation. In this case only the ToList part is even part of LINQ - List<T>.ForEach was introduced in .NET 2.0, before LINQ.
Sure, you can assign the IsSelected at once.
DisplayColumns.ToList().ForEach(a => a.IsSelected = !(a.ColumnName == "Bug" || a.ColumnName == "Feature"));
Provided that DisplayColumns isn't a projection of an anonymous type (in which case the properties are not re-assignable), you'll be able to change the flag in a single pass iteration through the collection.
You can also use Contains to ease the comparison. At class scope:
private static readonly string[] _searches = new [] {"Bug", "Feature"}
In your method:
DisplayColumns
.ToList() // For List.ForEach, although not #JonSkeet's caveat re mutating in Linq
.ForEach(a => a.IsSelected = !_searches.Contains(a.ColumnName));
Edit
As others have mentioned, creation of a new list simply to gain access to .ForEach to change objects in the (original) collection is wasteful and changes will be lost on a collection of value types. Rather, iterate over the original collection with foreach (or even for).
Firstly you only need to call ToList() once when creating a collection from your IEnumerable.
doing this after each operator is costly and redundant.
Secondly just change your condition . all true except for the tow.
DisplayColumns.Where(a => a.ColumnName != "Bug" && a.ColumnName != "Feature").ForEach(a => a.IsSelected = true).ToList();
Edit :
I'm sorry i like a part john's answer since this can be a re occurring thing , or IsSelected could be a Nullable , any ways lets keep it as general as possible .
I also like Stuart's approach , with the collection ( i also thought of it but didn't want to confuse . so let's give the best of all worlds.
when using linq we are actually building an expression tree at the end of which we can choose to materialize into a collection.
there for _searchs can change and each time we materialize that expression we do it with the values currently in that collection , thous making our code much more general .
private static readonly string[] _searches = new [] {"Bug", "Feature"}
DisplayColumns.ForEach(a => a.IsSelected = !_searchs.Contains(a.ColumnName)).ToList();
I'm assuming ForEach is an Extension method for type IEnumrable
Maybe this:
tmp = DisplayColumns.ToList();
var res = tmp.Except(tmp.Where(a => a.ColumnName == "Bug" || a.ColumnName == "Feature"));
foreach(var x in res) x.IsSeleceted = true;
Without using foreach
DisplayColumns
.Select(s=> {
s.IsSelected = (s.ColumnName == "Bug" && s.ColumnName == "Feature");
return s;
});

Can these two LINQ queries be used interchangeably?

a) Would the following two queries produce the same results:
var query1 = collection_1
.SelectMany(c_1 => c_1.collection_2)
.SelectMany(c_2 => c_2.collection_3)
.Select(c_3 => c_3);
var query2 = collection_1
.SelectMany(c_1 => c_1.collection_2
.SelectMany(c_2 => c_2.collection_3.Select(c_3 => c_3)));
b) I assume the two queries can't always be used interchangeably? For example, if we wanted the output elements to also contain values of c_1 and c_2, then we only achieve this with query2, but not with query1:
var query2 = collection_1
.SelectMany(c_1 => c_1.collection_2
.SelectMany(c_2 => c_2.collection_3.Select(c_3 => new { c_1, c_2, c_3 } )));
?
Thank you
The snippets you've given seem to be invalid. c_3 isn't defined in the scope of the Select statement, so unless I've misunderstood something, this won't compile.
It seems as though you're trying to select the elements of collection_3, but this is done implicitly by SelectMany, and so the final Select statements in both cases are redundant. Take them out, and the two queries are equivalent.
All you need is this:
var query = collection_1
.SelectMany(c_1 => c_1.collection_2)
.SelectMany(c_2 => c_2.collection_3);
Update: x => x is the identity mapping, so Select(x => x) is always redundant, regardless of the context. It just means "for every element in the sequence, select the element".
The second snippet is of course different, and the SelectMany and Select statements indeed need to be nested in order to select all three elements, c_1, c_2, and c_3.
Like Gert, says, though, you're probably better off using query comprehension syntax. It's much more succinct and makes it easier to mentally parse the workings of a query.
a. The queries are equal because in both cases you end up with all c_3's in c_1 through c_2.
b. You can't get to c_1 and c_2 with these queries as you suggest. If you want that you need this overload of SelectMany. This "fluent" syntax is quite clumsy though. This is typically a case where comprehensive syntax which does the same is much better:
from c_1 in colection_1
from c_2 in c_1.collection_2
from c_3 in c_2.collection_3
select new { c_1.x, c_2.y, c_3.z }

Lambda Max and Max and Max

Quick and probably easy Lambda question:
I have a restaurant with reviews.
I want to query for the one with the:
Max(AverageRating)
And the Max(ReviewCount)
And the Max(NewestReviewDate)
And the Min(DistanceAway)
Something like this:
var _Result = AllRestaurants
.Max(x => x.AverageRating)
.AndMax(x => x.ReviewCount)
.AndMax(x => x.NewestReviewDate)
.AndMin(x => x.DistanceAway);
Now, I know that is pseudo code. But it describes it perfectly!
Of course, in multiple statements, this is simple.
Just wondering if this is possible in one statement without killing the readability.
Thank you in advance. I know some of you love the query questions!
You can't have multiple maxes or mins, that doesn't make sense.
You'll need some kind of heuristic like:
.Max(x => x.AverageRating * x.ReviewCount - x.DaysSinceLastReview - x.DistanceAway)
Perhaps this would do?
var bestRestaurant = AllRestaurants
.OrderByDescending(r => r.AverageRating)
.ThenByDescending(r => r.ReviewCount)
.ThenByDescending(r => r.NewestReviewCount)
.ThenBy(r => r.DistanceAway)
.FirstOrDefault();
You'd need to change the order of the statements to reflect which is the most important.
An alternative to having some weighted heuristic is to order by AverageRating, then ReviewCount, then ...
Something like this should work:
var _Result = AllRestaurants
.OrderByDescending(x => x.AverageRating)
.ThenByDescending(x => x.ReviewCount)
.ThenByDescending(x => x.NewestReviewDate)
.ThenByDescending(x => x.DistanceAway);
// using *Descending so you get the higer-valued ones first
Consider something like this...
List<RestaurantRecord> _Restaurants;
public RestaurantRecord Best()
{
return _Restaurants.Where(
x =>
x.AverageRating >= _BestRating &&
x.ReviewCount >= _MinReviews &&
x.Distance <= _MaxDistance)
.GetFirstOrDefault();
}
That being said, using a lambda in this case will have maintainability consequences down the road. It would be a good idea to refactor this, such that if other criteria appear in the future (e.g.: smartphone access? Cuisine type?), your app can be more easily modified to adapt to those.
On that note, a slightly better implementation might be something like:
public RestaurantRecord Best()
{
IQueryable temp = _Restaurants.Clone();
temp = temp.Where( x => x.AverageRating >= _BestRating );
temp = temp.Where( x => x.ReviewCount >= _MinReviews );
// ...snip...
return temp.GetFirstOrDefault();
}
I hope this sets you on the right track. :)
If I'm understanding your question, I think the best approach is going to be writing individual statements as you mention...
var HighestRating = AllRestaurants.Max(x => x.AverageRating);
var HighestReviewCount = AllRestaurants.Max(x => x.ReviewCount);
var LatestReviewDate = AllRestaurants.Max(x => x.NewestReviewDate);
var ShortestDistanceAway = AllRestaurants.Min(x => x.DistanceAway);
Retrieving various maxes and mins from a single Linq query would get pretty messy and I'm not sure there'd be any advantage with efficiency, either.

Replacing nested foreach with LINQ; modify and update a property deep within

Consider the requirement to change a data member on one or more properties of an object that is 5 or 6 levels deep.
There are sub-collections that need to be iterated through to get to the property that needs inspection & modification.
Here we're calling a method that cleans the street address of a Employee. Since we're changing data within the loops, the current implementation needs a for loop to prevent the exception:
Cannot assign to "someVariable" because it is a 'foreach iteration variable'
Here's the current algorithm (obfuscated) with nested foreach and a for.
foreach (var emp in company.internalData.Emps)
{
foreach (var addr in emp.privateData.Addresses)
{
int numberAddresses = addr.Items.Length;
for (int i = 0; i < numberAddresses; i++)
{
//transform this street address via a static method
if (addr.Items[i].Type =="StreetAddress")
addr.Items[i].Text = CleanStreetAddressLine(addr.Items[i].Text);
}
}
}
Question:
Can this algorithm be reimplemented using LINQ? The requirement is for the original collection to have its data changed by that static method call.
Update: I was thinking/leaning in the direction of a jQuery/selector type solution. I didn't specifically word this question in that way. I realize that I was over-reaching on that idea (no side-effects). Thanks to everyone! If there is such a way to perform a jQuery-like selector, please let's see it!
foreach(var item in company.internalData.Emps
.SelectMany(emp => emp.privateData.Addresses)
.SelectMany(addr => addr.Items)
.Where(addr => addr.Type == "StreetAddress"))
item.Text = CleanStreetAddressLine(item.Text);
var dirtyAddresses = company.internalData.Emps.SelectMany( x => x.privateData.Addresses )
.SelectMany(y => y.Items)
.Where( z => z.Type == "StreetAddress");
foreach(var addr in dirtyAddresses)
addr.Text = CleanStreetAddressLine(addr.Text);
LINQ is not intended to modify sets of objects. You wouldn't expect a SELECT sql statement to modify the values of the rows being selected, would you? It helps to remember what LINQ stands for - Language INtegrated Query. Modifying objects within a linq query is, IMHO, an anti-pattern.
Stan R.'s answer would be a better solution using a foreach loop, I think.
I don't like mixing "query comprehension" syntax and dotted-method-call syntax in the same statement.
I do like the idea of separating the query from the action. These are semantically distinct, so separating them in code often makes sense.
var addrItemQuery = from emp in company.internalData.Emps
from addr in emp.privateData.Addresses
from addrItem in addr.Items
where addrItem.Type == "StreetAddress"
select addrItem;
foreach (var addrItem in addrItemQuery)
{
addrItem.Text = CleanStreetAddressLine(addrItem.Text);
}
A few style notes about your code; these are personal, so I you may not agree:
In general, I avoid abbreviations (Emps, emp, addr)
Inconsistent names are more confusing (addr vs. Addresses): pick one and stick with it
The word "number" is ambigious. It can either be an identity ("Prisoner number 378 please step forward.") or a count ("the number of sheep in that field is 12."). Since we use both concepts in code a lot, it is valuable to get this clear. I use often use "index" for the first one and "count" for the second.
Having the type field be a string is a code smell. If you can make it an enum your code will probably be better off.
Dirty one-liner.
company.internalData.Emps.SelectMany(x => x.privateData.Addresses)
.SelectMany(x => x.Items)
.Where(x => x.Type == "StreetAddress")
.Select(x => { x.Text = CleanStreetAddressLine(x.Text); return x; });
LINQ does not provide the option of having side effects. however you could do:
company.internalData.Emps.SelectMany(emp => emp.Addresses).SelectMany(addr => Addr.Items).ToList().ForEach(/*either make an anonymous method or refactor your side effect code out to a method on its own*/);
You can do this, but you don't really want to. Several bloggers have talked about the functional nature of Linq, and if you look at all the MS supplied Linq methods, you will find that they don't produce side effects. They produce return values, but they don't change anything else. Search for the arguments over a Linq ForEach method, and you'll get a good explanation of this concept.
With that in mind, what you probaly want is something like this:
var addressItems = company.internalData.Emps.SelectMany(
emp => emp.privateData.Addresses.SelectMany(
addr => addr.Items
)
);
foreach (var item in addressItems)
{
...
}
However, if you do want to do exactly what you asked, then this is the direction you'll need to go:
var addressItems = company.internalData.Emps.SelectMany(
emp => emp.privateData.Addresses.SelectMany(
addr => addr.Items.Select(item =>
{
// Do the stuff
return item;
})
)
);
To update the LINQ result using FOREACH loop, I first create local ‘list’ variable and then perform the update using FOREACH Loop. The value are updated this way. Read more here:
How to update value of LINQ results using FOREACH loop
I cloned list and worked NET 4.7.2
List<TrendWords> ListCopy = new List<TrendWords>(sorted);
foreach (var words in stopWords)
{
foreach (var item in ListCopy.Where(w => w.word == words))
{
item.disabled = true;
}
}

Extension Method to assign value to a field in every item?

I could have sworn that there was an extension method already built for the Queryable class that I just can't find, but maybe I'm thinking of something different.
I'm looking for something along the lines of:
IQueryable<Entity> en = from e in IDB.Entities select e;
en.ForEach(foo => foo.Status = "Complete");
en.Foreach() would essential perform:
foreach(Entity foo in en){
foo.Status = "Complete";
}
Is this already written? If not, is it possible to write said Extension Method, preferably allowing for any LINQ Table and any Field on that table. Where is a good place to start?
There's nothing in the base class library. Many, many developers have this in their own common library, however, and we have it in MoreLINQ too.
It sort of goes against the spirit of LINQ, in that it's all about side-effects - but it's really useful, so I think pragmatism trumps dogma here.
One thing to note - there's really no point in using a query expression in your example. (It's not entirely redundant, but if you're not exposing the value it doesn't matter.) The select isn't doing anything useful here. You can just do:
IDB.Entities.ForEach(foo => foo.status = "Complete");
Even if you want to do a single "where" or "select" I'd normally use dot notation:
IDB.Entities.Where(foo => foo.Name == "fred")
.ForEach(foo => foo.status = "Complete");
Only use query expressions where they actually make the code simpler :)
public static void ForEach<T>(this IEnumerable<T> sequence, Action<T> action)
{
foreach (var item in sequence)
{
action(item);
}
}
There is a foreach on a List<>. Roughly something along these lines:
IQueryable<Entity> en = from e in IDB.Entities select e;
en.ToList().ForEach(foo => foo.status = "Complete");

Categories