LINQ find max/min value with corresponding time fields - c#

I have a table of data recordings from a weather station from which I am querying results into a WPF ListBox.
The table structure is:
date
time
temp
rain_today
humidity
etc
I have a query which works fine:
var q = from c in db.Apr11log
group c by c.LogDate into g
orderby g.Key
select new
{
LogDate = g.Key,
MaxTemp = g.Max(c => c.Temp),
MinTemp = g.Min(c => c.Temp),
Rain = g.Max(c => c.Rain_today),
};
However I am trying to get the corresponding time of the Max Temp and Min Temp, i.e.
TimeMax = .....
TimeMin = .....
I've googled and googled but found nothing useful.

var q = from c in db.Apr11log
group c by c.LogDate into g
orderby g.Key
select new
{
LogDate = g.Key,
MaxRow = g.OrderByDescending(c => c.Temp).Select(c => new { c.LogDate, c.Temp }).First(),
MinRow = g.OrderBy(c => c.Temp).Select(c => new { c.LogDate, c.Temp }).First(),
Rain = g.Max(c => c.Rain_today),
};
or
var q = from c in db.Apr11log
group c by c.LogDate into g
orderby g.Key
let maxRow = g.OrderByDescending(c => c.Temp).First()
let minRow = g.OrderBy(c => c.Temp).First()
select new
{
LogDate = g.Key,
MaxTemp = maxRow.Temp,
MaxTempDate = maxRow.LogDate,
MinTemp = minRow.Temp,
MinTempDate = minRow.LogDate,
Rain = g.Max(c => c.Rain_today),
};

let maxTemp = c.Max(c=>c.Temp)
let minTemp = c.Min(c=>c.Temp)
select new {
LogDate = g.Key,
MaxTemp = maxTemp,
MinTemp = minTemp,
MaxTime = g.FirstOrDefault(c=>c.Temp = maxTemp).Time,
MinTime = g.FirstOrDefault(c => c.Temp = minTemp).Time,
Rain = g.Max(c => c.Rain_today),
};

Related

How can you chain group by using EF core 3

I want to generate a query using EF core 3.1 equivalent to this one:
SELECT g.Date, Count(*) countIntervals
FROM(
SELECT
TODATETIMEOFFSET(DATETIME2FROMPARTS(DATEPART(year, myTimestamp),1,1,0,0,0,0,0), '+00:00') Date,
DATEPART(month, myTimestamp) - (DATEPART(month, myTimestamp) % 3) interval
GROUP BY
DATEPART(year, myTimestamp),
DATEPART(month, myTimestamp) - (DATEPART(month, myTimestamp) % 3),
UserId
) as g
GROUP BY Date
here is my use case: "i want to count all intervals in each year"
I tried this in C# but i get an error System.InvalidOperationException: The LINQ expression could not be translated.:
var query = _context.DatesTable
.GroupBy(m => new
{
Year = m.Timestamp.Year,
interval = m.Timestamp.Month - m.Timestamp.Month % 3,
UserId = m.UserId
})
.Select(g => new
{
Date = new DateTimeOffset(g.Key.Year, 1, 1, 0, 0, 0, TimeSpan.Zero),
interval = g.Key.interval
})
.GroupBy(x => new {
Date = x.Date
})
.Select(g => new
{
Date = g.Key.Date ,
CountIntervals = g.Count()
});
query.ToList()
I already tried to load data in memory using AsEnumerable(). That works but it's not efficient:
var query = _context.DatesTable
.GroupBy(m => new
{
Year = m.Timestamp.Year,
interval = m.Timestamp.Month - m.Timestamp.Month % 3,
UserId = m.UserId
})
.Select(g => new
{
Date = new DateTimeOffset(g.Key.Year, 1, 1, 0, 0, 0, TimeSpan.Zero),
interval = g.Key.interval
}).AsEnumerable();
var result = query.GroupBy(x => new {
Date = x.Date
})
.Select(g => new
{
Date = g.Key.Date,
CountIntervals = g.Count()
});
query.ToList()
Is there any efficient solution for this query ?
Try the following query:
var query = _context.DatesTable
.GroupBy(m => new
{
Year = m.Timestamp.Year,
interval = m.Timestamp.Month - m.Timestamp.Month % 3,
UserId = m.UserId
})
.Select(g => new
{
Year = g.Key.Year,
interval = g.Key.interval
})
.GroupBy(x => new {
Year = x.Year
})
.Select(g => new
{
Year = g.Key.Year,
CountIntervals = g.Count()
});
var result = query.ToList();
A pretty direct transalation of your query goes like this.
var query = _context.DatesTable
.GroupBy(m => new
{
Year = m.Timestamp.Year,
interval = m.Timestamp.Month - m.Timestamp.Month % 3,
UserId = m.UserId
})
.GroupBy(x => g.Key.Year)
.Select(g => new
{
Year = g.Key,
CountIntervals = g.Count()
});
var result = await query.ToListAsync();

