C# LINQ query to use previous result if empty - c#

I am having a datatable that is populated from an Access DB. The result looks like
Month | Sum
--------------
1 | 1464
2 | 1716
3 | 2125
4 | 2271
5 | 2451
6 | 2583
7 | 2671
9 | 2823
10 | 2975
You are right - nothing for august!
What I want is, that for august the same value as for july is used.
Currently I am using this LINQ query to add the data to a linechart:
for (int i = 1; i <= System.DateTime.Now.Month; i++)
{
var numbers = (from p in dTable.AsEnumerable()
where p.Field<int>("M") >= i
select p).First();
series2.Points.Add(new DataPoint { AxisLabel = i.ToString(), YValues = new double[] { Convert.ToDouble(numbers["Sum"]) } });
}
The chart is shown, but for august the september value is used. I assume it is something very basic that I am doing wrong but I simply canĀ“t figure it out.
Thanks in advance!

You are requesting all the months greater than the current month.
where p.Field<int>("M") >= i
So for August (8), your are retrieving September and greater (9, 10, 11, 12), not July (7).
You have to invert your constraint, and order by descending month:
var numbers = (from p in dTable.AsEnumerable()
where p.Field<int>("M") <= i
select p)
.OrderByDesc(p => p.Month)
.First();

You have to invert your logic:
var numbers = (from p in dTable.AsEnumerable()
where p.Field<int>("M") <= i
select p).Last();
It goes without saying that this doesn't work when there is no previous month.
UPDATE:
the above asumes that the table you are reading from is ordered. If that is not the case, you have to order yourself (as mentioned by Cyril Gandon):
var numbers = (from p in dTable.AsEnumerable()
where p.Field<int>("M") <= i
orderby p.Field<int>("M") descending
select p).First();

Related

SQL to LINQ - Getting Wrong Results

If a user selects 07-07-2016, I want to count how many times billdays > 30 for that billdate month, for the past 12 months.
Query is this:
select COUNT(*) as 'BillsOver30' from pssuite_web.pujhaccd
where billdays>30 and DATEDIFF(month,'07-07-2016', GETDATE()) <= 13
Group By Month(billdate)
Result is this:
1784 (July)
1509 (June)
2986 (May)
2196 (etc)
5853
3994
1753
1954
869
1932
629
1673
LINQ query is:
DateStart = '07-07-2016' (from a textbox on view)
DateTime earliestDate = objdate1.DateStart.Value.AddMonths(-13);
var custQuery9 = (from m in DataContext.pujhaccds
where m.billdays > 30 &&
m.billdate >= earliestDate &&
m.billdate <= objdate1.DateStart
group m by m.billdate.Value.Month into p
select new Date1()
{
theMonth = p.Key,
theCount = p.Count()
});
Results are:
Month Count
1 1029
2 1018
3 1972
4 1519
5 2657
6 2019
7 1206
8 1023
9 761
10 1620
11 354
12 931
You can see that the LINQ query is way off.
Doesn't matter what date I put in, the result always stays the same. Rather than 12 months from starting date.
I must be writing it wrong, thanks.

How to select hour in priority

