Split field a string and compare each value with string linq c# - c#

I'm trying to search in DataTable with three string columns[start_date, end_date, expected_visits] using LINQ, but the third column has many lines as shown
So I split the third one with \n but I want to loop over date to compare it with text of txtSearchYear and return the result as DataTable, When I tried to use ForEach as shown in my code, An error appeared with me says A local or parameter named 'e' cannot be declared in this scope because that name is used in an enclosing local scope to define a local here is my code ...
var tblResult =
tbl
.AsEnumerable()
.Where(x =>
x.Field<string>("start_date").Substring(0, 4).Equals(txtSearchYear.Text.Trim())
|| x.Field<string>("end_date").Substring(0, 4).Equals(txtSearchYear.Text.Trim())
|| x.Field<string>("expected_visits").Split('\n').ToList().ForEach((e) =>
{
e.Substring(0, 4).Equals(txtSearchYear.Text.Trim())
}));
if (tblResult.Any())
{
GetData(tblResult.CopyToDataTable());
}
else
{
DGVData.Rows.Clear();
}

I would directly compare the year and use the year (int) in lambda and trying to handle the dates as List or Array of Dates instead of comparing substrings, e.g.
DateTime now = DateTime.Now;
int year = -1;
if (!Int32.TryParse(txtSearchYear.Text.Trim(), out year) || year < 1900)
{
throw new InvalidProgramException($"Year {year} isn't valid for us.");
}
List<DateTime> dates = new List<DateTime>();
dates.Add(now);
dates.Add(now.Subtract(new TimeSpan(0, 10, 10)));
dates.Add(now.Subtract(new TimeSpan(0, 20, 20)));
/* ... */
var myList = dates.AsEnumerable().Where(x => x.Year == year).ToList();
Action<DateTime> action = new Action<DateTime>(MyDelegateAction);
myList.ForEach(e => MyDelegateAction(e));
}
private static void MyDelegateAction(DateTime date) { /* do somewhat */; }
kind regards, heinrich.

Related

How to display all Fridays Date between two dates