Chart from multiple tables

I have three tables named Deposit, Debit and Transfer,
like
Deposit { DepositID, DepostDate, Amount}
Debit { DebitID, DebitDate, Amount}
Transfer { TransferID, TransferDate, Amount}
How can I show all three tables in one chart? I am even wondering if it is better to put those three tables into one table instead, like
Transaction {TransactionId, TransactionTypeId, TransactionDate, Amount}
where TransactiontypeId could be 1 = for Deposit, 2 for Debit and 3 for Transfer and bind this transaction table to the chart.
Let's say I have all those in one table instead and with table name Transactions then #mm8 helped me figure this out:
var result = (from pr in db.Transactions
join tr in db.TransactionType on pr.TrTypeId equals tr.TransactionTypeId
select new
{
TransactionDate = pr.TransactionDate,
TransactionType = tr.TrType,
Amount = pr.Amount
}).ToList();
chart1.DataSource = result
.GroupBy(x => x.TransactionDate.Value.Year)
.Select(g => new
{
Year = g.Key,
TransactionType = g. //////
Amount = g.Sum(y => y.Amount)
})
.ToArray();
Is is better to have a chart from one table or from multiple tables and how to do multiple.
I am aware that I have to create different series for every table like this:
var Depseries = chart1.Series.Add("Deposit");
Depseries.XValueMember = "Year";
Depseries.YValueMembers = "DepositAmount";
Depseries.Name = "Deposit";
chart1.ChartAreas["ChartArea1"].AxisX.Interval = 1;
chart1.Series["Deposit"].IsValueShownAsLabel = true;
Depseries.CustomProperties = "LabelStyle=Left";
// Debit
var Debseries = chart1.Series.Add("Debit");
Debseries.XValueMember = "Year";
Debseries.YValueMembers = "DebitAmount";
Debseries.Name = "Debit";
chart1.ChartAreas["ChartArea1"].AxisX.Interval = 1;
chart1.Series["Debit"].IsValueShownAsLabel = true;
Debseries.CustomProperties = "LabelStyle=Left";
// Transfer
var FDseries = chart1.Series.Add("Transfer");
FDseries.XValueMember = "Year";
FDseries.YValueMembers = "TransferAmount";
FDseries.Name = "Transfer";
chart1.ChartAreas["ChartArea1"].AxisX.Interval = 1;
chart1.Series["Transfer"].IsValueShownAsLabel = true;
FDseries.CustomProperties = "LabelStyle=Left";
You could just select the data from each of the tables and then use the DataBind method to populate the series with data, e.g.:
var deposits = (from x in db.Deposits select new { x.DepositDate, x.Amount })
.ToArray()
.GroupBy(x => x.DepositDate.Year)
.Select(g => new { Year = g.Key, Amount = g.Sum(y => y.Amount) })
.ToArray();
var Depseries = chart1.Series.Add("Deposit");
Depseries.XValueMember = "Year";
Depseries.YValueMembers = "DepositAmount";
Depseries.Name = "Deposit";
chart1.ChartAreas["ChartArea1"].AxisX.Interval = 1;
chart1.Series["Deposit"].IsValueShownAsLabel = true;
Depseries.CustomProperties = "LabelStyle=Left";
chart1.Series["Deposit"].Points.DataBind(deposits, "Year", "Amount", null);
var debits = (from x in db.Debits select new { x.DebitDate, x.Amount })
.ToArray()
.GroupBy(x => x.DebitDate.Year)
.Select(g => new { Year = g.Key, Amount = g.Sum(y => y.Amount) })
.ToArray();
var Debseries = chart1.Series.Add("Debit");
Debseries.XValueMember = "Year";
Debseries.YValueMembers = "DebitAmount";
Debseries.Name = "Debit";
chart1.ChartAreas["ChartArea1"].AxisX.Interval = 1;
chart1.Series["Debit"].IsValueShownAsLabel = true;
Debseries.CustomProperties = "LabelStyle=Left";
chart1.Series["Debit"].Points.DataBind(debits, "Year", "Amount", null);
...