I'm using C# and SQL Server 2008 R2.
I have hour data like this:
Client_ID TheHour HourMin HourMAx
53026 09:00 7 9
53026 12:00 10 12
53026 15:00 13 15
53026 18:00 16 19
I will put TheHour into my combobox, that depends on computer hour.
When the computer hour is 10:00 then the value of my combobox:
12:00
15:00
18:00
09:00
My linq is :
int The_Hour = DateTime.Now.Hour;
var query = from o in oEntite_T.LS_CLIENTHORRAIRE
where o.CLIENT_ID == CLIENT_ID &&
The_Hour >= o.HEURE_MIN && The_Hour <= o.HEURE_MAX
select o;
LesListe = query.ToList();
It return only 1 value, which is 12:00.
That meant my combobox will select hour that depend on computer hour, but it leave the possibility for the user to select another hour.
Here is a working example in LINQPad, I also added a flag to indicate selected value.:
void Main()
{
var LS_CLIENTHORRAIREs = new List<LS_CLIENTHORRAIRE>
{
new LS_CLIENTHORRAIRE{TheHour="09:00",HourMin=7,HourMAx=9},
new LS_CLIENTHORRAIRE{TheHour="12:00",HourMin=10,HourMAx=12},
new LS_CLIENTHORRAIRE{TheHour="15:00",HourMin=13,HourMAx=15},
new LS_CLIENTHORRAIRE{TheHour="18:00",HourMin=16,HourMAx=19}
};
LS_CLIENTHORRAIREs.Dump();
int The_Hour = 10;
var query = from o in LS_CLIENTHORRAIREs
select new {o.TheHour,o.HourMin,o.HourMAx,selected = The_Hour >= o.HourMin && The_Hour <= o.HourMAx}
;
query.OrderBy (q => q.HourMAx>The_Hour ? 0 : 1).ThenBy (q => q.HourMAx).Dump();
}
public class LS_CLIENTHORRAIRE
{
public string TheHour{get;set;}
public int HourMin{get;set;}
public int HourMAx{get;set;}
}
Results
So, basically, you don't want to filter the list, you want to sort it, right?
First, you want to sort it by
whether the MaxHour is in the past
and then by
TheHour itself.
Example: Let's assume it is 10:00:
Client_ID TheHour HourMin HourMax MaxHourIsInThePast
53026 09:00 7 9 x
53026 12:00 10 12
53026 15:00 13 15
53026 18:00 16 19
Now you return a list that orders first by MaxHourIsInThePast (first those without x and then those with x) and then by TheHour.
Implementation is left as an exercise. Hint: You will need the LINQ order by keyword, and an expression similar to (o.HourMax < currentHour ? 2 : 1) will come in handy.
Let assume the computer hour is 10.
Then: your condition would be: HourMin <= 10 && HoureMax >= 10.
The first condition would return row 1 and 2. The second one would return row 2. The intersect will return only the second row, which is 12:00.
If you want to get all rows, just remove these conditions.

Put value in datatable group by month

I have this datatable in c#
Date Employee Job1 Job2 Job3
1/1/2012 a 1 1 1
1/1/2012 b 2 2 2
1/1/2012 c 2 1 4
1/1/2012 d 4 2 1
1/2/2012 a 3 2 5
1/2/2012 b 2 2 2
1/2/2012 c 3 3 3
1/2/2012 d 1 1 1
1/3/2012 a 5 5 5
1/3/2012 b 2 2 6
1/3/2012 c 1 1 1
1/3/2012 d 2 3 4
2/1/2012 a 2 2.5 2
2/1/2012 b 5 5 2
2/1/2012 c 2 2 2
2/2/2012 a 3 3 3
2/2/2012 b 2 3 3
3/1/2012 a 4 4 11
3/5/2012 a 14 42.5 15
3/6/2012 a 21 12.143 22
3/8/2012 a 8.9 45 27
3/8/2012 b 4.4 25 31
I want to loop through the values monthwise such that i can store the value in a datatable and will do the other calculation with that. Here with this example it will have three datatable, first with january values, another one with feb and last one with march rows. How can this be done by Linq. Please suggest the Linq syntax which will group the results month wise.
var monthEmpGroups = dt.AsEnumerable()
.Select(r => new
{
Year = DateTime.Parse(r.Field<string>("DATE")).Year,
Month = DateTime.Parse(r.Field<string>("DATE")).Month.ToString()
})
.GroupBy(x => x.Month);
var dtf = CultureInfo.CurrentCulture.DateTimeFormat;
foreach (var empGroup in monthEmpGroups)
{
int month = int.Parse(empGroup.Key);
string colName = dtf.GetMonthName(month);
// Here i want to get all the rows where month is colName (i.e.january, feb, march)
}
Please suggest if there is other way around to get the values month wise using LINQ.
You should move getting items for every group into monthEmpGroups query:
var monthEmpGroups = (from r in dt.AsEnumerable()
let month = DateTime.Parse(r.Field<string>("DATE")).Month
group r by month into g
select new { Month = g.Key, Items = g.ToList() });
With that you can easily get desired results:
var dtf = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat;
foreach (var empGroup in monthEmpGroups)
{
int month = empGroup.Month;
string colName = dtf.GetMonthName(month);
List<DataRow> items = empGroup.Items;
}

