Linq deferred execution with local values - c#

I've been experimenting with Linq to see what it can do - and I'm really loving it so far :)
I wrote some queries for an algorithm, but I didn't get the results I expected... the Enumeration always returned empty:
case #1
List<some_object> next = new List<some_object>();
some_object current = null;
var valid_next_links =
from candidate in next
where (current.toTime + TimeSpan.FromMinutes(5) <= candidate.fromTime)
orderby candidate.fromTime
select candidate;
current = something;
next = some_list_of_things;
foreach (some_object l in valid_next_links)
{
//do stuff with l
}
I changed the query declaration to be inline like this, and it worked fine:
case #2
foreach (some_object l in
(from candidate in next
where (current.toTime + TimeSpan.FromMinutes(5) <= candidate.fromTime)
orderby candidate.fromTime
select candidate))
{
//do stuff with l
}
Does anybody know why it doesn't work in case #1 ?
The way I understood it, the query wasn't evaluated when you declared it, so I don't see how there is a difference.

Changes to current will be captured, but the query already knows the value of next. Adding extra items to the existing list will make them show up in the query, but changing the value of the variable to refer to a different list entirely won't have any effect. Basically, if you mentally expand the query from a query expression into a "normal" form, any variable present in a lambda expression will be captured as a variable, but any variable present directly as an argument will be evaluated immediately. That will only capture the reference value of the variable, not the items present in the list, but it still means changing the variable value itself won't be seen. Your first query expands to:
var valid_next_links = next
.Where(candidate => (current.toTime + TimeSpan.FromMinutes(5) <= candidate.fromTime))
.OrderBy(candidate => candidate.fromTime);

Related

DataTable Linq with two join statements in C#

My code:
var Result = from TempRow in ArrAbsDiff
where TempRow.Strike == StrikeOfMinAbsDiff
select TempRow.PutMidPrice;
I know that the above code return just one value of type decimal (maybe double). However, Result is of type Enumerable and I could not easily write Result + 2. I need to convert it property. I can do it through the following code:
var Result = (from TempRow in ArrAbsDiff
where TempRow.Strike == StrikeOfMinAbsDiff
select TempRow.PutMidPrice).Min();
Is there more efficient way to accomplish it?
Regards,
I know that the above code return just one value of type decimal
Then use First method instaed of Min.
At compile time, it's unknown whether the query you have returns 1 record or more than 1 record. So, it assumes a list of them.
I don't know if there is a performance difference between them, I typically use .Single() to get the single record. It's more clear that I want a single record and not, for example, the minimum one.
Try using FirstOrDefault()" this selects the first result returned (as you know only one value will be returned). if a value is not returned the Result will be null, catch this in an if statement like below:
var Result = (from TempRow in ArrAbsDiff
where TempRow.Strike == StrikeOfMinAbsDiff
select TempRow.PutMidPrice).FirstOrDefault();
if (Result == null)
return; // or error message
this will also keep the type of value returned
(typed this of the top of my head, may need slight changes!)

Why multiple filters are applied even if query recreated on each iteration