How can I improve this LINQ query?

I fear that I'm doing n+1 query here, how can I improve this?
var inventories = AppContext.Inventories
.GroupBy(i => new { i.LocationId, i.ProductId })
.Select(g => new InventoryAvailableQuantity
{
ProductId = g.Key.ProductId,
LocationId = g.Key.LocationId,
Product = g.FirstOrDefault().Product.Name,
Location = g.FirstOrDefault().Location.Name,
PurchasePrice = AppContext.Inventories.Where(i => i.ProductId == g.Key.ProductId).OrderByDescending(i => i.DateAdded).FirstOrDefault().PurchasePrice,
ResellerPrice = AppContext.Inventories.Where(i => i.ProductId == g.Key.ProductId).OrderByDescending(i => i.DateAdded).FirstOrDefault().ResellerPrice,
RetailPrice = AppContext.Inventories.Where(i => i.ProductId == g.Key.ProductId).OrderByDescending(i => i.DateAdded).FirstOrDefault().RetailPrice
}).ToList();
You can use comprehension instead of method and gain the ability to use "let":
var inventories = from inv in AppContext.Inventories
group inv by new { i.LocationId, i.ProductId } into g
let firstInv = g.FirstOrDefault()
let firstPur = AppContext.Inventories
.Where(i => i.ProductId == g.Key.ProductId)
.OrderByDescending(i => i.DateAdded)
.FirstOrDefault()
select new InventoryAvailableQuantity
{
ProductId = g.Key.ProductId,
LocationId = g.Key.LocationId,
Product = firstInv.Product.Name,
Location = firstInv.Location.Name,
PurchasePrice = firstPur.PurchasePrice,
ResellerPrice = firstPur.ResellerPrice,
RetailPrice = firstPur.RetailPrice
}; // ( select ... { ... }).ToList(); if you will
Fast answer
var inventories = Inventories
.GroupBy(i => new {i.LocationId, i.ProductId})
.Select(g => new
{
g.Key.ProductId,
g.Key.LocationId,
CurrentInventories = g.FirstOrDefault(),
LastInventories = Inventories.Where(i => i.ProductId == g.Key.ProductId).OrderByDescending(i => i.DateAdded).FirstOrDefault()
})
.Select(g => new InventoryAvailableQuantity
{
ProductId = g.ProductId,
LocationId = g.LocationId,
Product = g.CurrentInventories.Product.Name,
Location = g.CurrentInventories.Location.Name,
PurchasePrice = g.LastInventories.PurchasePrice,
ResellerPrice = g.LastInventories.ResellerPrice,
RetailPrice = g.LastInventories.RetailPrice
})
.ToList();
You can take last item after grouping and take what you want.

Linq ordering output