Complex LINQ to SQL Query with dates

I'm building a query with LINQ to SQL in my C# project, but I have some problems with it...
What I want to do, is select the 4 lasts days that are like today (For example, a Friday), so if we're on Friday 28, I want to query for: Friday 21, 14, 7... The last four Fridays but NOT today.
This is easy, I've done that but here's the complex part, I want to not query the exceptions I set, for example End of month, which are from 28th to 1st day of each month, so let's say i want to query this (october, fridays):
Today is Friday 26, I want to query:
19, 12, 5 and September 28th (the fourth friday from now), but as I said, 28th is end of month, so i need to return September 21th which is the last friday and it is not end of month... I have the same issues with holidays, but I think if I can handle end of months, I can do with them...
i hope I've explained good for you to understand what I want... Here's my query, which is working but can't handle exceptions. (the field b.day is the Id for each days, 8 means end of month, and 7 holiday)
var values =
from b in dc.MyTable
where // This means end of month
b.day != 8
// This triggers to query last 4 days
&& b.date == Convert.ToDateTime(last.ToString("dd/MM/yyy")).AddDays(-28)
|| b.date == Convert.ToDateTime(last.ToString("dd/MM/yyy")).AddDays(-21)
|| b.date == Convert.ToDateTime(last.ToString("dd/MM/yyy")).AddDays(-14)
|| b.date == Convert.ToDateTime(last.ToString("dd/MM/yyy")).AddDays(-7)
orderby b.id descending
group b.valor by b.hora_id into hg
orderby hg.Key descending
select new
{
Key = hg.Key,
Max avg = System.Convert.ToInt32(hg.Average() + ((hg.Average() * intOkMas) / 100)),
Min avg = System.Convert.ToInt32(hg.Average() - ((hg.Average() * intOkMenos) / 100))
};
You should prepare the list of days you'd like retrieve before trying to query:
// Get the last four days excluding today on the same weekday
var days = Enumerable.Range(1, 4).Select(i => DateTime.Today.AddDays(i * -7));
Then remove any days you don't want:
// Remove those pesky end-of-month days
days = days.Where(d => d.Day < 28 && d.Day > 1);
When you're done preparing the list of days you want to retrieve, only then should you perform your query:
from b in dc.MyTable
where days.Contains(b.date) // Translated to SQL: date IN (...)
...
EDIT: As you mentioned in your comment, you want a total of four days even after any filtering you perform. So simply generate more days and take the first four:
var days = Enumerable.Range(1, int.MaxValue - 1)
.Select(i => DateTime.Today.AddDays(i * -7))
.Where(d => d.Day < 28 && d.Day > 1)
.Take(4);
Due to the way LINQ (and in general, enumerators) work, only four days plus any skipped days will be calculated.
Building on Allon Guralnek's answer, I'd modify it slightly:
First, build an infinite date generator:
public IEnumerable<DateTime> GetDaysLikeMe(DateTime currentDate)
{
DateTime temp = currentDate;
while(true)
{
temp = temp.AddDays(-7);
yield return temp;
}
}
Then you can use deferred execution to your advantage by limiting to only dates that meet your additional criteria:
GetDaysLikeMe(DateTime.Now).Where(dt => /* dt meets my criteria */).Take(4)
Then you can use this list that was generated to query in your LINQ to SQL like Allon Guralnek suggested above:
from b in dc.MyTable
where days.Contains(b.date) // Translated to SQL: date IN (...)
...
This has the benefit of you being able to specify additional predicates for what are acceptable dates and still get at least 4 dates back. Just be sure to put some bounds checking on the infinite date generator in case one of your predicates always returns false for whatever reason (which means the generator will never exit).
IE: while(temp > currentDate.AddYears(-1))
I would highly suggest writing your exception code (last friday of the month) after retrieving your rows, as this logic seems to be too complicated for a LINQ statement. Instead of retrieving the last 4 days, retrieve the last 5. Remove any that are the last Friday of each respective months. If you still have 5 rows, remove the last one.
Update
var values1 =
from b in dc.MyTable
where // This means end of month
b.day != 8
// This triggers to query last 4 days
&& b.date == Convert.ToDateTime(last.ToString("dd/MM/yyy")).AddDays(-28)
|| b.date == Convert.ToDateTime(last.ToString("dd/MM/yyy")).AddDays(-21)
|| b.date == Convert.ToDateTime(last.ToString("dd/MM/yyy")).AddDays(-14)
|| b.date == Convert.ToDateTime(last.ToString("dd/MM/yyy")).AddDays(-7)
orderby b.id descending
select b;
//Do stuff with values
var values2 = from b in values2
group b.valor by b.hora_id into hg
orderby hg.Key descending
select new
{
Key = hg.Key,
Max avg = System.Convert.ToInt32(hg.Average() + ((hg.Average() * intOkMas) / 100)),
Min avg = System.Convert.ToInt32(hg.Average() - ((hg.Average() * intOkMenos) / 100))
};