I found this code below in a file called Filter.cs in a project created with Microsoft App Studio. Although I am a veteran C# programmer, I'm short on experience with LINQ predicate expression builders. I can tell that the code below it is "meta-logic" for flexibly building a query given a list of filter predicates containing type field info and a set of data values to inject into the sub-expressions. What I can't figure out is how the "expression" variable in the following statement:
query = query.Where(expression).AsQueryable()"
.. is concatenating the per-field expressions into a more complex query expression that is finally executed at the end of the code to create the ObservableCollection result. If it was "query +=" I could infer a chaining action like an Event handler field, but as a straight assignment statement it baffles me since I would expect it to replace the last value the expression variable got from the last loop iteration, thereby resetting it in the process and losing its previous value(s). What is going on here?
public class Filter<T>
{
public static ObservableCollection<T> FilterCollection(
FilterSpecification filter, IEnumerable<T> data)
{
IQueryable<T> query = data.AsQueryable();
foreach (var predicate in filter.Predicates)
{
Func<T, bool> expression;
var predicateAux = predicate;
switch (predicate.Operator)
{
case ColumnOperatorEnum.Contains:
expression = x => predicateAux.GetFieldValue(x).ToLower().Contains(predicateAux.Value.ToString().ToLower());
break;
case ColumnOperatorEnum.StartsWith:
expression = x => predicateAux.GetFieldValue(x).ToLower().StartsWith(predicateAux.Value.ToString().ToLower());
break;
case ColumnOperatorEnum.GreaterThan:
expression = x => String.Compare(predicateAux.GetFieldValue(x).ToLower(), predicateAux.Value.ToString().ToLower(), StringComparison.Ordinal) > 0;
break;
case ColumnOperatorEnum.LessThan:
expression = x => String.Compare(predicateAux.GetFieldValue(x).ToLower(), predicateAux.Value.ToString().ToLower(), StringComparison.Ordinal) < 0;
break;
case ColumnOperatorEnum.NotEquals:
expression = x => !predicateAux.GetFieldValue(x).Equals(predicateAux.Value.ToString(), StringComparison.InvariantCultureIgnoreCase);
break;
default:
expression = x => predicateAux.GetFieldValue(x).Equals(predicateAux.Value.ToString(), StringComparison.InvariantCultureIgnoreCase);
break;
}
// Why doesn't this assignment wipe out the expression function value from the last loop iteration?
query = query.Where(expression).AsQueryable();
}
return new ObservableCollection<T>(query);
}
My understanding is that you are having trouble understanding why this line executed in a loop
query = query.Where(expression).AsQueryable();
produces an effect similar to "concatenation" of expressions. A short answer is that it is similar to why
str = str + suffix;
produces a longer string, even though it is an assignment.
A longer answer is that the loop is building an expression one predicate at a time, and appends a Where to the sequence of conditions. Even though it is an assignment, it is built from the previous state of the object, so the previous expression is not "lost", because it is used as a base of a bigger, more complex, filter expression.
To understand it better, imagine that the individual expressions produced by the switch statement are placed into an array of IQueryable objects, instead of being appended to query. Once the array of parts is built, you would be able to do this:
var query = data.AsQueryable()
.Where(parts[0]).AsQueryable()
.Where(parts[1]).AsQueryable()
...
.Where(parts[N]).AsQueryable();
Now observe that each parts[i] is used only once; after that, it is no longer needed. That is why you can build the chain of expressions incrementally in a loop: after the first iteration, query contains a chain that includes the first term; after the second iteration, it contains two first terms, and so on.
It doesn't "wipe it out" since it is chaining. It's handling it by assigning back to query. It's effectively like writing:
var queryTmp = query;
query = queryTmp.Where(expression).AsQueryable();
Each time you call .Where(expression).AsQueryable(), a new IQueryable<T> is being returned, and set to query. This IQueryable<T> is the result of the last .Where call. This means you effectively get a query that looks like:
query.Where(expression1).AsQueryable().Where(expression2).AsQueryable()...
Code essentially generates sequence of Where/AsQueryable calls. Not sure why you expect each loop to append expressions.
Essentially result is
query = query
.Where(expression0).AsQueryable()
.Where(expression1).AsQueryable()
.Where(expression2).AsQueryable()
where I think you expect more like
query = query
.Where(v => expression0(v) && expression1(v) && expression2(v) ...).AsQueryable()
The query variable name is a bit misleading. This code doesn't build up a long filter in the expression variable and then run it against the data set - it runs each filter against the data set, one at a time, until all of the filters have been run. The query variable just contains everything from the data that is left over from the previously run filters.
So this line:
query = query.Where(expression).AsQueryable();
is applying the filter to the existing data stored in query, and then saving the new (filtered) result back into the variable. The value of expression is overwritten each time through the loop, but we don't need it anymore because the filter has already been applied.

Passing object to linq result

I get an item from the list to manuplate it.
App.CurrentQuestion = App.AllQuestionList[dataSourceRowIndex];
After manuplations I save new data to database and get back from database into App.CurrentQuestion, so App.CurrentQuestion's list reference breaks. I want to update list and trying to focus selected item by linq
App.AllQuestionList
.Where(q => q.qID == App.CurrentQuestion.qID)
.FirstOrDefault() = App.CurrentQuestion;
but I get an error like "The left-hand side of an assignment must be a variable, property or indexer"
I can use this method
for (int i = 0; i < App.AllQuestionList.Count; i++)
{
if (App.AllQuestionList[i].qID == App.CurrentQuestion.qID)
{
App.AllQuestionList[i] = App.CurrentQuestion;
break;
}
}
but looking for an alternative method. Or is there any faster method?
You shouldn't have to do anything, since it's by reference.
App.CurrentQuestion = App.AllQuestionList[dataSourceRowIndex];
Whatever change you make in App.CurrentQuestion should be reflected in the App.AllQuestionList
App.AllQuestionList[App.AllQuestionList.IndexOf(App.AllQuestionList.
.Where(q => q.qID == App.CurrentQuestion.qID)
.First())] = App.CurrentQuestion;
Edit: you can just use the IndexOf to find the index of the object you wanted to find by LINQ query

How to group then select elements into new class in LINQ (C# preferably)

Hi I am trying to get my head around grouping, and then building my own class in the result. I know the result of a group by is an IGrouping collection but can I access the rows as they are being built to add a couple of flags to them with a custom class?
I have a class called FlightTimes with some data, but I'd like to append some data to the rows, like a FlagRedEye. So I created a class called FlightTimeResult with the original FlightTime class data plus the flag.
Can I do this? I can't seem to figure out how to get it to work. I like to use strong types until I understand what is going on. I had to change a few things to protect my client so I apologize for any syntax errors.
IGrouping<string, FlightTimeResult> FlightTimes =
( from flighttimes in schedules.FlightTimes
group flighttimes by flighttimes.FlightType.ToString()
into groupedFlights
select new FlightTimeResult( )
{
FlightTimeData = FlightTime, // Original class data
FlagRedEye = (FlightTime.departureTime.Hour >= 0 &&
FlightTime.departureTime.Hour < 6) // Extra flag
} )
The goal is to have a collection of FlightTimesResult (FlightTime + extra flag) grouped by FlightType. Not sure how to access the individual FlightTime rows in the query 'select new FlightTimeResult()'
Do i need to use a nested query on the groupedFlights?
Thank you very much.
It is easiest achieved by calling Linq functions explicitly in following way:
IQueryable<IGrouping<string, FlightTimeResult>> query
= schedules.FlightTimes.GroupBy(
ft => ft.FlightType.ToString(), // key
ft => new FlightTimeResult() { // your constructed objects for key
FlightTimeData = ft,
FlagRedEye = (ft.departureTime.Hour >= 0 && ft.departureTime.Hour < 6)
}
);
The two-argument GroupBy operator function takes two lambdas as arguments - one for extracting keys, second for extracting values for it.
Also keep in mind that group by operation (be it group itm by key construction or GroupBy call) returns a collection of IGrouping<,>s - not a single one.
Thus it will be IEnumerable<IGrouping<,>> or IQueryable<IGrouping<,>>.
I think you're on the right track. Instead of grouping FlightTimes by FlightType, try building FlightTimeResults and grouping those by FlightType instead:
var results =
from ft in schedules.FlightTimes
group new FlightTimeResult
{
FlightTimeData = ft,
FlagRedeye = ft.DepartureTime.Hour >= 0 && ft.DepartureTime.Hour < 6
}
by ft.FlightType.ToString()
into groupedFlights
select groupedFlights;

LINQ to returns count even the list is empty

I have this block of code inside another LINQ statement.
VideoLinks = (from video in m.VideoLinks.DefaultIfEmpty(new VideoGallery())
orderby video.CreatedDate
select new VideoGallery()
{
Source = video.Source,
Type = video.Type,
Links = video.Links,
Title = video.Title
}).ToList()
unfortunately if dont use DefaultIfEmpty its through me an exception. If I use DefaultIfEmpty i give count as 1 for videolinks even if m.VideoLinks is null.
So now how can avoid getting count 1 if m.VideoLinks is null
DefaultIfEmpty is going to give you a default value. It counts as an item when you call ToList() on it...thus your count is 1.
This looks like you are using linq-to-objects, so you should add a where video != null
VideoLinks = (from video in m.VideoLinks
where video != null
orderby video.CreatedDate
select new VideoGallery()
{
Source = video.Source,
Type = video.Type,
Links = video.Links,
Title = video.Title
}).ToList()
wllmsaccnt is correct - your problem is the "DefaultIfEmpty" portion of your statement. By definition, you are requesting that there be at least one item in the collection, according to the definition of this method:
Returns the elements of the specified sequence or the specified value
in a singleton collection if the sequence is empty
I think the important question here is what exception are you receiving when you don't use DefaultIfEmpty? If you tell us that, perhaps we can help you avoid it...

Categories

Resources