I want something really simple, i want from a list of records to find the ones of a specific date (records can have the same date more than once) and with a specific value,
and count how many of those values exist. So far i'm using this but im not sure if it is the best approach
Here is Records
public class Records
{
public DateTime Date { get; set; }
public int Record { get; set; }
}
and here is the code
List<Records> records = GetRecords();
var distinctDates = records.Select(x => x.Date).Distinct().ToList();
for (int i = 0; i < distinctDates.Count(); i++)
{
statistics.Rows.Add(new object[] { distinctDates[i].ToString("MM/dd/yyyy HH:mm"),
records.Select(x => x).Where(d => d.Date == distinctDates[i]).Count(x => x.Record == -1).ToString()});
}
I want to know the sum of the appearance of a specific Date and a specific value in Record
So if the database has these values:
11/11/1980, 3
11/11/1980, 3
12/11/1980, 2
I want to get as a result 2 when i will seek for the count of the conditions 11/11/1980 and 3
var result = records.GroupBy(x => x.Date)
.Select(x => new { Date = x.Key, Count = x.Count() });
is what you want
result will now have a list of dates and count.
if you want a specific value then like this:
var result = records.Where(x => x.Record == -1).
.GroupBy(x => x.Date)
.Select(x => new { Date = x.Key, Count = x.Count() });
To group by the date and not the date time use the date property of the object in the GroupBy statement
.GroupBy(x => x.Date.Date)
What you want is a couting of all dates. GroupBy() allows you to distinctly select all dates. Then you can Count() all grouped records with some conditions as you want.
records.GroupBy(x => x.Date).Select(x => new
{
Date = x.Key,
Count = x.Count(y => y.Record == -1)
}) // This should give you all distinct dates with count
So given a DateTime date and an int recordValue, you want to count all Records from your recordCollection that have a property Date equal to date and a property Record equal to RecordValue.
How about this:
Datetime date = new DateTime(1980, 11, 11);
int recordValue = 3;
var result = recordCollection
// keep only the records with date and recordValue:
.Where(record => record.Date == date && record.Record == recordValue)
// and Count them:
.Count();
Related
What I have is a string data type which stores the duration.
I am a looking for sum of the duration and then average of that sum.
I am using ASP.NET MVC.
Example:
00:30:21
00:40:01
00:21:10
Model class
public DateTime? FeedbackDateTime { get; set; }
public DateTime? FeedbackSharedDateTime { get; set; }
public string AuditorAHT { get; set; }
ReportVM To Group Data and display in the View
public string FeedbackSharedBy { get; set; }
public int AuditCount { get; set; }
public string AudtAht { get; set; }
Controller that saves the action perform by auditor as duration in
public string AuditorAHT { get; set; }
dto.FeedbackSharedDateTime = DateTime.Now;
string ahtString = string.Format("{0:hh\\:mm\\:ss}", dto.FeedbackSharedDateTime - dto.FeedbackDateTime);
dto.AuditorAHT = ahtString;
db.SaveChanges();
Below Action should display Auditors Name, Count, and Average Time spent. From which Name and Count is working but not the Average Time Spend
var audtName = db.Chats.Where(x => System.Data.Entity.DbFunctions.TruncateTime(x.MSTChatCreatedDateTime) >= mostRecentMonday
&& System.Data.Entity.DbFunctions.TruncateTime(x.MSTChatCreatedDateTime) <= weekEnd && x.Feedback != null && x.FeedbackSharedBy != null).Select(x => new {
x.FeedbackSharedBy,
x.AuditorAHT
}).ToList() // this hits the database
// We need to do grouping in the code (rather than the db)
// because timespans are stored as strings
.GroupBy(e => e.FeedbackSharedBy)
.Select(g => new ReportVM
{
FeedbackSharedBy = g.Key,
AuditCount = g.Count(),
AudtAht = TimeSpan.FromSeconds(g.Sum(t => TimeSpan.Parse(t.AuditorAHT).TotalSeconds / g.Count())).ToString()
})
.OrderByDescending(s => s.AuditCount).ToList();
ViewBag.AudtReport = audtName;
Above COde is working for me, managed to make it work.
You can convert the string duration into a TimeSpan type and use this to do time calculations. To convert it you can use TimeSpan.Parse() or if you have a fix format use TimeSpan.ParseExact().
With TimeSpan you can get the the totals out of it with the various .Total*() methods. You also can get the internal ticks count with .Ticks. That's the one with the highest precision.
Now its a simple math: sum of all ticks / count = average ticks
You can pass this average ticks count into a TimeSpan again to grab it as .TotalMilliseconds() or output it formatted with .ToString().
Here is a basic sample:
using System;
public class Program
{
public static void Main()
{
var duration1 = TimeSpan.Parse("00:30:21");
var duration2 = TimeSpan.Parse("00:40:01");
var duration3 = TimeSpan.Parse("00:21:10");
var totalDuration = duration1.Add(duration2).Add(duration3);
var averageDurationTicks = totalDuration.Ticks / 3;
var averageDuration = TimeSpan.FromTicks(averageDurationTicks);
Console.WriteLine($"Total duration: {totalDuration}, Average duration: {averageDuration}");
}
}
Here is a .Net Fiddle: https://dotnetfiddle.net/1Q9tmV
After spending lot of time and with help of tymtam made the code work with below code.
var audtName = db.Chats.Where(x => System.Data.Entity.DbFunctions.TruncateTime(x.MSTChatCreatedDateTime) >= mostRecentMonday
&& System.Data.Entity.DbFunctions.TruncateTime(x.MSTChatCreatedDateTime) <= weekEnd && x.Feedback != null && x.FeedbackSharedBy != null).Select(x => new {
x.FeedbackSharedBy,
x.AuditorAHT
}).ToList() // this hits the database
// We need to do grouping in the code (rather than the db)
// because timespans are stored as strings
.GroupBy(e => e.FeedbackSharedBy)
.Select(g => new ReportVM
{
FeedbackSharedBy = g.Key,
AuditCount = g.Count(),
AudtAht = TimeSpan.FromSeconds(g.Sum(t => TimeSpan.Parse(t.AuditorAHT).TotalSeconds / g.Count())).ToString()
})
.OrderByDescending(s => s.AuditCount).ToList();
ViewBag.AudtReport = audtName;
If a second precision is enough for you you could combine Linq's Sum and Average with TimeSpan's Parse and TotalSeconds:
Sum = TimeSpan.FromSeconds(g.Sum(t => TimeSpan.Parse(t.Time).TotalSeconds)),
Avg = TimeSpan.FromSeconds(g.Average(t => TimeSpan.Parse(t.Time).TotalSeconds))
Here is a full example:
var data = new []{
new { X = "A", Time = "00:30:21"},
new { X = "B", Time = "00:40:01"},
new { X = "B", Time = "00:21:10"}
};
var grouped = data
.GroupBy(e => e.X)
.Select( g => new {
X = g.Key,
Count = g.Count(),
Sum = TimeSpan.FromSeconds(g.Sum(t => TimeSpan.Parse(t.Time).TotalSeconds)),
Avg = TimeSpan.FromSeconds(g.Average(t => TimeSpan.Parse(t.Time).TotalSeconds))
});
foreach (var item in grouped)
{
Console.WriteLine( $"'{item.X}' has {item.Count} item(s), sum = {item.Sum}, avg = {item.Avg}");
}
This produces:
'A' has 1 item(s), sum = 00:30:21, avg = 00:30:21
'B' has 2 item(s), sum = 01:01:11, avg = 00:30:35.5000000
You could use TotalMilliseconds + FromMillisecond or even go super precise Ticks.
Variant with Aggregate
Another option is to Parse earlier:
Sum = g.Select(e => TimeSpan.Parse(e.Time)).Aggregate((t1, t2) => t1 + t2),
Avg = g.Select(e => TimeSpan.Parse(e.Time)).Aggregate((t1, t2) => t1 + t2) / g.Count()
For LINQ to Entities
As you report in your comment if we try the above with your real code it results in LINQ to Entities does not recognize the method 'System.TimeSpan FromSeconds(Double)' method, and this method cannot be translated into a store expression. (With the same for TimeSpan.Parse.
Because of this we will would need to do the grouping in the code. This is less efficient that if the TimeSpan was in the database as TimeSpan.
var grouped = db.Chats
.Where(...)
.Select( x => new {
x.FeedbackSharedBy,
x.AuditorAHT
})
.ToList() // this hits the database
// We need to do grouping in the code (rather than the db)
// because timespans are stored as strings
.GroupBy(e => e.FeedbackSharedBy)
.Select(g => new
{
FeedbackSharedBy = g.Key,
AuditCount = g.Count(),
AuditorAHTSumSeconds = TimeSpan.FromSeconds(g.Sum(t => TimeSpan.Parse(t.AuditorAHT).TotalSeconds) / g.Count())
.ToString(),
})
.OrderByDescending(s => s.AuditCount)
.ToList(); // Optional
Please guys,
What i want to achieve is to get the previous 6 month back and search through my Transactions Table and retrieve a list of transactions that fall under each month property of Date Time and sum thier amount.
forexample. the current date is 04/03/2020 the last 6 months date becomes 02/20,01/2020,12/2019,11/2019,10/2019,09/2019 now i want to search through a table transactions which has a DateTime Property DateCreated and retrieve all the transactions that occurred within each month and sum all their amount.
ResponseCode 00 means successful payment
what i have tried.
List<DateTime> months = new List<DateTime>();
List<double> MonthSum= new List<Double>();
DateTime[] lastSixMonths = Enumerable.Range(0, 6).Select(i => DateTime.Now.AddMonths(-i)).ToArray();
foreach (var month in lastSixMonths)
{
var monthString = month.ToString("MM/yyyy");
var trans = await _context.Transactions.Where(c => monthString.Contains(c.DateCreated.ToString()) && c.ResponseCode == "00").Select(c => c.Amount).ToListAsync();
var sum = trans.Sum();
MonthSum.Add(sum);
}
something seems to be wrong with how am doing this. please help
I hope that's what you need:
var fromDate = DateTime.Now.AddMonths(-6);
var sumByMonth = _context.Transactions.Where(d => d.CreateDate > fromDate)
.GroupBy(d => d.CreateDate.Month)
.Select(d => new { Month = d.Key, Sum = d.Sum(docs => docs.Amount) })
.ToDictionary(a => a.Month , b => b.Sum);
The DateTime field is displayed as "yyyy-MM-dd" in linq, so you need to change month to "yyyy-MM" for judgment.
In the where condition, the 'Contains' condition needs to be exchanged.
List<DateTime> months = new List<DateTime>();
List<double> MonthSum = new List<Double>();
DateTime[] lastSixMonths = Enumerable.Range(0, 6).Select(i => DateTime.Now.AddMonths(-i)).ToArray();
foreach (var month in lastSixMonths)
{
var monthString = month.ToString("yyyy-MM");
var trans = await _context.Transactions.Where(c => c.DateCreated.ToString().Contains(monthString) && c.ResponseCode == "00").Select(c => c.Amount).ToListAsync();
var sum = trans.Sum();
MonthSum.Add(sum);
}
I'm trying to figure out how to count the number of Mondays, Tuesdays etc in a table using Linq and C#
Here is my sample data:
Status StatusDate
DELIVRD 2015-04-16 11:57:47.000
DELIVRD 2015-04-16 13:02:57.000
I know I need to use Group by to group the same Mondays, Tuesdays etc as 1.
My attempt:
var mondays = rad.SMSSentItems
.Where(x => (x.StatusDate.Value.DayOfWeek == DayOfWeek.Monday)
&& (x.Status == "DELIVRD"))
.ToList()
.Count;
You need to filter by the desired Status (DELIVRD) then group them by DayOfWeek of the status date
var weekDays = rad.SMSSentItems
.Where(x => x.Status == "DELIVRD")
.AsEnumerable()
.GroupBy(x => x.StatusDate.Value.DayOfWeek)
.Select(g => {
//Total items sent on this day of the week
var totalItemCount = g.Count();
//Total number if this distinct day of the week
var totalNumberOfDays = g.Select(x => x.StatusDate.Value.Date).Distinct().Count();
return new {
DayOfWeek = g.Key,
TotalItemCount = totalItemCount,
TotalNumberOfDays = totalNumberOfDays,
AverageItemPerDay = totalItemCount / totalNumberOfDays
};
})
.ToList();
Here unoinColumn is return the all the date with out duplicate but i want to get the date group by month
month totalamount
7/2014 10000
8/2014 10000
enter code here var unoinDateColumn = (from agent in db.collections
where agent.Date_Time.Value.Year == thisYear
select agent.Date_Time).Union(from u in db.bank_deposit
where u.Date_Time.Value.Year == thisYear
select u.Date_Time).ToList();
You can do something like that, if I understood well.
Filter collections and deposits by given year, select date_time and amount
Union, group by month, select date in the desired format, and sum amount.
You can of course do all that in one query, but I'm not sure this will be easier to read ;)
var collections = db.Collections.Where(x => x.Date_Time.Value.Year == thisYear)
.Select(m => new {
dt = m.Date_Time.Value,
amount = m.Amount
});
var deposits = db.Collections.Where(x => x.Date_Time.Value.Year == thisYear)
.Select(m => new {
dt = m.Date_Time.Value,
amount = m.Amount
});
var result = collections.Union(deposits)
.GroupBy(m => m.dt.Month)
.Select(g => new {
date = g.First().dt.ToString("MM/yyyy"),
totalAmount = g.Sum(x => x.amount)
});
I have a table Account with a column called CreatedOn (nullable datetime).
I'm writing a method that takes two parameters from and to (DateTime objects) and I want to select all the dates between from and to along with the number of rows in T that where created on each of those dates.
How can I do this in an elegant and effective way in LINQ?
I tried to do it this way, but it seems to query the database once for each date in the list dates:
static void Test(DataClasses1DataContext context, DateTime from, DateTime to)
{
List<DateTime> dates = new List<DateTime>();
while (from <= to)
{
dates.Add(from.Date);
from = from.AddDays(1);
}
var result = dates.Select(d => new { Date = d, Count = context.Accounts.Count(a => a.CreatedOn.HasValue && a.CreatedOn.Value.Date == d) });
foreach (var row in result)
Console.WriteLine(row.Date + " " + row.Count);
}
Updated in Response to comment
I wouldn't say its elegant but it does only query the database once.
static void Test(DataClasses1DataContext context, DateTime fromDate, DateTime toDate)
{
var result = context.Accounts
.Where(p => p.CreatedOn >= fromDate && p.CreatedOn <= toDate)
.GroupBy(x => x.CreatedOn.Date)
.Select(x => new {
dt = x.Key,
count = x.Count()});
}
List<DateTime> dates = new List<DateTime>();
while (fromDate <= toDate)
{
dates.Add(fromDate.Date);
fromDate = fromDate.AddDays(1);
}
var allDates = dates.Select(x => new {
dt = x,
count = 0});
// Merge both lists and then filter to include highest count
var result = rows.Concat(allDates)
.GroupBy(x => x.dt)
.Select(x => new {
dt = x.Key,
count = x.OrderByDescending(c => c.count)
.FirstOrDefault().count});