How to get a Friday date from the given start date and end date,
For Example:
25/03/2021 - starting date
14/08/2021 - endind date
I have a class
public static class DateUtils
{
public static List<DateTime> GetWeekdayInRange(this DateTime from, DateTime to, DayOfWeek day)
{
const int daysInWeek = 7;
var result = new List<DateTime>();
var daysToAdd = ((int)day - (int)from.DayOfWeek + daysInWeek) % daysInWeek;
do
{
from = from.AddDays(daysToAdd);
result.Add(from);
daysToAdd = daysInWeek;
}
while (from < to);
return result;
}
}
That is how i call it in main method:
var from = DateTime.Today; // 25/8/2019
var to = DateTime.Today.AddDays(23); // 23/9/2019
var allFriday = from.GetWeekdayInRange(to, DayOfWeek.Friday);
Console.WriteLine(allFriday);
Console.ReadKey();
Error i get:
System.Collections.Generic.List`1[System.DateTime]
I am new and still learning, how do I call in the main method so that my output be like all dates(fridays) between the range?
Link I followed
To Answer your question, instead of printing allFridays in one go, iterate over each element of list i.e allFridays, convert into string and then print
foreach(var friday in allFridays)
Console.WriteLine(friday);
Why you are getting System.Collections.Generic.List[System.DateTime] ?
Console.WriteLine(), for non primitive type by default calls
.ToString() function which prints type of it(if it is not overridden). In your case, you
need an individual date not a type of List, so you need to iterate
each DateTime from the list and print each date.
One Liner solution:
Console.WriteLine(string.Join(Environment.NewLine, allFridays));
Alternate solution:
public static List<DateTime> GetWeekdayInRange(this DateTime #from, DateTime to, DayOfWeek day)
{
//Create list of DateTime to store range of dates
var dates = new List<DateTime>();
//Iterate over each DateTime and store it in dates list
for (var dt = #from; dt <= to; dt = dt.AddDays(1))
dates.Add(dt);
//Filter date based on DayOfWeek
var filteredDates = dates.Where(x => x.DayOfWeek == day).ToList();
return filteredDates;
}
...
var #from = DateTime.Today; // 25/8/2019
var to = DateTime.Today.AddDays(23); // 23/9/2019
var allFriday = #from.GetWeekdayInRange(to, DayOfWeek.Friday);
Console.WriteLine(string.Join(Environment.NewLine, allFridays));
.NET FIDDLE
Since in your Usage section, you have successfully get the result via GetWeekdayInRange. You can print the dates with these methods:
Method 1:
allFriday.ForEach(x => Console.WriteLine(x.ToShortDateString()));
Method 2:
foreach (var friday in allFriday)
{
Console.WriteLine(friday.ToShortDateString());
}
Method 3:
for (var i = 0; i < allFriday.Count(); i++)
{
Console.WriteLine(allFriday[i].ToShortDateString());
}
Note: ToShortDateString() is one of the methods to display Date string. You can define your desired Date pattern with ToString().

Finding the month between a range

I have a datasource that returns dates and I have to find where the months falls within the month and day range buckets. The months and day range buckets are predefined so I put it in a Dictionary (not sure if that is even a good idea). I am using linq to find the min and Max dates and extracting the month from them. I need to find month from the dictionary where that month extracted falls within the range. For Example
Dictionary<int, int> MonthDayBuckets = new Dictionary<int, int>() { { 3,31 }, { 6,30 }, { 9,30 }, { 12,31 } };
var MinyDate = _dataSource.Min(x => x.values[0]);
var MaxDate = _dataSource.Max(x => x.values[0]);
var startMonth = Convert.ToDateTime(MinyDate).ToString("MM");
var endMonth = Convert.ToDateTime(MaxDate).ToString("MM");
Say startmonth return Jan so I want to be able to go to the dictionary and return only march (03.31) and if I get 10 for the Max (October) I am trying to return (12,31) December
If my understanding is correct, your MonthDayBuckets variable is meant to represent date ranges:
3/31 - 6/30
6/30 - 9/30
9/30 - 12/31
12/31 - 3/31
...and given a month, you're wanting to see what the end date is of the interval that the first of that month falls between? Like you gave the example of October returning 12/31.
This problem can be simplified since you'll get the same result saying "what's the next occurring date after this given date?" The next occurring date for 10/01 would be 12/31. So here's how you could rearrange your data:
var availableDates = new List<string> { "03/31", "06/30", "09/30", "12/31" };
Now you'll be able to find a match by finding the index of the first one that's greater than your given date. Note how I made the month/day combos lexicographical orderable.
var startMonth = Convert.ToDateTime(MinyDate).ToString("MM");
var startDate = startMonth + "/01";
var endMonth = Convert.ToDateTime(MaxDate).ToString("MM");
var endDate = endMonth + "/01";
// Wrap around to the first date if this falls after the end
var nextStartDate = availableDates.FirstOrDefault(d => d.CompareTo(startDate) >= 1) ?? availableDates[0];
var nextEndDate = availableDates.FirstOrDefault(d => d.CompareTo(endDate) >= 1) ?? availableDates[0];
You could use Linq for the purpose. For example,
var nearestKey = MonthDayBuckets.Keys.Where(x => x >= endMonth.Month).Min();
var nearestDate = new DateTime(DateTime.Now.Year,nearestKey,MonthDayBuckets[nearestKey]); // or whatever the year it needs to be represent
Though above query would get you the result, I would suggest you define a structure to store the Range itself, rather than using Dictionary
For example,
public class Range
{
public MonthDate StartRange{get;set;}
public MonthDate EndRange{get;set;}
public Range(MonthDate startRange,MonthDate endRange)
{
StartRange = startRange;
EndRange = endRange;
}
}
public class MonthDate
{
public MonthDate(int month,int date)
{
Month = month;
Date = date;
}
public int Month{get;set;}
public int Date{get;set;}
//Depending on if your Ranges are inclusive or not,you need to decide how to compare
public static bool operator >=(MonthDate source, MonthDate comparer)
{
return source.Month>= comparer.Month && source.Date>=comparer.Date;
}
public static bool operator <=(MonthDate source, MonthDate comparer)
{
return source.Month<= comparer.Month && source.Date<=comparer.Date;
}
}
Now you could define ranges as
var dateRanges = new Range[]
{
new Range(new MonthDate(12,31),new MonthDate(3,31)),
new Range(new MonthDate(3,31),new MonthDate(6,30)),
new Range(new MonthDate(6,30),new MonthDate(12,31)),
};
var result = dateRanges.First(x=>x.StartRange <= new MonthDate(endMonth.Month,endMonth.Day) && x.EndRange >= new MonthDate(endMonth.Month,endMonth.Day));

Get date of last seven days

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;
}

How can I get a collection of months between two Dates?

Below is my code. I am only getting the difference between two dates, but I want the name of that month which comes between the from and to dates.
public static int GetMonthsBetween(DateTime from, DateTime to)
{
if (from > to) return GetMonthsBetween(to, from);
var monthDiff = Math.Abs((to.Year * 12 + (to.Month - 1)) - (from.Year * 12 + (from.Month - 1)));
if (from.AddMonths(monthDiff) > to || to.Day < from.Day)
{
return monthDiff - 1;
}
else
{
return monthDiff;
}
}
Based on your code you could substract the month difference from the "to" DateTime to get DateTime difference from your input.
public static List<DateTime> GetMonthsBetween(DateTime from, DateTime to)
{
if (from > to) return GetMonthsBetween(to, from);
var monthDiff = Math.Abs((to.Year * 12 + (to.Month - 1)) - (from.Year * 12 + (from.Month - 1)));
if (from.AddMonths(monthDiff) > to || to.Day < from.Day)
{
monthDiff -= 1;
}
List<DateTime> results = new List<DateTime>();
for (int i = monthDiff; i >= 1; i--)
{
results.Add(to.AddMonths(-i));
}
return results;
}
To get the name of the month just format the DateTime to "MMM".
var dts = GetMonthsBetween(DateTime.Today, DateTime.Today.AddMonths(5));
foreach (var dateTime in dts)
{
Console.WriteLine(dateTime.ToString("MMM"));
}
If you want the names of all months between two dates, use something like this:
var d1 = new DateTime(2015,6,1);
var d2 = new DateTime(2015,9,1);
var monthlist = new List<string>();
string format = d1.Year == d2.Year ? "MMMM" : "MMMM yyyy";
for (var d = d1; d <= d2; d = d.AddMonths(1))
{
monthlist.Add(d.ToString(format));
}
The full list is now in monthlist - you will want to return that from your method.
Assuming you're using Java and JodaTime there are several flaws in your code.
You cant use from > to to evaluate if a date is after an other. Use from.isAfter(to) instead.
JodaTime already supplies a method to calculate the amount of whole months between two given Dates Months.monthsBetween(start,end).
With the calculated month difference you can instantiate a new DateTime object that holds a date in your desired month and output its name via yourNewDateTimeObject.month().getAsText().
edit: Just found out you're using C# so ignore my text above this. Below here I will try to answer your question in C#.
Why dont you just subtract the from from the to date and obtain your difference?
The resulting TimeSpan can be used to determine the amount of whole months between your two given dates.
To obtain the resulting month name you could use yourDateTime.ToString("MMMM");

How can I make this LINQ query cleaner?

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 );

Categories