Group by Week of year (Week number) in Linq to SQL

In regular SQL i could do something like
SELECT * From T GROUP BY DATEPART(wk, T.Date)
How can i do that in Linq to SQL ?
The following don't work
From F In DB.T Group R By DatePart(DateInterval.WeekOfYear, F.Date)
Also don't work:
From F In DB.T Group R By (F.Date.DayOfYear / 7)
LINQ to SQL does not support the Calendar.WeekOfYear method, but you could potentially create a TSQL function that wraps the call to DatePart. The DayOfYear / 7 trick should work for most cases and is much easier to use. Here's the code I ended up with:
var x = from F in DB.T
group F by new {Year = F.Date.Year, Week = Math.Floor((decimal)F.Date.DayOfYear / 7)} into FGroup
orderby FGroup.Key.Year, FGroup.Key.Week
select new {
Year = FGroup.Key.Year,
Week = FGroup.Key.Week,
Count = FGroup.Count()
};
Results in something like this:
Year Week Count
2004 46 3
2004 47 3
2004 48 3
2004 49 3
2004 50 2
2005 0 1
2005 1 8
2005 2 3
2005 3 1
2005 12 2
2005 13 2
You can use the SqlFunctions.DatePart method from the System.Data.Entity.SqlServer namespace.
// Return the week number
From F In DB.T Group R By SqlFunctions.DatePart("week", F.Date)
Range variable name can be inferred only from a simple or qualified name with no arguments
This works correctly.
from F in DB.T group F by F.Date.DayOfYear / 7;
You were specifying the group by incorrectly. The result of this code be a collection of objects. Each object will have a Key property which will be what you grouped by (in this case the result of F.Date.DayOfYear / 7. Each object will be a collection of objects from T that met the group condition.
If you are concerned about the culture you are in the following code will take that into account:
var ci = CultureInfo.CurrentCulture;
var cal = ci.Calendar;
var rule = ci.DateTimeFormat.CalendarWeekRule;
var firstDayOfWeek = ci.DateTimeFormat.FirstDayOfWeek;
var groups = from F in DB.T
group F by cal.GetWeekOfYear(F, rule, firstDayOfWeek) into R
select R;
First you should get the date of the first day in the week.
To get the date of the first day in the week.
you can use this code:
public static class DateTimeExtensions
{
public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek)
{
int diff = dt.DayOfWeek - startOfWeek;
if (diff < 0)
{
diff += 7;
}
return dt.AddDays(-1 * diff).Date;
}
}
Then you can group by the first date of the week.
So this code in regular SQL :
SELECT * From T GROUP BY DATEPART(wk, T.Date)
can be done in Linq to SQL like this
T.GroupBy(i => i.Date.StartOfWeek(DayOfWeek.Monday));

Categories

Resources