Similar to jQuery's end() in LINQ? - c#

Is there something similar to jQuery's end() function in LINQ?
So I could modify a result set from a previous select?

There is nothing similar in .NET. There is not even a guarantee that an IEnumerable<> is enumerable twice (or that the two results will be the same). Often each enumeration will redo the query (or whatever it's). So for example if you do:
** CODE THAT REALLY WORKS, BUT BADLY! (SEE BELOW) **
var persons = from p in ctx.Persons select p;
foreach (var person in persons) { }
foreach (var person in persons) { }
where ctx.Persons is a table in an SQL Server, each foreach will redo the query.
I think it would be possible to build something similar in .NET (a caching stacked enumerator), but you would have to rewrite all the LINQ methods (and this only for LINQ-to-Objects)
There is another problem, more semantical: LINQ is a language for queries, there is no ForEach method. The methods of jQuery are different. They "act" on the items returned by the query. So the only "correct" place for something like this in .NET would be to be able to have more than one result-set for the same query... Something like:
** CODE NOT WORKING, JUST AS AN EXAMPLE **
var res = (from p in myItems select p).StackfulEnumerable()
.Count(p => p.SomeProperty == 1)
.Pop() // like the End() of jquery
.Count(p => p.SomeProperty == 2);
and res in some is now a Tuple<int, int> (in some way... technically it would be very very complex to make it return a Tuple<int, int> in a type safe way, because in C# you can't build type safe generic methods with variable number of arguments)
Now, clearly someone could do this:
** CODE THAT REALLY WORKS **
var enu = // my enumerable
var res = from p in new[] { enu }
let res1 = p.Where(q => q.Prop == 1)
let res2 = p.Where(q => q.Prop == 2)
select Tuple.Create(res1.ToArray(), res2.ToArray());
and it would be ok, but remember that you are enumerating enu twice!
Clearly you could play it safe:
** CODE THAT REALLY WORKS **
var res = from p in new[] { enu.ToList() }
let res1 = p.Where(q => q.Prop == 1)
let res2 = p.Where(q => q.Prop == 2)
select Tuple.Create(res1.ToArray(), res2.ToArray());
now at least you know you'll enumerating twice something that is safe to enumerate any number of times you want.
Note that you could write:
** CODE THAT REALLY WORKS **
select Tuple.Create(res1, res2);
but then every time you would enumerate the two enumerables of the Tuple you would enumerate again the "source" (unless you used the ToList(), as in the second example)

Related

Is a statement recalculated in every iteration when used in LINQ?

For instance
myEnumerable.Where(v => v != myDictionary["someKey"])
when this query is called is myDictionary["someKey"] statement executed (meaning that dictionary is queried for the key) or the result of myDictionary["someKey"]
is used after the first iteration?
The result of myDictionary["someKey"] will not be cached(*see edit below), it will be accessed on every item of myEnumerable. However, you can still cache it manually :
var someValue = myDictionary["someKey"];
myEnumerable.Where(v => v != someValue)
Also take note that, if you plan to iterate/access that IEnumerable multiple time, it is best to actualize it via ToList(). Or, the execution will be deferred every single time.
var query = myEnumerable.Where(v => v != myDictionary["someKey"]);
foreach (var item in query) { /* ... */}
foreach (var item in query) { /* ... */}
In the above example, the Where clause is executed twice.
EDIT: As #LucasTrzesniewski has pointed out, this is only stands true for LINQ-to-Objects. This is because the query is evaluated in memory. However, for LINQ-to-Entities, it gets a little bit different, as the query will be converted into SQL query and then executed in the database in order to avoid round trips.
Here's a really simple demo (and please, don't try this at home):
var myDictionary = new Dictionary<string,string>() { { "someKey", "someValue" } };
var myEnumerable = new List<string> { "someValue", "someOtherValue" };
var test = myEnumerable.Where(v => v == myDictionary["someKey"]);
foreach (var t in test)
{
Console.WriteLine(t);
myDictionary["someKey"] = "someOtherValue";
}
If myDictionary["someKey"] was only evaulated once, then changing the value of myDictionary["someKey"] wouldn't change anything. But if you run the code, you will see that it will echo both someValue and someOtherValue. If you comment out the line that changes the dictionary value, then you will only see someValue
As #Lucas Trzesniewski points out in the comments to the other answer, this applies to LINQ-to-objects. There are a number of important differences between LINQ-to-objects and LINQ-to-SQL.
The Lambda expression you supply to the Linq Where extension is simply a Func<> delegate. The method is executed for each item in the IEnumerable(of T), receiving the current item as a parameter. It doesn't do anything special other than that. Your code is somewhat similar similar to:
var myTempCollection = new List<MyClass>();
foreach(MyClass item in myEnumerable)
{
if (item != myDictionary["someKey"])
{
myTempCollection.Add(item);
}
}
var result = myTempCollection;
It depends on the QueryProvider implementation. For example, the ObjectQueryProvider used by Linq-to-objects will access it on every iteration. For Linq-to-entities, it will access it once and then send that value to the database server.

C# and LINQ - arbitrary statement instead of let

Let's say I'm doing a LINQ query like this (this is LINQ to Objects, BTW):
var rows =
from t in totals
let name = Utilities.GetName(t)
orderby name
select t;
So the GetName method just calculates a display name from a Total object and is a decent use of the let keyword. But let's say I have another method, Utilities.Sum() that applies some math on a Total object and sets some properties on it. I can use let to achieve this, like so:
var rows =
from t in totals
let unused = Utilities.Sum(t)
select t;
The thing that is weird here, is that Utilities.Sum() has to return a value, even if I don't use it. Is there a way to use it inside a LINQ statement if it returns void? I obviously can't do something like this:
var rows =
from t in totals
Utilities.Sum(t)
select t;
PS - I know this is probably not good practice to call a method with side effects in a LINQ expression. Just trying to understand LINQ syntax completely.
No, there is no LINQ method that performs an Action on all of the items in the IEnumerable<T>. It was very specifically left out because the designers actively didn't want it to be in there.
Answering the question
No, but you could cheat by creating a Func which just calls the intended method and spits out a random return value, bool for example:
Func<Total, bool> dummy = (total) =>
{
Utilities.Sum(total);
return true;
};
var rows = from t in totals
let unused = dummy(t)
select t;
But this is not a good idea - it's not particularly readable.
The let statement behind the scenes
What the above query will translate to is something similar to this:
var rows = totals.Select(t => new { t, unused = dummy(t) })
.Select(x => x.t);
So another option if you want to use method-syntax instead of query-syntax, what you could do is:
var rows = totals.Select(t =>
{
Utilities.Sum(t);
return t;
});
A little better, but still abusing LINQ.
... but what you should do
But I really see no reason not to just simply loop around totals separately:
foreach (var t in totals)
Utilities.Sum(t);
You should download the "Interactive Extensions" (NuGet Ix-Main) from Microsoft's Reactive Extensions team. It has a load of useful extensions. It'll let you do this:
var rows =
from t in totals.Do(x => Utilities.Sum(x))
select t;
It's there to allow side-effects on a traversed enumerable.
Please, read my comment to the question. The simplest way to achieve such of functionality is to use query like this:
var rows = from t in totals
group t by t.name into grp
select new
{
Name = t.Key,
Sum = grp.Sum()
};
Above query returns IEnumerable object.
For further information, please see: 101 LINQ Samples

Why did my code work when I changed it from IEnumerable to List?

I'm trying to get my head around this rather than just chalking it up to general voodoo.
I do an EF query and get some data back, and I .ToList() it, like this:
IEnumerable<DatabaseMatch<CatName>> nameMatches = nameLogicMatcher.Match(myIQueryableOfCats).ToList();
Some cats appear twice in the database because they have multiple names, but each cat has a primary name. So in order to filter this down, I get all of the ids of the cats in a list:
List<int> catIds = nameMatches.Select(c => c.Match.CatId).ToList();
I then iterate through all of the distinct ids, get all of the matching cat names, and remove anything that isn't a primary name from the list, like this:
foreach (int catId in catIds.Distinct())
{
var allCatNameMatches = nameMatches.Where(c => c.Match.CatId == catId);
var primaryMatch = allCatNameMatches.FirstOrDefault(c => c.Match.NameType == "Primary Name");
nameMatches = nameMatches.Except(allCatNameMatches.Where(c => c != primaryMatch));
}
Now this code, when I first ran it, just hung. Which I thought was odd. I stepped through it, and it seemed to work but after 10 iterations (it is capped at 100 cats in total) it started to slow down and then eventually it was glacial and then hung completely.
I thought maybe it was doing some intensive database work by mistake, but the profiler shows no SQL executed except that which retrieves the initial list of cat names.
I decided to change it from IEnumerable of nameMatches to a List, and put the appropriate .ToList() on the last line. It worked instantly and perfectly after I did this.
The question I'd like to ask is, why?
Without the ToList() you are building up in nameMatches a nested chain of IEnumerables awaiting delayed execution. This might not be so bad, except you are also calling FirstOrDefault on each iteration which will execute the chain. So on iteration number n, you are executing the filter operations contained in the loop n-1 times. If you had 1000 distinct cats, the Linq chain is getting executed 1000 + 99 + ... + 1 times. (I think you have something that is O(n³)!)
The moral is, if you want to use delayed execution, make very sure that you're only executing your chain once.
Let's simplify your code a little:
foreach (int catId in catIds.Distinct())
{
var allCatNameMatches = nameMatches.Where(c => c.Match.CatId == catId);
var primaryMatch = null;
nameMatches = nameMatches.Except(allCatNameMatches.Where(c => c != primaryMatch));
}
And a little more:
foreach (int catId in catIds.Distinct())
{
nameMatches = nameMatches.Where(c => c.Match.CatId == catId);
var primaryMatch = null;
nameMatches = nameMatches.Except(nameMatches.Where(c => c != primaryMatch));
}
In the latter one it is obvious that due to deferred execution each pass of foreach body lengthens the chain of Where and Except. Then remember var primaryMatch = allCatNameMatches.FirstOrDefault. It is not deferred executed, so in each iteration of foreach it should execute all chain. Therefore it hangs.

Linq - getting a value from a string

First question on SO - I've read it many, many times so time to drop in and get my feet wet in the community!
I start by getting a single row from a Linq query:
var relationshipDetails = (from p in dc.tbl_ClientRelationships
where p.ID == relationship_id
select p).FirstOrDefault();
Then I look through a list of strings (_cols), which is the known column names (and also form item names) like so:
foreach (string c in _cols)
{
if (relationshipDetails.GetType().GetProperty(c).GetValue(relationshipDetails, null).ToString() != null)
{
setValue(relationshipDetails.GetType().GetProperty(c).GetValue(relationshipDetails, null).ToString(), c);
}
}
the setValue() method basically assigns the returned value to the webcontrol (and has logic to determine the type and how it should be assigned etc..)
My question, is there a better way to get a value out of a Linq object from a know property value?
It works on some forms but has recently just blown up on me!
Otherwise, I'm tempted to go back to the old method or returning a DataRow from the DAL and just reference by name easily!
Thanks in advance,
Mark
One of the biggest advantages (in my opinion) of Linq to (Sql / Entities) is that the objects returned are strongly-typed. You're using LinqToX and then using reflection to assign values, you are basically doing what the old school DataRow did.
I'm not sure why you are trying to dynamically assign values. This definitely is an XY Problem.
First:
var relationshipDetails = (from p in dc.tbl_ClientRelationships
where p.ID == relationship_id
select p).FirstOrDefault();
Linq queries are objects that represent the query, keep them separate and distinct from the results of those queries. In this case I'd suggest something like this instead:
var relationshipDetails = dc.tbl_ClientRelationships
.FirstOrDefault( p => p.Id == relationship_id);
Now, this is going to be very slow:
foreach (string c in _cols)
{
if (relationshipDetails.GetType().GetProperty(c).GetValue(relationshipDetails, null).ToString() != null)
{
setValue(relationshipDetails.GetType().GetProperty(c).GetValue(relationshipDetails, null).ToString(), c);
}
}
You can easily get a reference to the reflection members and cut down on the overhead, maybe something like this: (Might not be 100% syntax correct)
var properties = relationshipDetails.GetType().GetProperties();
foreach (string c in _cols)
{
var currentProperty = properties.Single( p=> p.Name == c );
if (currentProperty.GetValue(relationshipDetails, null) != null)
{
setValue(currentProperty.GetValue(relationshipDetails, null).ToString(), c);
}
}
Finally - Why are you doing this? Please detail exactly what you are trying to do, and why refering to the columns in a type safe named manner ie:
relationshipDetails.Id = ...
relationshipDetails.SomethingElse = ...
relationshipDetails.AnotherThing = ...
Won't work in your case.

How To Use Yield Inside A Linq Query

I have the following method:
public string GetDepartmentTitle(string DepartmentAbbreviation) {
List<TaxonomyItem> Divisions = TaxonomyFromCMS.GetAllItems(DomainDataConstants.DivisionAndDepartment.TAXONOMY_ID);
List<TaxonomyItem> Departments = new List<TaxonomyItem>();
Divisions.ForEach(delegate(TaxonomyItem Division) {
Departments.AddRange(Division.SubTaxonomyItems);
});
TaxonomyItem Result = (from d in Departments
where d.Name == DepartmentAbbreviation
select d).FirstOrDefault();
return Result == null ? "" : Result.Title;
}
It first reads all of the Divisons (which there are only 3) but those divisions have many Departments below them as SubTaxonomyItems. Currently I step through each of the Divisions and extract out each of the Departments and put them in a List called Departments. Then I use Linq to search for the specific item.
It works great but I would love to skip/consume that first step of getting the sub items. I have tried the following line that doesn't seem to work:
TaxonomyItem Result = (from d in Departments.SubTaxonomyItems
I then through perhaps some sort of lambda with a foreach of the Departments.SubTaxonomyItems that contains a yeild statement. That may be the trick, but I couldn't get it to work. Looking into the yeild statement it seems there can be a way if I make some extension method. But I am wanting to see if it can be done inline and like the following pseudo code:
public string GetDepartmentTitle(string DepartmentAbbreviation) {
List<TaxonomyItem> Divisions = TaxonomyFromCMS.GetAllItems(DomainDataConstants.DivisionAndDepartment.TAXONOMY_ID);
TaxonomyItem Result = (from d in Divisions.ForEach(delegate(TaxonomyItem Division) {
yeild return Divison.SubTaxonomyItems;
}) AS Dps
where Dps.Name == DepartmentAbbreviation
select Dps).FirstOrDefault();
return Result == null ? "" : Result.Title;
}
Is this possible this way or some other way I am not seeing? Can it even be done without an extension method?
First off, you can solve your problem easily by just adding another "from" to the query:
var query = from division in divisions
from department in division.Departments
where department.Name == whatever
select department;
This does exactly what you were doing; it selects out the sequence of departments from each division, and glues all those sequences together to make one long sequence of departments.
This gives you a slick syntax for the "stitch together a bunch of sequences" scenario. More generally though, sometimes you run into this sort of situation:
var bars = from foo in foos
some complicated query logic here
select foo.bar;
var abcs = from bar in bars
some other query logic here
select bar.abc;
and you want to figure out how to make this into a single query. You can do that like this:
var abcs = from bar in (
from foo in foos
some complicated query logic here
select foo.bar)
some other query logic here
select bar.abc;
which is ugly, or you can do this:
var abcs = from foo in foos
some complicated query logic here
select foo.bar into bar
some other query logic here
select bar.abc;
That does exactly the same thing but it is more pleasant to read. This syntax is called a "query continuation".
To answer your specific question: it is not legal to put a "yield return" in an anonymous method or lambda. This is quite unfortunate because it would be really useful. The transformations that the compiler performs to make anonymous functions and iterator blocks work are quite complex and thus far we have always punted on getting them to work together fully. (That is, you can put a lambda in an iterator block, but you can't put an iterator block in a lambda.) I hope, but do not promise, that some day we'll be able to fix this code up and allow iterator block lambdas. (Remember, Eric's musings about future language features are For Entertainment Purposes Only.)
It looks like you just want something like this.
public string GetDepartmentTitle(string DepartmentAbbreviation) {
var items = TaxonomyFromCMS.GetAllItems(DomainDataConstants.DivisionAndDepartment.TAXONOMY_ID);
var result = items.SelectMany(item=>item.SubTaxonomyItems).FirstOrDefault(item=>item.Name == DepartmentAbbreviation);
var text = result !=null ? result.Title : String.Empty;
return text;
}
Yield return can only be used in very select (pun!) locations, and a Linq query isn't one of them. Luckily you don't need it here.
var q = from division in Divisions
from dps in division.SubTaxonomyItems
where dps.Name == DepartmentAbbreviation
select dps.Title;
return q.FirstOrDefault() ?? String.Empty;
Why not just do:
var divisions = TaxonomyFromCMS.GetAllItems
(DomainDataConstants.DivisionAndDepartment.TAXONOMY_ID);
var titles = from division in divisions
from deparment in division.SubTaxonomyItems
where deparment.Name == DepartmentAbbreviation
select deparment.Title;
return titles.FirstorDefault() ?? "";
Is this linq you are looking for?
var Result = Divisions.SelectMany(d => d.SubTaxonomyItems).Where(subItem => subItem.Name == DepartmentAbbreviation).FirstOrDefault();

Categories

Resources