I'm looking for a best practice for counting how many times each date occurs in a list.
For now, I have working code (just tested) but I think the way I did is not so good.
var dates = new List<DateTime>();
//Fill list here
var dateCounter = new Dictionary<DateTime, int>();
foreach (var dateTime in dates)
{
if (dateCounter.ContainsKey(dateTime))
{
//Increase count
dateCounter[dateTime] = dateCounter[dateTime] + 1;
}
else
{
//Add to dictionary
dateCounter.Add(dateTime, 1);
}
}
Anyone who knows a better solution?
dates.GroupBy(d => d).ToDictionary(g => g.Key, g => g.Count());
Related
I am currently working on a task where i want to take the content of 2 .csv files and put them in one new .csv file as well as sorting the content by date.
so far so good... the problem is that the content uses several datetime formats
can one of you guys help me with this?
here ist the code i have so far
//reading the raw files
string[] rawdata = File.ReadAllLines(PathInRaw);
string[] rawdataTick = File.ReadAllLines(PathInRawTick);
//clearing existing file and writing in the new content
File.WriteAllText(PathOut, " ");
File.AppendAllLines(PathOut, rawdata);
File.AppendAllLines(PathOut, rawdataTick);
//changing date format??? which i dont get to work
string[] list = { };
int counter = 0;
foreach (string line in File.ReadAllLines(PathOut))
{
column = line.Split(';');
column[0] = DateTime.Now.ToString("yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture);
list[counter] = Convert.ToString(column);
counter++;
}
File.WriteAllText(PathOut, " ");
File.WriteAllLines(PathOut, list);
//sorting it
DateTime d = DateTime.MinValue;
var query = from line in File.ReadLines(PathOut)
let fields = line.Split(';')
let dateParsed = DateTime.TryParse(fields[0], out d)
let dateObject = dateParsed ? d : DateTime.MinValue
orderby dateParsed, dateObject
select line;
List<string> sortedLines = query.ToList();
File.WriteAllText(PathOut, " ");
File.WriteAllLines(PathOut, sortedLines);
The Dateformates I have in the .csv are
5/30/2018 2:48:57 PM (MM/dd/yyyy HH:mm:ss a)
06.01.2018 06:12:19 (MM.dd.yyyy HH:mm:ss)
20180601 16:21:50 (yyyyMMdd HH:mm:ss)
The first two formats should be standard enough to be parsed with DateTime.Parse(...). The last one is a bit too custom for this to catch, so implementing a parsing method will be your best bet.
private static DateTime ParseDateTime(string dateTime) {
DateTime? d1 = null;
try {
d1 = DateTime.Parse(dateTime);
}
catch { }
if (d1 == null) {
try {
d1 = DateTime.ParseExact(dateTime, "yyyyMMdd HH:mm:ss", CultureInfo.InvariantCulture);
}
catch { }
}
return (DateTime)d1;
}
Here the first try catch block will catch all the standard formats and if it does not, the d1 object will still be null so then we will try your custom format. Finally we cast back to a non-nullable DateTime object to make it easier to work with.
A way to use this would be the following, where you would replace the two hard coded lists of DateTime strings with reading from your files.
List<string> rawLines1 = new List<string>() { "5/30/2018 2:48:57 PM", "06.01.2018 06:12:19" };
List<string> rawLines2 = new List<string>() { "20180601 16:21:50" };
List<string> rawLines = new List<string>();
rawLines.AddRange(rawLines1);
rawLines.AddRange(rawLines2);
List<DateTime> parsedDateTimes = new List<DateTime>();
foreach (string rawLine in rawLines) {
parsedDateTimes.Add(ParseDateTime(rawLine));
}
parsedDateTimes = parsedDateTimes.OrderBy(x => x).ToList();
The very last line takes care of sorting back into the order with the oldest at the top. If you want this reversed, replace .OrderBy(x => x) with .OrderByDescending(x => x)
From here you can write back out to your output file.
I want to get date of last seven days from now.For example current date is
02-10-2016, get date of seven days like this
01-10-2016,30-09-2016,29-09-2016,28-09-2016,27-09-2016,26-09-2016
My code
string dt = DateTime.Now.ToString("yyyy-MM-dd");
DateTime lastWeek = dt.AddDays(-7.0);
AddDays is a part of DateTime, not of string.
You need to build your dates iteratively and then convert it to a string.
DateTime[] last7Days = Enumerable.Range(0, 7)
.Select(i => DateTime.Now.Date.AddDays(-i))
.ToArray();
foreach (var day in last7Days)
Console.WriteLine($"{day:yyyy-MM-dd}"); // Any manipulations with days go here
Without LINQ, with a simple loop:
DateTime dt = DateTime.Now;
for (int i=0;i<7;i++)
{
dt = dt.AddDays(-1);
Console.WriteLine(dt.Date.ToShortDateString());
}
Try using Linq:
var date = new DateTime(2016, 10, 2);
var result = Enumerable.Range(1, 7)
.Select(day => date.Date.AddDays(- day))
.ToArray(); // if you want to represent dates as an array
Test
// 01-10-2016,30-09-2016,29-09-2016,28-09-2016,27-09-2016,26-09-2016,25-09-2016
Console.Write(string.Join(",", result.Select(d => d.ToString("dd-MM-yyyy"))));
You are almost there, the AddDays method will add only a specific number of days to the given data and dives you the resulted date. But here in your case you need a list of dates, so you have to loop through those dates and get them as well. I hope the following method will help you to do this:
public static string GetLast7DateString()
{
DateTime currentDate = DateTime.Now;
return String.Join(",",Enumerable.Range(0, 7)
.Select(x => currentDate.AddDays(-x).ToString("dd-MM-yyyy"))
.ToList());
}
Note : If you want to exclude the current date means you have to take the range from 7 and the count should be 7. You can read more about Enumerable.Range here
If you call this method like the following means you will get the output as 24-10-2016,23-10-2016,22-10-2016,21-10-2016,20-10-2016,19-10-2016,18-10-2016
string opLast7Days = GetLast7DateString();
public static List<DateTime> getLastSevenDate(DateTime currentDate)
{
List<DateTime> lastSevenDate = new List<DateTime>();
for (int i = 1; i <= 7; i++)
{
lastSevenDate.Add(currentDate.AddDays(-i));
}
return lastSevenDate;
}
Is there a way to fill a List by all of minutes of a day? So, it must has 1440 (60 * 24) minutes as DateTime
(14:37, 14:38, 14:39 etc..)
object.
Thanks in advance.
var startTime = DateTime.Now.Date;
var minsOfDay =
Enumerable.Range(0, 1440).Select(i => startTime.AddMinutes(i)).ToList();
A longer, though easier to follow way:
var timeStart = DateTime.Now.Date;
var timeStop = DateTime.Now.Date.AddDays(1);
var mins = new List<DateTime>();
while(timeStart < timeStop)
{
mins.Add(timeStart);
timeStart = timeStart.AddMinutes(1);
}
As an alternative, you can use List<string> with AddMinutes methods like;
List<string> list = new List<string>();
DateTime midnight = DateTime.Today;
while (midnight < DateTime.Today.AddDays(1))
{
list.Add(midnight.ToString("HH:mm"));
midnight = midnight.AddMinutes(1);
}
foreach (var item in list)
{
Console.WriteLine(item);
}
Here a demonstration.
So I have a set of data from an API call that I need to use. I filter to correct subset and access a specific field with the code below. Is there a better way of getting currentDate and beforeCurrentDate?
DateTime beforeCurrentDate, currDate;
var curr = from c in GlobalResults<FinancialYear>.Results
where c.IsCurrentYear = true
select c;
var prev = from c in GlobalResults<FinancialYear>.Results
where c.ID < curr.FirstOrDefault().ID && c.YearEnd == curr.FirstOrDefault ().YearStart.AddDays(-1)
select c;
foreach (var cfy in curr)
{
currDate = cfy.YearEnd;
}
foreach (var pfy in prev)
{
beforeCurrentDate = pfy.YearStart.AddDays (-1);
}
I know the foreach is the wrong way, so what should I use?
EDIT: What the API results contain is a set of dates, with one having the IsCurrent field set to true. I want the EndDate field of the IsCurrent = true result, and the StartDate field of the previous result. Previous ito of the StartDate to EndDate period. The ID field is no use since a previous date range could be captured after the current date range.
var curr = GlobalResults<FinancialYear>.Results.FirstOrDefault(c => c.IsCurrentYear);
var prev = GlobalResults<FinancialYear>.Results.FirstOrDefault(c => c.ID < curr.ID && c.YearEnd == curr.YearStart.AddDays(-1));
var currDate = curr.YearEnd;
var beforeCurrentDate = prev.YearStart.AddDays(-1);
You can get the current Date for the financial year by constraining the year and choosing the Last(), which replaces your foreach loop:
var currYear = GlobalResults<FinancialYear>.Results.Where(p=>p.IsCurrentYear == true).Last();
var currDate = currYear.YearEnd;
Similarly, you can use the currYear to get the previous information:
var prevYear = GlobalResults<FinancialYear>.Results.Where(p=>p.YearEnd == currDate.AddDays(-1)).FirstOrDefault();
var beforeCurrentDate = prevYear.YearStart.AddDays(-1);
I have recently written a LINQ query to get a Dictionary containing the last 6 month's placement amounts.
It is returning a Dictionary of Month string - Decimal Amount pairs.
It seems kind of cludgey. Any of you LINQ masters out there able to help me refactor this to make a bit cleaner?
/// <summary>
/// Gets the last 6 months of Placement History totalled by Month
/// for all Agencies
/// </summary>
/// <returns></returns>
public Dictionary<string, decimal> getRecentPlacementHistory()
{
var placementHistoryByMonth = new Dictionary<string, decimal>();
using (DemoLinqDataContext db = new DemoLinqDataContext())
{
for (int i = 0; i < 6; i++)
{
Decimal monthTotal =
(from a in db.Accounts
where
(a.Date_Assigned.Value.Month == DateTime.Now.AddMonths(-i).Month &&
a.Date_Assigned.Value.Year == DateTime.Now.AddMonths(-i).Month)
select a.Amount_Assigned).Sum();
String currentMonth = DateTime.Now.AddMonths(-i).ToString("MMM");
placementHistoryByMonth.Add(currentMonth, monthTotal);
}
return placementHistoryByMonth;
}
}
First problem:
where (a.Date_Assigned.Value.Month == DateTime.Now.AddMonths(-i).Month &&
a.Date_Assigned.Value.Year == DateTime.Now.AddMonths(-i).Month)
Shouldn't the latter expression end with .Year rather than .Month? Surely you'll rarely get a year with a value of 1-12...
I would extract the idea of the "current month" as you're using it a lot. Note that you're also taking the current time multiple times, which could give odd results if it runs at midnight at the end of a month...
public Dictionary<string, decimal> getRecentPlacementHistory()
{
var placementHistoryByMonth = new Dictionary<string, decimal>();
using (DemoLinqDataContext db = new DemoLinqDataContext())
{
DateTime now = DateTime.Now;
for (int i = 0; i < 6; i++)
{
DateTime selectedDate = now.AddMonths(-i);
Decimal monthTotal =
(from a in db.Accounts
where (a.Date_Assigned.Value.Month == selectedDate.Month &&
a.Date_Assigned.Value.Year == selectedDate.Year)
select a.Amount_Assigned).Sum();
placementHistoryByMonth.Add(selectedDate.ToString("MMM"),
monthTotal);
}
return placementHistoryByMonth;
}
}
I realise it's probably the loop that you were trying to get rid of. You could try working out the upper and lower bounds of the dates for the whole lot, then grouping by the year/month of a.Date_Assigned within the relevant bounds. It won't be much prettier though, to be honest. Mind you, that would only be one query to the database, if you could pull it off.
Use Group By
DateTime now = DateTime.Now;
DateTime thisMonth = new DateTime(now.Year, now.Month, 1);
Dictionary<string, decimal> dict;
using (DemoLinqDataContext db = new DemoLinqDataContext())
{
var monthlyTotal = from a in db.Accounts
where a.Date_Assigned > thisMonth.AddMonths(-6)
group a by new {a.Date_Assigned.Year, a.Date_Assigned.Month} into g
select new {Month = new DateTime(g.Key.Year, g.Key.Month, 1),
Total = g.Sum(a=>a.Amount_Assigned)};
dict = monthlyTotal.OrderBy(p => p.Month).ToDictionary(n => n.Month.ToString("MMM"), n => n.Total);
}
No loop needed!
If you are not worried about missing months with no data,then I had a similar problem where I did the following : (translated to your variables)
DateTime startPeriod =
new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1).AddMonths(-6);
var query1 = from a in db.Accounts where a.Date_Assigned >= startPeriod
group a by new { a.Date_Assigned.Year ,a.Date_Assigned.Month } into result
select new
{
dt = new DateTime( result.Key.Year, result.Key.Month , 1),
MonthTotal = result.Sum(i => i.Amount_Assigned)
} ;
var dict = query1.OrderBy(p=> p.dt).ToDictionary(n => n.Dt.ToString("MMM") , n => n.MonthTotal );