I am trying to sort a listbox by the customerID and then by Total (discount*unitPrice*quantity) and cannot manage to organize the code in a way that will sort it in that way. Any help would be greatly appreciated.
HERE is a link showing an image on how the results should be returned as.
var load1 = System.IO.File.ReadAllLines(#"c:\temp\AS3Products.csv")
.Select(x => new
{
CID = x.Split(',')[0],
discount = x.Split(',')[2].Trim(),
productId = x.Split(',')[0].Trim()
});
var load2 = System.IO.File.ReadAllLines(#"c:\temp\AS3Transactions.csv")
.Select(x => new
{
productId = x.Split(',')[3],
unitPrice = x.Split(',')[4],
quantity = x.Split(',')[5]
});
var querypractice = from x in load1
join y in load2 on x.productId equals y.productId
where x.CID == "110"
orderby x.discount, y.quantity
select new { x.CID, x.discount, x.productId, y.quantity, y.unitPrice };
foreach (var x in querypractice)
{
double total = double.Parse(x.quantity) * double.Parse(x.unitPrice) * double.Parse(x.discount);
listBox1.Items.Add(x.CID+ " " +x.discount+" "+x.quantity+ " " + total);
}
Disclaimer: I don't have VS on this machine, so this isn't validated, but I think you can do it using the LET statement to set up the calculated value, then order based on it.
var querypractice = from x in load1
join y in load2 on x.productId equals y.productId
let total = x.discount*x.unitPrice*x.quantity
where x.CID == "110"
orderby x.CID, total
select new { x.CID, total };
http://www.codeproject.com/Articles/231164/Into-and-let-in-LINQ-Let-vs-Into
If you're positive that these files have numbers in the expected places all the time, you could parse them as you read them from the files. Otherwise, you'll want to do some validation first or you'll get exceptions.
(I changed double.Parse to decimal.Parse - it's more accurate for manipulating dollar amounts.)
var load1 = System.IO.File.ReadAllLines(#"c:\temp\AS3Products.csv")
.Select(x => new
{
CID = int.Parse(x.Split(',')[0]),
discount = decimal.Parse(x.Split(',')[2].Trim()),
productId = int.Parse(x.Split(',')[0].Trim())
});
var load2 = System.IO.File.ReadAllLines(#"c:\temp\AS3Transactions.csv")
.Select(x => new
{
productId = int.Parse(x.Split(',')[3]),
unitPrice = decimal.Parse(x.Split(',')[4]),
quantity = int.Parse(x.Split(',')[5])
});
Then you can create your list like this. (I removed the specific id you had in your query.)
var orderedList = (from x in load1
join y in load2 on x.productId equals y.productId
let total = (x.discount * y.unitPrice * y.quantity)
orderby x.CID descending, total descending
select new
{
x.CID,
x.discount,
x.productId,
y.quantity,
y.unitPrice
});

SQL query to LINQ expression

How can I translate this SQL query to a LINQ expression?
select NoteDate, SUM( DurationInHours ) from Note
where IDUser = '2933FB9C-CC61-46DA-916D-57B0D5EF4803' and
NoteDate > '2013-07-01'
group by NoteDate
I tried it, but it didn't work
var lastMonth = DateTime.Today.AddMonths(-1);
var userNotes = GetNotesByUser(idUser);
var b = from note in userNotes
group note by new {note.IDUser, note.NoteDate, note.DurationInHours} into g
where g.Key.NoteDate > lastMonth
select new {g.Key.NoteDate, TotalHours = g.Sum(a => a.DurationInHours)}
var id = new Guid("2933FB9C-CC61-46DA-916D-57B0D5EF4803");
var date = new DateTime(2013, 7, 1);
var query = from n in db.Notes
where n.IDUser == id && n.NoteDate > date
group n by n.NoteDate into g
select new {
NoteDate = g.Key,
Sum = g.Sum(x => x.DurationInHours)
};

Categories