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();
Related
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.
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;
}
My RowMultiplevaluw table is
public class RowMultipleValues
{
public int ID { get; set; }
public String Year{ get; set; }
public string country { get; set; }
public decial Admin { get; set; }
public decimal Finance { get; set; }
public virtual ICollection<UsedAmount> UsedAmount { get; set; }
}
My used amount table is
public class UsedAmount
{
public int ID { get; set; }
public string Year{ get; set; }
public string country { get; set; }
public decial UsedAmount { get; set; }
public int RowMultipleValues ID { get; set; }
Public virtual RowMultibleValue RowMultibleValue { get; set; }
}
My query is
var query = from mtv in context.multiplerowvaluetable
join usd in dbcontext.usedtsble on mtv.year equal usd.year group g by mtv.country into g
select new { country =g.key,sumadmincolumn =g.sum(Admin),sumfinancecolumn = g.sum(finance) }).tolist();
Result which I want is
ID Year Country Admin. UsedAdmin Finance UsedFinance
1. 2017 USA 100 50 200 300
2. 2017 China 300 300 500 400
Total. 400 350 700 700
Please help me my model design and query for result.Thank.
So you want to join every MultipleValue with the UsedAmount on equal year value. Then group the result into groups of joined items with same country. Finally from every group create one object with the country, the sum of all Admin values and the sum of all finance values.
// first join the two collections on same year.
// we only need properties Country, Admin, Finance:
var result = myDbContext.MultipleRowValueTable.Join(myDbContext.UsedAmountTable,
multipleRow => multipleRow.Year, // from every multipleRow take the year
usedAmount => usedAmount.Year, // from every usedAmount take the year
(multipleRow, usedAmount) => new // when they match make a new object
{
Country = multipleRow.Country,
Admin = multipleRow.Admin,
UsedAdmin = usedAmount.Admin,
Finance = multipleRow.Finance,
UsedFinance = usedAmount.Finance,
})
// group the elements from this join table into groups with same Country
.GroupBy(joinedItem => joinedItem.Country, // all items in the group have this Country
joinedItem => new // the elements of the group
{
Admin = joinedItem.Admin,
UsedAdmin = joinedItem.UsedAdmin,
Finance = joinedItem.Finance,
UsedFinance = joinedItem.UsedFinance,
})
// finally: from every group take the Key (which is the Country)
// and the sum of the Admins and Finances in the group
.Select(group => new
{
Country = group.Key,
SumAdminColumn = group
.Select(groupElement => groupElement.Admin)
.Sum(),
... // others are similar
});
// from every group take the elements and sum the properties
.Select(group => new
{
Id = multipleRowValue.Id,
Year = multipleRowValue.Year,
Country = multipleRowValue.Country,
}
In my Entity Framework application, I have an Entity called Invoice.cs, it has various properties, but here are the ones we're concerned about for the question:
public class Invoice : IEntity
{
public int Id { get; set; }
public decimal Amount { get; set; }
public DateTime Date { get; set; }
public int OrderId { get; set; }
public virtual ICollection<Payment> Payments { get; set; }
}
I am attempting to query the database to get a list of all outstanding invoices. An outstanding invoice is the following:
If the total of the payments made against the Invoice are less than the Invoice Amount, then the invoice is Outstanding.
I'm stuck on working out if an invoice is outstanding or not in my LINQ query, which currently looks like this:
var outstandingInvoices = from inv in _context.Invoices
where !inv.IsDeleted && inv.Date >= startDate && inv.Date <= endDate
select inv;
startDate and endDate are parameters passed in to filter the result.
Another complexity with this is to do with how payments are made. The payments can be made in RMB (Chinese currency) or GBP (UK currency), and in the report I'm generating, I want all to be displayed as GBP.
So I must somehow add this logic too:
// loop through payments for each invoice, and add payment
// amount to a local variable called total
if (payment.Currency == Currency.Gbp)
{
total += payment.Amount;
} else
{
total += payment.Amount / (decimal)payment.ConversionRate;
}
The Payment.cs entity has these two properties that are of concern:
public class PaymentViewModel
{
public int Id { get; set; }
[Required]
public decimal Amount { get; set; }
[Required]
[Display(Name = "Payment Currency")]
public Currency Currency { get; set; }
[Display(Name = "Conversion Rate")]
public float ConversionRate { get; set; }
}
You are going to have an issue because of the float/decimal differences. Depending on your database provider, it might allow you to force the cast from decimal to float (or it might not). Of course, then you'll have issues of payments that are really close, but not quite the same. What if the amount / CoversionRate is 0.999999999999999999999999999 GBP when the amount was for 1 GBP? Technically it's not fully paid.
Ultimately, the conversion rate should also be a decimal, not float, but determining the precision depends on your source. Is it accurate to 5 decimal places or 7?
var outstanding = _context.Invoices
.Where(x=>x.Date >= startDate)
.Where(x=>x.Date <= endDate)
.Where(x=>!x.IsDeleted)
.Where(x=>x.Payments.Sum(p=>(float)p.Amount / p.ConversionRate) < x.Amount);
Alternatively if the total paid is within 1 GBP:
var outstanding = _context.Invoices
.Where(x=>x.Date >= startDate)
.Where(x=>x.Date <= endDate)
.Where(x=>!x.IsDeleted)
.Where(x=>x.Payments.Sum(p=>(float)p.Amount / p.ConversionRate) - x.Amount < 1);
You can use ternary operator to adjust payment amount in case it is not in GBP:
var outstandingInvoices =
from inv in _context.Invoices
let totalPayment = inv.Payments.Sum(p =>
p.Currency == Currency.Gbp ? p.Amount : p.Amount / p.ConversionRate)
where !inv.IsDeleted
&& inv.Date >= startDate && inv.Date <= endDate
&& totalPayment < inv.Amount
select inv;
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))
});
}