I count months and years from a given date to the present date
and from this list I have to subtract the months that were returned to me in the sql (linq) query.
I try to use "Except" on the results, but gives me an error in the picture below
var list = _ecpContext.Akceptacje_UnionAll_V
.Where(f => f.ADLogin == user)
.Select(f => new
{
Miesiac= f.Miesiac, //month
Rok= f.Rok // year
})
.ToList();
//-------------------------------------------------------------------
DateTime employmentDate = _ecpContext.Ustawienia.FirstOrDefault(x => x.UstLogin == user).EmploymentDate;
int employmentYear = employmentDate.Year;
int employmentMonth = employmentDate.Month;
DateTime now = DateTime.Now;
int currentYear = now.Year;
int currentMonth = now.Month;
var newList = Array.Empty<object>().Select(x => new { Month = 1, Year = 1 }).ToList();
for (var i = employmentYear; i <= currentYear; i++)
{
for (var x = employmentMonth; x <= currentMonth; x++)
{
newList.Add(new { Month = x, Year = i });
}
}
//-------------------------------------------------------------------
// i try
IEnumerable<DatesOfShortages> listMissingDates = list.Except(newList);
public class DatesOfShortages
{
public int Year { get; set; }
public int Month { get; set; }
}
new error
The Except method is a method which produces the set difference of two sequences so you need to invoke it.
IEnumerable<DatesOfShortages> listMissingDates = newList.Except(list);
You can't have one list A full of anonymous types, and another list B full of Tuples, and run a.Except(b) on them
Make a list of anonymous types instead of tuples:
var newList = Array.Empty<object>().Select(x => new { Month = 1, Year = 1 }).ToList();
for (var i = employmentYear; i <= currentYear; i++)
{
for (var x = employmentMonth; x <= currentMonth; x++)
{
newList.Add(new{ Month = x, Year = i});
}
}
For newList I suppose something like new [] { list.ElementAtOrDefault(-1) }.ToList(); would work too.. Whatever trick you feel like pulling to get a list of ATs!
Related
Payment Table Contains
PaymentID
MemberID
AmountPaid
Date
I want to query sum of amount for each month individually using linq in C#. Can you help me with querying this?
var result = (Your db context).tableName
.GroupBy(g => new { g.MonthColumn})
.Select(x => new
{
x.Key.MonthColumn,
amount = x.Sum(c => c.amount )
}).OrderBy(o => o.MonthColumn)
.ToList();
try this way it will help you to find your result
Good Question!
If your database has multiple year and multiple record in every month per member
var all = _context.PaymentTable.ToList();
var min = all.Min(f => f.Date).Year;
var max = all.Max(f => f.Date).Year;
List<PaidQueryViewModel> paidlist = new List<PaidQueryViewModel>();
for (int i = min; i < max; i++)
{
for (int m = 1; m < 11; m++)
{
PaidQueryViewModel paid = new PaidQueryViewModel();
var start = new DateTime(i, m, 1);
var end = start.AddMonths(1).AddMinutes(-1);
string Month = start.ToString("MMM-yyyy"); // if you want string month name
var amount = all.Where(f => f.Date >= start && f.Date <= end).Sum(f => f.AmountPaid);
paid.Month = start;
paid.Amount = amount;
paidlist.Add(paid);
}
}
public class PaidQueryViewModel
{
public DateTime Month { get; set; }
public decimal Amount { get; set; }
}
IEnumerable<IGrouping<long, MyClass>> datas = list.GroupBy(x => x.PropertyXYOfMyClass);
// get all items from each group
foreach (var grouping in datas)
{
long groupKey = groupingByMyClass.Key;
//iterating through values
foreach (var item in groupingByMyClass)
{
long key = item.PropertyIntOfClassA;
string property = item.PropertyA;
}
}
Each group contains some items, wow to get values from first item of each group?
UPDATE
void Extract()
{
List<DataHolder> data = new List<DataHolder>();
List<DateTime> randomTimes = new List<DateTime>();
Random r = new Random();
DateTime d = new DateTime(2019, 9, 19, 7, 0, 0);
for (int i = 0; i < 100; i++)
{
DataHolder dh = new DataHolder();
TimeSpan t = TimeSpan.FromSeconds(r.Next(0, 14400));
dh.OID = i;
dh.Value = r.Next(50);
dh.Snapshottime = d.Add(t);
data.Add(dh);
}
data.OrderBy(o => o.Snapshottime).ToList();
List<DataHolder> SortedList = data.OrderBy(o => o.Snapshottime).ToList();
TimeSpan interval = new TimeSpan(0, 15, 0);
var result = SortedList.GroupBy(x => x.Snapshottime.Ticks / interval.Ticks) .OrderBy(x => x.Key);
}
public class DataHolder
{
public int OID { get; set; }
public double Value { get; set; }
public DateTime Snapshottime { get; set; }
}
Here from result i need to take first item from each group.
try this:
var finalResult = result.Select(gpr=>grp.First());
or if you want the earliest/Latest/etc you could order by first:
var finalResult = result.Select(gpr=>grp.OrderBy(x=>x.SnapShotTime).First());
You've already done the heavy lifting. Make a simple loop over the result:
var result = SortedList.GroupBy(x => x.Snapshottime.Ticks / interval.Ticks) .OrderBy(x => x.Key);
var resultList = new List<DataHolder>();
foreach(var group in result)
{
resultList.Add(group.First());
}
I hope this helps.
My project is MVC5. I have a table with multiple rows for the same day, I need to get the total of this entry for each day, I use the following:
var days = db.Nutrition.Where(x => x.Day >= fromDate
&& x.Day <= toDate).DistinctBy(x => x.Day).AsEnumerable().ToList();
List<double?> calories = new List<double?>();
foreach (var item in days)
{
calories.Add(days.Where(c => c.Day==item.Day).Select(x => x.Calories).Sum());
}
I get a list containing the totals. Now I need to make a new list that has two columns.
I made the following model:
public class Consumption
{
public virtual double? Calories { get; set; }
public virtual string Name { get; set; }
}
I tried to use the following to generate the new list:
List<Consumption> newList = new List<Consumption>();
var name = new Consumption { Name = "name" };
foreach (var item in calories)
{
newList.Add(name, Calories = (double)item.Value);
}
I get the following error:
The name 'Calories' does not exist in the current context
Edit
Thanks to Stephen's comment:
I just used one line to achieve same result
var results = db.Nutrition.Where(x => x.Day >= fromDate && x.Day <= toDate).GroupBy(l => l.Day)
.Select(cl => new { Name = "name", Calories = cl.Sum(c => c.Calories)}).ToList();
Try with:
List<Consumption> newList = new List<Consumption>();
var name = new Consumption { Name = "name" };
foreach (var item in calories)
{
var cal = new Consumption{ Name = "name", Calories = (double)item.Value });
newList.Add(cal);
}
You received this compiler error
The name 'Calories' does not exist in the current context
because the List<Consumption>.Add(Comsumption item) method on your variable newList only accepts one argument of type Consumption.
Regarding your intentions, and the discussion in your comments with #StephenMuecke, it became clear that your intention is to Sum a property double Calories, and GroupBy by property DateTime Day and then project that into a List<Consumption>.
var dateTimeFormat = "yyyy-dd-MM";
var results = db.Nutrition.Where(x => x.Day >= fromDate && x.Day <= toDate)
.GroupBy(x => x.Day)
.Select(groupedX => new Consumption
{
Name = groupedX.Key.ToString(dateTimeFormat),
Calories = groupedX.Sum(y => y.Calories)
}).ToList();
I have a List of type DailySummary
public class DailySummary
{
public string AffiliateID { get; set; }
public string TotalCalls { get; set; }
public string Date{ get; set; }
}
with following sample data:
List<DailySummary> DealerTFNDatesTable = new List<DailySummary>();
DealerTFNDatesTable.Add(new DailySummary() { AffiliateID="0", Date = "12/12/2016", TotalCalls = "10"});
DealerTFNDatesTable.Add(new DailySummary() { AffiliateID="0", Date = "12/13/2016", TotalCalls = "74"});
DealerTFNDatesTable.Add(new DailySummary() { AffiliateID="1", Date = "12/22/2016", TotalCalls = "63"});
DealerTFNDatesTable.Add(new DailySummary() { AffiliateID="0", Date = "12/12/2016", TotalCalls = "58"});
Now I want to retrieve Date and TotalCalls grouped by AffiliateID and assign in another list.
for(int i =0; i < DealerTFNDatesTable.Count; i++)
{
List<NewList> newList = new List<NewList>();
newList.Date = //Assign Dintinct dates WHERE AffiliateId = 0
newList.AffiliateID = //AffiliateID=0
newList.TotalCalls= //TotalCalls SUM GROUPBY DATE and AffiliateID = 0
//For Date '12/12/2016' it will be 68, For '12/13/2016' it will be 74 and so on
}
I'm sorry, I'm new to LINQ. Can someone help me or share resources where I can get a hint to achieve this?
This should work for grouping by AffilateID and Date and then getting the sum (though it's weird to store a number as a string for something like this, but whatever floats your boat).
var results = DealerTFNDatesTable
.GroupBy(x => new { x.AffiliateID, x.Date })
.Select(x => new DailySummary {
AffiliateID = x.First().AffiliateID,
Date = x.First().Date,
TotalCalls = x.Sum(y => Convert.ToInt32(y.TotalCalls)).ToString()
});
If you now look at the result, for example with this code, you get exactly the values you wanted:
foreach (var x in results) {
Console.WriteLine($"id = {x.AffiliateID}, date = {x.Date}, totalCalls = {x.TotalCalls}");
}
> id = 0, date = 12/12/2016, totalCalls = 68
> id = 0, date = 12/13/2016, totalCalls = 74
> id = 1, date = 12/22/2016, totalCalls = 63
First off,
Since DealerTFNDatesTable is a variable, you should use camel case. Thus it is dealerTFNDatesTable
Then to complete #andy his answer, as you also want to do a select. You can select it as follows:
var newVariable = from item in dealerTFNDatesTable
group item by new
{
item.Date,
item.AffiliateID,
}
into g
select new
{
Date = g.Key.Date,
Id = g.Key.AffiliateID,
Total = g.Sum(a => a.TotalCalls)
};
This will give you an IEnumerable, of which you can put the relevant parts in a list by doing var otherList = new List<object>(newVariable
.Where(a => a.Total > 0)); or simply add .ToList() after the select if you want the collection as-is.
Note that this is simply another notation than LINQ, the result is the same.
var results = DealerTFNDatesTable.GroupBy(T => new { T.AffiliateID })
Link
I'm querying a datatable and I seem stuck on selecting a group of groups.
This code
var grouping = table.AsEnumerable()
.Where(x => curveids.Contains(x.Field<short>("CurveID")) && x.Field<DateTime>("Timestamp").Hour >= hour && x.Field<DateTime>("Timestamp").Hour < (hour + 1))
.GroupBy(x => x.Field<DateTime>("Timestamp")).Where(x => x.Select(y => y["CurveID"]).Count() == curveids.Count);
Groups by timestamp and returns a group of x curves, where x = curveid.Count(). It contains 5000ish groups.
However for each day there can be more than one timestamp.
int nrdays = grouping.GroupBy(z => z.Key.Date).Count();
tells me there are 255 distinct days.
I would now like to group this again, but not by time stamp but by calendar day and then take the first (as in earliest) group for each day. I tried this:
var grouping2 = grouping.GroupBy(z => z.Key.Date).OrderBy(a => a.Key).Take(curveids.Count);
but this only returns 4 groups and I dont get why?
It should return 255 groups with each of them containing the same timestamp and x curveids, so x*255 record sets.
The datatable has 3 columns, Timestamp (DateTime), CurveID(short), Price(double).
UPDATE
As requested by Mr Skeet a full example:
public class listprx
{
public DateTime timestamp;
public int curveID;
public double prx;
}
static void Main(string[] args)
{
var data = new List<listprx>();
// populating data
for (int i = 0; i < 50000; i++)
{
Random rand = new Random(i);
var tempdt = new DateTime(2016, rand.Next(1, 12), rand.Next(1, 29), rand.Next(1, 23), rand.Next(1, 59), 0);
if(i % 3 == 0)
{
data.Add(new listprx { timestamp = tempdt, curveID = 1, prx = rand.Next(1,50)});
data.Add(new listprx { timestamp = tempdt, curveID = 2, prx = rand.Next(1, 50) });
}
else if (i % 5 == 0)
{
data.Add(new listprx { timestamp = tempdt, curveID = 1, prx = rand.Next(1, 50) });
}
else
{
data.Add(new listprx { timestamp = tempdt, curveID = 1, prx = rand.Next(1, 50) });
data.Add(new listprx { timestamp = tempdt, curveID = 2, prx = rand.Next(1, 50) });
data.Add(new listprx { timestamp = tempdt, curveID = 3, prx = rand.Next(1, 50) });
}
}
// setting hour criteria
int hour = 16;
int nrcurves = 3;
// grouping by timestamp and only take those where all curves are there, (as close to the desired time as possible
var grouping = data.Where(x => x.timestamp.Hour >= hour && x.timestamp.Hour < (hour + 1))
.GroupBy(x => x.timestamp).Where(x => x.Select(y => y.curveID).Count() == nrcurves);
// Grouping by day and take only the time stamp that is closest to the hour
// this fails
var grouping2 = grouping.GroupBy(z => z.Key.Date).OrderBy(a => a.Key).Take(nrcurves);
Console.WriteLine("Nr of timestamps with all curves {0}, nr of days {1}, nr of groups in second group {2}, expected same as nr days"
, grouping.Count(), grouping.GroupBy(z => z.Key.Date).Count(), grouping2.Count());
Console.ReadLine();
}
UPDATE 2
I have removed the random element and simplified further:
public class listprx
{
public DateTime timestamp;
public int curveID;
}
static void Main(string[] args)
{
var data = new List<listprx>();
// populating data
var tempdt = new DateTime(2016, 4, 6, 16, 1, 0);
for (int i = 0; i < 4; i++)
{
if (i == 2)
{
tempdt = tempdt.AddDays(1);
}
if(i % 2 == 0 )
{
data.Add(new listprx { timestamp = tempdt, curveID = 1});
}
else
{
data.Add(new listprx { timestamp = tempdt, curveID = 1});
data.Add(new listprx { timestamp = tempdt, curveID = 2});
}
tempdt = tempdt.AddMinutes(i+1);
}
// setting hour criteria
int hour = 16;
int nrcurves = 2;
//grouping by timestamp and only take those where all curves are there, (as close to the desired time as possible
var grouping = data.Where(x => x.timestamp.Hour >= hour && x.timestamp.Hour < (hour + 1))
.GroupBy(x => x.timestamp).Where(x => x.Select(y => y.curveID).Count() == nrcurves);
//Grouping by day and take only the time stamp that is closest to the hour
//this fails
var grouping2 = grouping.GroupBy(z => z.Key.Date).OrderBy(a => a.Key).Take(nrcurves);
Console.WriteLine("Nr of timestamps with all curves {0}, nr of days {1}, nr of groups in second group {2}, expected same as nr days"
, grouping.Count(), grouping.GroupBy(z => z.Key.Date).Count(), grouping2.Count());
Console.ReadLine();
}
The expected end result is:
Timestamp CurveID
------------------------
6/4/16 16:02 1
6/4/16 16:02 2
7/4/16 16:06 1
7/4/16 16:06 2
Edited answer working on your example.
Ok, I went trought your example and fixed some bugs and my answer. Let's clear code a bit and comment what went wrong where.
Our models will be
public class Curve
{
public int CurveID { get; set; }
public DateTime Timestamp { get; set; }
}
public class CurveGroup
{
public DateTime Timestamp { get; set; }
public IEnumerable<Curve> Curves { get; set; }
}
next is function to generate test data:
public static List<Curve> GetData()
{
var data = new List<Curve>();
var startTime = new DateTime(2016, 4, 6, 16, 1, 0);
for (int i = 0; i < 4; i++)
{
if (i == 2)
{
//startTime.AddDays(1); - this line does nothing, DateTime is an immutable struct so all function changing its value returns a new copy
startTime = startTime.AddDays(1);
}
if (i % 2 == 0)
{
data.Add(CreateNewCurve(startTime, 1));
}
else
{
data.Add(CreateNewCurve(startTime, 1));
data.Add(CreateNewCurve(startTime, 2));
}
//startTime.AddMinutes(i + 1); same issue as above
startTime = startTime.AddMinutes(i + 1);
}
return data;
}
public static Curve CreateNewCurve(DateTime time, int curveID)
{
return new Curve()
{
Timestamp = time,
CurveID = curveID
};
}
and here goes main function
static void Main(string[] args)
{
var data = GetData();
int hour = 16;
int totalCurveCount = 2;
var grouping = data
.Where(x => x.Timestamp.Hour >= hour && x.Timestamp.Hour < (hour + 1))
.GroupBy(x => x.Timestamp)
.Where(x => x.Count() == totalCurveCount); //there is no need to select curveId like in your code: Where(x => x.Select(y => y.curveID).Count() == nrcurves);
var grouping2 = grouping
.GroupBy(x => x.Key.Date)
.Select(x =>
new CurveGroup
{
Timestamp = x.Key,
Curves = x.OrderBy(c => c.Key).Take(totalCurveCount).SelectMany(c => c)
}
);
foreach (var g in grouping2)
{
foreach (var c in g.Curves)
{
Console.WriteLine(c.Timestamp);
Console.WriteLine(c.CurveID);
}
}
}
this returns expected results.
Your code failed because your second grouping is not taking (Take(nrcurves)) values in groups but groups themselves. So instead of returning 255 groups with 2 values in each you return 2 groups with all values in them.
Hope this fixes your issue.