Aggregate and create results list using EF - c#

I'm having a list of budget units each one containing the following properties:
DateTime Month,
int IdCurrency,
decimal Planned,
int sign, //denotes whether we have income (1) or cost (0)
etc...
Based on given year, I'd like to return a list of objects of the following structure:
public class BudgetBalances
{
public DateTime Month { get; set; }
public int IdCurrency { get; set; }
public decimal Incomes { get; set; }
public decimal Costs { get; set; }
public decimal Balance { get; set; }
}
The first part is easy - I'm getting all budget units for given day from the database, but now I do not know how to make an EF query to:
Get all incomes (sign==1) in currencies within one month, sum them and store it Incomes property
Get all costs (sign==0) and do the same as above
Substract Cost from Income and store it under Balance property
As the result I will have
Jan2022, USD, 3000, 1000, 2000
Jan2022, EUR, 5000, 2000, 3000
etc..
I can always make three level foreach structure, but that is not an effective way to do so. Could you please give me hint how to do it proper way?
That is what I got so far:
public List<BudgetBalances>GetYearlyBudget(int IdOwner, int year)
{
var budgets = _context.Budgets
.Where(_ => _.Month.Year == year && _.IdOwner == IdOwner);
List<BudgetBalances> list = budgets.GroupBy(a => a.Month)
.Select(ls => new BudgetBalances
{
Incomes = ls.Where(_ => _.IsIncome == 1).Sum(_ => _.Planned),
Costs = ls.Where(_ => _.IsIncome == 0).Sum(_ => _.Planned)
}).ToList();
return list;
}
And it calculates each month budget taking into account incomes and costs, but it does not take currencies into consideration. Also I do not know how should I obtain balance value.
Balance = Income - Costs
does not work

Reference this
code sample
using (var context = new MyContext())
{
var result = context.BudgetBalances
.Where(b => b.IdCurrency == 1);
}

Thanks, finally I got what I wanted, here's my code:
public List<BudgetBalances>GetYearlyBudget(int IdOwner, int year)
{
var budgets = _context.Budgets
.Where(_ => _.Month.Year == year && _.IdOwner == IdOwner);
List<BudgetBalances> list = budgets.GroupBy(a => new { a.Month, a.IdCurrency})
.Select(ls => new BudgetBalances
{
IdCurrency = ls.Key.IdCurrency,
CurrencySymbol = _context.Currencies.Where(_=>_.IdCurrency==ls.Key.IdCurrency).FirstOrDefault().CurrencySymbol,
Month = ls.Key.Month,
Incomes = ls.Where(_ => _.IsIncome == 1).Sum(_ => _.Planned),
Costs = ls.Where(_ => _.IsIncome == 0).Sum(_ => _.Planned),
})
.OrderBy(_=>_.Month)
.ToList();
foreach(BudgetBalances ls in list)
{
ls.Balance = ls.Incomes - ls.Costs;
ls.month = ls.Month.ToString("MM/yyyy");
}
return list;
}

Related

Find if list is sequential and with timestamp to today

Using Entity Framework Core 6 I have the entity:
public class Node {
public String Parameter { get; set; }
public String Period { get; set; }
public Decimal Value { get; set; }
public DateTimeOffset Timestamp { get; set; }
}
The Period value can be:
M1 = 1 Minute
H1 = 1 Hour
D1 = 1 Day
For each pair (Parameter, Period) I need to find if I have the most recent 200 values.
For example, I need to find if I have the temperature for the previous 200 days:
Parameter = Temperature
Period = D1
I need to check all the pairs that don't satisfy this condition. I started with:
var stored = _context.Nodes
.GroupBy(x => new { x.Parameter, x.Period })
.Select(x => new { 
Parameter = x.Key.Parameter,
Period = x.Key.Period,
Count = x.Count()
});
With this I have the count for each pair.
But I am not sure if they are sequential and reaching today.

Calculating price ranges with LINQ and corresponding sale numbers

