I have a LINQ statement that use average on grade, but the problem is that sometimes
grade gets like 3.777777 displayed, but I would like it to be 3.7 how can I truncate it in my linq statement?
this is my LINQ statement:
public List<CoreValueAndAverageGrade> GetAverageGradeForAllCoreValues2()
{
var answers = db.GoalCardQuestionAnswer
.Where(answer => answer.Grade.HasValue
&& (answer.SelectedQuestion.Question is CoreValueQuestion
&& (answer.SelectedQuestion.Question as CoreValueQuestion).SubjectType.Ignored_Statistic == false));
var groupedByCoreValue = answers.GroupBy(answer => (answer.SelectedQuestion.Question as CoreValueQuestion).CoreValue);
return groupedByCoreValue
.OrderBy(group => group.Key.Name)
.Select(group => new CoreValueAndAverageGrade
{
CoreValue = group.Key,
AverageGrade = group.Any() ? group.Average(answer => answer.Grade.Value) : 0
}).ToList();
Maby its possible to do it inside the controller in my action method?
var averageGrades = OfficeStatisticRepository.GetAverageGradeForAllCoreValues2();
var dataItems = (averageGrades.Select(averageGrade => averageGrade.AverageGrade).ToArray()); // here
You have three options here.
1) Round in the Linq query using Math.Truncate Just like you would use Average or Aggregate. as part of the Linq->SQL translation.
...
... ? group.Average(answer => Math.Truncate(10 * answer.Grade.Value) / 10) : 0M
...
Funny that the docs mention a System.Math.Truncate(decimal, int) method, but that it doesn't actually exist... Luckily you can just multiply and round. This will work fine for Decimals, but if your grade is a Double, it might cause new rounding issues because of the division.
2) Round the values in your Linq query after calling ToList using decimal.Round (be sure to pick the right rounding direction, for grades you won't want to use bankers rounding.
var groupedByCureValue = answers.GroupBy....ToList();
/* then in the next query use Math.Truncate or Math.Round as you
would otherwise, you can now use MidPointRounding if you want to
no sql translation is done as this is all executed in memory,
so you're free to use any framework method available to you. */
3) Keep the values unchanged and only display the rounded value in your UI using a display format such as F1 on the textbox/label/binding you're using to display the value. How you'll set this up is dependent on the display framework you're using. This will not combine values if there's a 3.77 and a 3.76 in the group.
Related
I have come across a scenario where i am summing in my LINQ query.
The property could have actually NULL in database.
However, when we apply aggregate i.e. SUM on same field in collection using LINQ it calculates/returns 0 for null
I am avoiding sum for null field as following.
TotalDays = x.Select(y => y.day.HasValue ? x.Sum(z => z.day) : null).FirstOrDefault(),
Is it nice way or could have even better?
Null values sum to zero because naturally they can neither add nor subtract to the tally so generally one wants zero in such cases.
Consider:
(new int?[]{0, null, 3, 2}).Sum() // result is 5. Other linq providers do similar.
Where this can sometimes cause a problem is if you want to note all-null result-sets separately:
(new int?[]{null, null}).Sum() // result is 0, but maybe we want to note that there was indeed no values.
We could do this with:
source.Any(x => x.HasValue) ? source.Sum() : default(int?);
Which to bring back to your example would be:
int? totalDays = x.Any(y => y.day.HasValue) ? x.Sum(y => y.day) : default(int?);
However you might prefer to do:
int? totalDays = x.Sum(y => y.day);
if (totalDays == 0 && y.All(y => !y.day.HasValue))
totalDays = null;
Then you only examine the set to see if all values are null in the case of receiving the 0 result (any other result is not possible in this case).
Checking Any() first is more efficient when all-null results are more common, and doing Sum() first is more efficient when all-null results less common, because in each case you are only doing two operations in the less common case.
in my polynomial class all the terms consist of a List of tuples (double, uint), representing the coefficient and the exponent; a real and a natural number. The +operator implementation works great, but I was wondering if I could avoid to write two times grouping.Sum(s => s.Item1) It somehow feels not good, but I can't seem find a way to circumvent it.
Here is the code:
public static tuplePolynomial operator +(tuplePolynomial tp1, tuplePolynomial tp2)
{
tuplePolynomial Result = new tuplePolynomial();
Result.Terms =
(
from t in tp1.Terms.Concat(tp2.Terms)
group t by t.Item2 into grouping
where grouping.Sum(s => s.Item1) != 0.0
select new Tuple<double, uint>(grouping.Sum(s => s.Item1), grouping.Key)
).ToList();
return Result;
}
I actually merge the two polymonial's terms and group the terms with the same exponents to sum them. I filter out the terms with zero exponents. Terms is of type List<Tuple<double,uint>>.
This is easy with the let clause:
from t in tp1.Terms.Concat(tp2.Terms)
group t by t.Item2 into grouping
let sum = grouping.Sum(s => s.Item1)
where sum != 0.0
select new Tuple<double, uint>(sum, grouping.Key)
You could just move your where condition outside and apply it after projecting the new tuples.
Then you only apply the Sum operator on each group once and filter the resulting zero sums out before you call ToList.
The code would look something like:
Result.Terms = ( ... ).Where(t => t.Item1 != 0).ToList();
Problem details:
SQL Server 2005;
Entity Framework 4.0.
I'm trying with linq to run a query for a period of time, only. Exemple:
I have the following datetime data in my server:
30/03/2012 12:53:22
30/03/2012 17:23:29
04/04/2012 11:10:14
04/04/2012 19:06:55
I want to run a query that will return me all data between the time (12:00 and 20:00) and the query have to return to me the following data:
30/03/2012 12:53:22
30/03/2012 17:23:29
04/04/2012 19:06:55
Or between (11:00 and 13:00) and the query have to return to me the following data:
30/03/2012 12:53:22
04/04/2012 11:10:14
How can i do this with linq? Is it possible (to ignore the date, use only the time)?
var filteredTimes = myContext.MyTable
.Where(r => SqlFunctions.DatePart("hour", r.DateField) >= 11 &&
SqlFunctions.DatePart("hour", r.DateField) <= 13);
You need to include System.Data.Objects.SqlClient to get SqlFunctions.
If you convert the date value to a double and use the fractional part of if, you get a number between 0 and 1 that represents a time in a day. Having that, it is trivial to test for time intervals, where e.g. 13:45 would be 0,5729166667 (or more precise: 13:45.345 is 0,572920679).
You can do this because EF (i.e. 4.3, the version I use) translates Cast and even Math functions into SQL:
mycontext.Data.Where(dt => dt.DateTime.HasValue)
.Select(dt => dt.DateTime.Value).Cast<double>()
.Select(d => d - Math.Floor(d))
.Where(... your comparisons
This translates to Sql containing CAST([date column] AS float) and Sql's FLOOR function.
After your comments:
It looked so easy, but I can't find a way to instruct EF to do a CAST on a single property in a Select(). Only the Cast<>() function is translated to CAST (sql), but that operates on a set.
Well, fortunately, there is another way:
mycontext.Data.Where(dt => dt.DateTime.HasValue)
.Select(dt => new
{
Date = DbFunctions.TruncateTime(dt.DateTime.Value),
Ms = DbFunctions.DiffMilliseconds(
DbFunctions.TruncateTime(dt.DateTime.Value), dt.DateTime.Value)
})
.Where(x => x.Ms > lower && x.Ms < upper)
where lower and upper are the TotalMilliseconds property of TimeSpan objects.
Note that this is horribly inefficient in sql! The date functions are repeated for each comparison. So make sure that you do the comparisons on a set of data that has been confined as much as possible by other criteria. It may even be better to fetch data in memory first and then do the comparisons.
Note: prior to EF6, DbFunctions was EntityFunctions.
If you had a date and a time column in your table (as possible with SQL Server 2008), you could do this directly in SQL.
As that's not the case, you have to do it like this:
// the first two lines can be omitted, if your bounds are already timespans
var start = startDate.TimeOfDay;
var end = endDate.TimeOfDay;
var filteredItems = context.Items.ToList()
.Where(x => x.DateTimeColumn.TimeOfDay >= start
&& x.DateTimeColumn.TimeOfDay <= end);
-You can use the TimeOfDay property on the dates to compare them.
string timeAsString = "13:00";
from f in TableRows
where f.Date.TimeOfDay > DateTime.Parse("11-12-2012 "+timeAsString).TimeOfDay
select f
EDIT
here is some code you can test that runs for me:
DateTime d = DateTime.Parse("12-12-2012 13:00");
List<DateTime> dates = new List<DateTime>();
dates.Add(DateTime.Parse("12-12-2012 13:54"));
dates.Add(DateTime.Parse("12-12-2012 12:55"));
dates.Add(DateTime.Parse("12-12-2012 11:34"));
dates.Add(DateTime.Parse("12-12-2012 14:53"));
var result = (from f in dates where f.TimeOfDay > d.TimeOfDay select f);
EDIT 2
Yea it seems that you needed to .ToList(), which, tbh you should have been able to figure out. I had no way of knowing what of collection you had. Not sure either of us deserve a downvote for trying to help you when you don't supply an awful amount of information on the problem
I have the following LINQ query:
var q = from bal in data.balanceDetails
where bal.userName == userName && bal.AccountID == accountNumber
select new
{
date = bal.month + "/" + bal.year,
commission = bal.commission,
rebate = bal.rebateBeforeService,
income = bal.commission - bal.rebateBeforeService
};
I remember seeing a lambda shorthand for summing the commission field for each row of q.
What would be the best way of summing this? Aside from manually looping through the results?
It's easy - no need to loop within your code:
var totalCommission = q.Sum(result => result.commission);
Note that if you're going to use the results of q for various different calculations (which seems a reasonable assumption, as if you only wanted the total commission I doubt that you'd be selecting the other bits) you may want to materialize the query once so that it doesn't need to do all the filtering and projecting multiple times. One way of doing this would be to use:
var results = q.ToList();
That will create a List<T> for your anonymous type - you can still use the Sum code above on results here.
I need to add a literal value to a query. My attempt
var aa = new List<long>();
aa.Add(0);
var a = Products.Select(p => p.sku).Distinct().Union(aa);
a.ToList().Dump(); // LinqPad's way of showing the values
In the above example, I get an error:
"Local sequence cannot be used in LINQ to SQL implementation
of query operators except the Contains() operator."
If I am using Entity Framework 4 for example, what could I add to the Union statement to always include the "seed" ID?
I am trying to produce SQL code like the following:
select distinct ID
from product
union
select 0 as ID
So later I can join the list to itself so I can find all values where the next highest value is not present (finding the lowest available ID in the set).
Edit: Original Linq Query to find lowest available ID
var skuQuery = Context.Products
.Where(p => p.sku > skuSeedStart &&
p.sku < skuSeedEnd)
.Select(p => p.sku).Distinct();
var lowestSkuAvailableList =
(from p1 in skuQuery
from p2 in skuQuery.Where(a => a == p1 + 1).DefaultIfEmpty()
where p2 == 0 // zero is default for long where it would be null
select p1).ToList();
var Answer = (lowestSkuAvailableList.Count == 0
? skuSeedStart :
lowestSkuAvailableList.Min()) + 1;
This code creates two SKU sets offset by one, then selects the SKU where the next highest doesn't exist. Afterward, it selects the minimum of that (lowest SKU where next highest is available).
For this to work, the seed must be in the set joined together.
Your problem is that your query is being turned entirely into a LINQ-to-SQL query, when what you need is a LINQ-to-SQL query with local manipulation on top of it.
The solution is to tell the compiler that you want to use LINQ-to-Objects after processing the query (in other words, change the extension method resolution to look at IEnumerable<T>, not IQueryable<T>). The easiest way to do this is to tack AsEnumerable() onto the end of your query, like so:
var aa = new List<long>();
aa.Add(0);
var a = Products.Select(p => p.sku).Distinct().AsEnumerable().Union(aa);
a.ToList().Dump(); // LinqPad's way of showing the values
Up front: not answering exactly the question you asked, but solving your problem in a different way.
How about this:
var a = Products.Select(p => p.sku).Distinct().ToList();
a.Add(0);
a.Dump(); // LinqPad's way of showing the values
You should create database table for storing constant values and pass query from this table to Union operator.
For example, let's imagine table "Defaults" with fields "Name" and "Value" with only one record ("SKU", 0).
Then you can rewrite your expression like this:
var zero = context.Defaults.Where(_=>_.Name == "SKU").Select(_=>_.Value);
var result = context.Products.Select(p => p.sku).Distinct().Union(zero).ToList();