to start things off I have a class which basically represents all the transactions for specific items, it's usually 100 items for which I have transactions. The class looks as following:
public class RawTransactions
{
public string SellerName { get; set; }
public int FeedBackScore { get; set; }
public int QuantityPurchased { get; set; }
public DateTime TransactionDate { get; set; }
public string ProductTitle { get; set; }
public double CurrentPrice { get; set; }
public double SalePrice { get; set; }
public string ItemID { get; set; }
public string Date { get; set; }
}
Based on this class and the transactions inside a list of this type I've created a function which basically takes smallest and largest sale price of any transaction that is present in the list and creates a 7 stage price range
private List<double> GetRangeForElements(double minPrice, double maxPrice)
{
double step = (maxPrice - minPrice) / 7.00d;
return Enumerable.Range(0, 8).Select(i => minPrice + i * step).ToList();
}
So for example if I pass $0 (smallest sale price) and $10 (largest sale price) it will create a list of 7 price range like following:
0
1.5
3
4.5
6
7.5
9
10
This can be interpreted as:
0 - 1.5 price range
1.5 - 3 price range
3 - 4.5 price range
4.5 - 6 price range
6 - 7.5 price range
// and so on...
The usage is as following:
var ranges = GetRangeForElements(0,10); // Here I will have the ranges now
Now based on these ranges that were just created and the existing transactions that I have I need to determine following parameters:
Price range
How many sales specific ItemID has sales for a specific range
How many sellers (based on SellerName property) had sales for a specific price range
How many sellers (again based on SellerName propert) DIDN'T had sales for a specific price range
I'm not really sure how can I now combine all this data to get these parameters using LINQ? Can someone help me out with this?
P.S. guys the transactions of all items are stored in a List like following:
var allItemsTransactions = new List<ProductResearchRawTransactions>();
P.S. guys this is the existing solution that I have, but it's giving me completely wrong results:
var priceRanges = ranges.Select(r => new PriceRangeGraph
{
Price = Math.Round(r, 2),
Sales = allItemsTransactions.Where(x => ranges.FirstOrDefault(y => y >= x.SalePrice) == r).Sum(x => x.QuantityPurchased),
SuccessfulSellers = allItemsTransactions.Where(x => ranges.FirstOrDefault(y => y >= x.SalePrice) == r).GroupBy(x => new { x.SellerName, x.QuantityPurchased }).Where(x => x.Key.QuantityPurchased > 0).Select(x => x.Key.SellerName).Count(),
UnSuccessfulSellers = allItemsTransactions.Where(x => ranges.FirstOrDefault(y => y >= x.SalePrice) == r).GroupBy(x => new { x.SellerName, x.QuantityPurchased }).Where(x => x.Key.QuantityPurchased == 0).Select(x => x.Key.SellerName).Count(),
}).ToList();

Splitting list of objects by DateTime property

I'm building a booking system for cinemas(of course it's just small project for studies).
Here is my Showcase model:
public class ShowcaseModel
{
public string objectId { get; set; }
public string MovieId { get; set; }
public int Auditorium { get; set; }
public DateTime? StartDate { get; set; }
}
I want to display schedule in "per day" form. To achieve this i get all Showcases where DateTime is greater than today and put them into
List< ShowcaseModel >.
Now i don't know how to split this list(into separate lists) by day using StartDate property.
Is there any way to achieve this?
Thanks in advance.
You can use GroupBy method:
List<ShowcaseModel> list = new List<ShowcaseModel>();
//...
var gooupByDay = list.GroupBy(o=>o.StartDate.Value.Date);
I have used fixture (simply creates random instances of your class) to demonstrate how you can get a list of items per date.
var fixture = new Fixture();
IEnumerable<ShowcaseModel> showCaseModel = fixture.CreateMany<ShowcaseModel>();
IEnumerable<ShowcaseModel> futureShowCases = showCaseModel.Where(s => s.StartDate != null && s.StartDate > DateTime.Now);
// we know none of start dates are null
var groupedShowCases = futureShowCases.GroupBy(s => s.StartDate.Value.Date);
List<Tuple<DateTime, IEnumerable<ShowcaseModel>>> showCasesByDate = new List<Tuple<DateTime, IEnumerable<ShowcaseModel>>>();
foreach (var groupedShowCase in groupedShowCases)
{
var key = groupedShowCase.Key;
showCasesByDate.Add(Tuple.Create(key, groupedShowCase.ToList().AsEnumerable()));
}
Using Linq GroupBy()
var grouped = list.Where(f=>f.StartDate!= null)
.GroupBy(f => f.StartDate.Value.Date, b => b,
(k, g) => new { Date= k.Date,
Movies= g }).ToList();
Assuming list is a collection of your ShowCaseModel

How can I sum the values of a member in a generic list based on the value of another member?

I've got this generic list:
private List<ItemsForMonthYear> _itemsForMonthYearList;
...which stores instantiations of this class:
public class ItemsForMonthYear
{
public String ItemDescription { get; set; }
public String monthYr { get; set; }
public int TotalPackages { get; set; }
public Decimal TotalPurchases { get; set; }
public Decimal AveragePrice { get; set; }
public Double PercentOfTotal { get; set; }
}
I need to calculate the totals for all the TotalPackages in the list for each month (separately). I've got a start with this:
private int GetGrandTotalPackagesForMonth(String YYYYMM)
{
return _itemsForMonthYearList.Sum(x => x.TotalPackages);
}
This, though, gets the Grand TotalPackages for all months; I just want (one at a time) the Grand Totals for a given month. I was hoping I could use "Where" something like this:
return _itemsForMonthYearList.Sum(x => x.TotalPackages).Where(x => x.monthYr == YYYYMM);
...but that is not available.
How can I restrict a summation to the specified month?
You should apply the filter first and then sum TotalPackages like:
return _itemsForMonthYearList.Where(x => x.monthYr == YYYYMM).Sum(x => x.TotalPackages);
Although not related, but if you want you can get the sum for each month in a single query using GroupBy like:
var sumForeachMonth = _itemsForMonthYearList.GroupBy(x => x.monthYr)
.Select(grp => new
{
Month = grp.Key,
Sum = grp.Sum(r => r.TotalPackages)
});
You have Where clause available for such operations
myList.Where(x => x. monthYr == "Nov-15").Sum(x => x.TotalPackages);

Linq Grouping and averages

I have two objects one is a car object and the other object I use to log how many car objects are on shift and record what properties these cars have.
public class Car
{
public int Id { get; set; }
public bool OnShift { get; set; }
public bool HasExtraBaggageSpace { get; set; }
}
public class Log
{
public DateTime TimeStamp { get; set; }
public int CarId { get; set; }
public bool HasExtraBaggageSpace { get; set; }
}
Every five minutes the app selects all the cars on shift and writes the information to a log object and inserts them into a List Logs.
After three weeks of logging I would now like to return a number which reflects the average of the last three weeks . Example:
How many cars with HasExtraBaggageSpace can I expect on a thursday at 14:00.
public class myApp
{
public class AverageReturnArgs
{
public int Hour { get; set; }
public int Minute { get; set; }
public int Count { get; set; }
}
public AverageReturnArgs GetAverage(List<Log> logs, DateTime TimeReq)
{
int hour = TimeReq.Hour;
int min = TimeReq.Minute;
var average = logs.GroupBy(grpByHourMin => new
{
hour = grpByHourMin.TimeStamp.Hour,
min = grpByHourMin.TimeStamp.Minute
}).Select(av => new AverageReturnArgs()
{
Hour = av.Key.hour,
Minute = av.Key.min,
Count = av.Average(x => x.HasExtraBaggageSpace)
});
}
}
This is producing a compiler error.
Count = av.Average(x => x.HasExtraBaggageSpace)
Any ideas how I could accomplish this?
How would you calculate the average of boolean values ?
I think the Count aggregate should be what you are looking for:
Count = av.Count(x => x.HasExtraBaggageSpace)
EDIT If you mean to calculate the percentage of cars having ExtraBaggageSpace you may try something like this :
Count = av.Average(x => x.HasExtraBaggageSpace ? 1 : 0)
With use of the ternary operator this expression convert your boolean value to an integer and calculate the average (that will be a Double).
EDIT 2
Here is what your line should look like.
Count should be made of type Double.
Count = av.Average(x => av.Count(y=>y.HasExtraBaggageSpace))
EDIT 3
Ok the logic was all wrong :
public AverageReturnArgs GetAverage(List<Log> logs, DateTime TimeReq)
{
int hour = TimeReq.Hour;
int min = TimeReq.Minute;
var average = logs
.Where(log => log.TimeStamp.Hour == hour && log.TimeStamp.Minute == min)
.GroupBy(grp => grp.TimeStamp)
.Select(av => new AverageReturnArgs()
{
Hour = hour,
Minute = min,
Count = av.Average(x => av.Count(y=>y.HasExtraBaggageSpace))
});
}

Categories