Get Month name and year from list - c#

I am trying to retrieve the month name and the year using LINQ from a list that has 2 properties without repeating the name of the months and the year.
public class Record
{
public int Id { get; set; }
public DateTime Date { get; set; }
}
DateTime d1 = new DateTime(2015, 1, 14);
DateTime d2 = new DateTime(2016, 3, 12);
DateTime d3 = new DateTime(2016, 4, 17);
DateTime d4 = new DateTime(2015, 5, 19);
DateTime d5 = new DateTime(2016, 6, 10);
List<Record> dates = new List<Record>
{
new Record { Id= 1, Date = d1 },
new Record { Id= 2, Date = d2 },
new Record { Id= 3, Date = d3 },
new Record { Id= 4, Date = d4 },
new Record { Id= 5, Date = d5 }
};
//Month should be in string format (January,June, etc)
// Get Year and Months from that list withour repeating the names
//List<string> months =
//List < string > years =

For months and using Linq:
List<string> months = dates.Select(d => d.Date.ToString("MMMM"))
.Distinct()
.ToArray();
Information on the ToStirng format for the month name can be found on MSDN here.
and for years:
List<string> years = dates.Select(d => d.Date.Year.ToString())
.Distinct()
.ToArray();
Although it is unclear how you want the list of years to look.
Information on Distinct can be found on MSDN here.

With an extension method to simplify it (taken from here):
static class DateTimeExtensions
{
public static string ToMonthName(this DateTime dateTime)
{
return CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(dateTime.Month);
}
}
You can do this:
var months = dates.Select(r => r.Date.ToMonthName())
.Distinct();
var years = dates.Select(r => r.Date.Year)
.Distinct();
Note that I've given years as int here, if you want strings, then just add ToString().

Related

Insert Data into middle of range

I have the following model
public class DailyRoutine
{
public int Id { get; set; }
public DateTime Date { get; set; }
public string Description { get; set; }
}
Scenario:
When is created at initial time with 5 records which means 5 entries are entered for each day. Take an example May 1 to May 5 of 2017. Description have any string.
User can add a new record in the middle so that the following records should be moved and changed to next days.
Expected Output:
Example, user can give a date and description in input and submit. If the input date is '5/3/2017' (May 3), the entry should be added after May 2 record and the existing May 3 record changed to May 4, May 4 to May 5 etc. So the out is like May 1 to May 6 and the given input is updated on May 3.
Please help me to this with out degrading performance
This approach will work:
List<DailyRoutine> d = new List<DailyRoutine>()
{
new DailyRoutine() { Date = new DateTime(2017, 7, 1)},
new DailyRoutine() { Date = new DateTime(2017, 7, 2)},
new DailyRoutine() { Date = new DateTime(2017, 7, 3)},
new DailyRoutine() { Date = new DateTime(2017, 7, 4)},
new DailyRoutine() { Date = new DateTime(2017, 7, 5)}
};
DailyRoutine newDr = new DailyRoutine() { Date = new DateTime(2017, 7, 2) };
DailyRoutine oldDr = d.Where(dr => dr.Date == newDr.Date).FirstOrDefault();
if (oldDr != null)
{
int idx = d.IndexOf(oldDr);
List<DailyRoutine> changeList = d.Where((dr, i) => i >= idx).ToList();
foreach (DailyRoutine i in changeList)
{
i.Date = i.Date.AddDays(1);
}
d.Insert((int)idx, newDr);
}
else
{
d.Add(newDr);
}

Groupby multiple date properties by month and year in LINQ

I need to group by multiple properties by month and year in C# LINQ
This is my code:
public class Class1
{
public Nullable<DateTime> dt1 { get; set; }
public Nullable<DateTime> dt2 { get; set; }
}
Class1 obj1 = new Class1 { dt1 = new DateTime(2012, 11, 12),
dt2 = new DateTime(2012, 12, 12) };
Class1 obj2 = new Class1 { dt1 = new DateTime(2012, 11, 12),
dt2 = new DateTime(2012, 12, 12) };
Class1 obj3 = new Class1 { dt1 = null, dt2 = new DateTime(2012, 10, 12) };
Class1 obj4 = new Class1 { dt1 = new DateTime(2012, 10, 12), dt2 = null };
Class1 obj5 = new Class1 { dt1 = null, dt2 = new DateTime(2012, 11, 12) };
Class1 obj6 = new Class1 { dt1 = new DateTime(2013, 10, 12), dt2 = null };
List<Class1> listGoogleTimezone = new List<Class1>
{
obj1,
obj2,
obj3,
obj4,
obj5,
obj6
};
My required result to be like this
MONTH YEAR COUNT
OCT 2012 2
NOV 2012 3
DEC 2012 2
Help me out
You need to collect all the dates using SelectMany (in this particular case filter out the null values) and then do the typical GroupBy / Count projection:
var result = listGoogleTimezone
.SelectMany(x => new[] { x.dt1, x.dt2 }.Where(dt => dt != null).Select(dt => dt.Value))
.GroupBy(dt => new { dt.Month, dt.Year })
.OrderBy(g => g.Key.Year).ThenBy(g => g.Key.Month) // optional
.Select(g => new
{
g.Key.Month,
g.Key.Year,
Count = g.Count()
}).ToList();
If you have MoreLinq referenced you can use make Ivan's answer cleaner by using CountBy:
var result = listGoogleTimezone
.SelectMany(x => new[] { x.dt1, x.dt2 }
.Where(dt => dt != null)
.Select(dt => dt.Value))
.CountBy(x => new { Year = x.Year, Month = x.Month });
This will can you an IEnumerable<KeyValuePair<TKey, int>> where the TKey is an anonymous type with Year and Month and the int is the count.

Group the results by calender week and get the average

I have a data set as follows
Name Start Date End Date Percentage
A 1/1/2015 31/3/2015 50
B 1/1/2015 30/6/2015 100
C 1/4/2015 30/6/2015 50
I want to group this data set by calender week and need to get the average percentage
The results should be as follows
Year Week Average
2015 1 ---
2015 2 ---
What will be the optimum linq query to get this result. I have tried following code. But the values are not correct
var grouped = result.Select(p =>
new
{
Week = CultureInfo.InvariantCulture.Calendar.GetWeekOfYear
(p.StartDate, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday),
Year = p.StartDate.Year,
EndDate = p.EndDate,
percentage = p.percentage
})
.GroupBy(x => new { x.Year, x.Week })
.OrderBy(x=>x.Key.Year).ThenBy(x=>x.Key.Week)
.Select((g, i) => new
{
g.Key.Year,
g.Key.Week,
Sum = g.Average(t => t.percentage)
});
public class Program
{
public static void Main()
{
var input = new List<P>
{
new P { StartDate = new DateTime(2015, 1, 1), EndDate = new DateTime(2015, 3, 31), Week = DayOfWeek.Monday, Year = 2015, percentage = 50 },
new P { StartDate = new DateTime(2015, 1, 1), EndDate = new DateTime(2015, 6, 30), Week = DayOfWeek.Monday, Year = 2015, percentage = 100 },
new P { StartDate = new DateTime(2015, 4, 1), EndDate = new DateTime(2015, 6, 30), Week = DayOfWeek.Monday, Year = 2015, percentage = 50 },
};
var result = input.SelectMany(p =>
{
var weeks = Enumerable.Range(0, (p.EndDate - p.StartDate).Days / 7)
.Select(i => CultureInfo.InvariantCulture.Calendar.AddWeeks(p.StartDate, i))
.Select(i => CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(i, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday));
return weeks.Select(w => new { Week = w, Percentage = p.percentage / weeks.Count() });
})
.GroupBy(x => x.Week)
.Select(g => new { Week = g.Key, Average = g.Average(t => t.Percentage) });
}
}
public class P
{
public DateTime StartDate;
public DateTime EndDate;
public DayOfWeek Week;
public int Year;
public double percentage;
}
I haven't used Year here, but you can get the idea and add year yourself.

A better way to fill a two-dimensional array with date and zodiac sign

I'm working on the following problem:
I want to fill a two-dimensional [365,2] Array. The first value is supposed to hold the date: starting with January 1st and ending with December the 31st. The second value is supposed to hold the corresponding Zodiac Sign to each date:
e.g. array[0, 0] holds 101 and array[0, 1] holds Aries and so on.
I've written a function:
public static void fill_array(string[,] year_zodiac, int days, string zodiac, string startdate, int starting_day)
{
//function to fill array with date and zodiac
int startdate_int = 0;
for (int i = starting_day; i <= days; i++)
{
year_zodiac[i, 0] = startdate;
year_zodiac[i, 1] = zodiac;
startdate_int = Int32.Parse(startdate);
startdate_int++;
startdate = startdate_int.ToString();
}
and call it like this:
fill_array(main_array, 18, "Aries", "101", 0);
This has to be done for every Zodiac sign. I circumvented the month break problem by simply calling fill_array twice (i.e. I call it once for the part of Aries in December and once for the part of aries in January).
This Solution works, but it seems incredibly crude to me.
Can anybody give me some pointers towards a more elegant solution?
Here is a class that does what you want, it has been tested. I did not fill out all signs on the first example, but when I re-factored it I did. I suggest you test this well as I only tested a few cases and might have missed some edge cases.
As has been pointed out to me by #ClickRick, I did start with his array/enum design, but found that lacking when using Linq and moved to a List. I also had to fix his data array so it would compile. I'm giving credit as is due.
public class Signs
{
static List<string> SignList = new List<string>() { "Aquarius", "Pisces", "Aries", "Taurus", "Not Found"};
static DateTime[] startDates = {
new DateTime(DateTime.Now.Year,1,21),
new DateTime(DateTime.Now.Year,2,20),
new DateTime(DateTime.Now.Year,3,21),
new DateTime(DateTime.Now.Year,4,21),
new DateTime(DateTime.Now.Year,12,31) };
static public string Sign(DateTime inDate)
{
return SignList.Zip(startDates, (s, d) => new { sign = s, date = d })
.Where(x => (inDate.Month*100)+inDate.Day <= (x.date.Month*100)+x.date.Day)
.Select(x => x.sign)
.First();
}
}
re-factored (this is clearer with the example above first)
public class Signs
{
static List<string> SignList = new List<string>() {
"Capricorn", "Aquarius", "Pisces", "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Not Found" };
static List<int> startDates = new List<int>() {
// month * 100 + day of month
120, 219, 320, 420, 521, 621, 722, 821, 923, 1023, 1122, 1222, 1232, 9999 // edge marker
};
static public string Sign(DateTime inDate)
{
return SignList[startDates.TakeWhile(d => (inDate.Month*100)+inDate.Day > d).Count()];
}
}
Why do you specifically want an array? Surely a more sophisticated data structure would help to encapsulate the functionality and help you to isolate your remaining code from knowing about the workings of it, and would also let you take account in the future of more subtle variations in the exact dates where they vary by year if you ever want to do that.
For example:
public class SO23182879
{
public enum StarSign
{
Aquarius, Pisces, Aries, Taurus, Gemini, Cancer,
Leo, Virgo, Libra, Scorpio, Sagittarius, Capricorn
};
static DateTime[] starSignStartDates = new DateTime[]
{
new DateTime(DateTime.Now.Year, 1, 20),
new DateTime(DateTime.Now.Year, 2, 19),
new DateTime(DateTime.Now.Year, 3, 21),
new DateTime(DateTime.Now.Year, 4, 20),
new DateTime(DateTime.Now.Year, 5, 21),
new DateTime(DateTime.Now.Year, 6, 21),
new DateTime(DateTime.Now.Year, 7, 23),
new DateTime(DateTime.Now.Year, 8, 23),
new DateTime(DateTime.Now.Year, 9, 23),
new DateTime(DateTime.Now.Year, 10, 23),
new DateTime(DateTime.Now.Year, 11, 22),
new DateTime(DateTime.Now.Year, 12, 22),
new DateTime(DateTime.Now.Year, 1, 20),
};
private class StarSignDateRange
{
public StarSign Sign { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
private List<StarSignDateRange> signStartDates = new List<StarSignDateRange>();
public SO23182879()
{
int date = 0;
foreach (StarSign sign in Enum.GetValues(typeof(StarSign)))
{
signStartDates.Add(new StarSignDateRange
{
Sign = sign,
StartDate = starSignStartDates[date],
EndDate = starSignStartDates[date + 1]
});
++date;
}
}
public StarSign Sign(DateTime date)
{
return signStartDates.First(
sd => date.Month == sd.StartDate.Month && date.Day >= sd.StartDate.Day ||
date.Month == sd.EndDate.Month && date.Day < sd.EndDate.Day
).Sign;
}
public void Test()
{
IList<DateTime> testDates = new List<DateTime>
{
new DateTime(2014,1,1),
new DateTime(2014,1,19),
new DateTime(2014,1,20),
new DateTime(2014,4,19),
new DateTime(2014,4,20),
new DateTime(2014,12,21),
new DateTime(2014,12,22),
new DateTime(2014,12,31),
};
foreach (DateTime d in testDates)
Console.WriteLine(string.Format("{0} is in {1}", d, Sign(d)));
}
}
As you'll see, I have completed the code which I had started earlier, and added a Test() method for you to see that the edge conditions work. I am grateful to Hogan for pointing out the "year zero" problem and other similar "gotchas" in my earlier sketch.

Grouping the name in the manner of years and months from a list

i had created a class with two properties one of datetime datatype and other of string.
public class name1
{
public DateTime dob { get; set; }
public string name { get; set; }
}
and then page load created a list
DateTime d1 = new DateTime(1989, 5, 12);
DateTime d2 = new DateTime(1989, 5, 26);
DateTime d3 = new DateTime(1989, 3, 12);
DateTime d4 = new DateTime(1986, 3, 21);
DateTime d5 = new DateTime(1990, 8, 19);
List<name1> randmoptn=new List<name1>();
name1 n1=new name1();
n1.name="rachit";
n1.dob=d1;
name1 n2=new name1();
n2.name="abhinav";
n2.dob=d2;
name1 n3=new name1();
n3.name="mandeep";
n3.dob=d3;
name1 n4=new name1();
n4.name="jasmeet";
n4.dob=d4;
name1 n5=new name1();
n5.name="rajat";
n5.dob=d5;
randmoptn.Add(n1);
randmoptn.Add(n2);
randmoptn.Add(n3);
randmoptn.Add(n4);
randmoptn.Add(n5);
and now i wanted
desired output should be
year 1986
month 3
21/3/1986 jasmeet
year 1989
month 3
12/3/1989 mandeep
month 5
12/5/1989 rachit
26/5/1989 abhinav
year 1990
month 8
19/8/1990 rajat
If your desired output is as below
Year
Month
DoB with Name
then do something like this
foreach item in YourList
{
print item.dob.year; // this will return year in integer format
print item.dob.month; // this will return month in integer format
print item.dob; // this will return DoB as dateformat, do whatever formating you want
print item.name; // this will return Name as String
}
print the DoB and Name in the same line if you want
var q=from x in randmoptn
group x by new { Year=x.dob.Year,Month=x.dob.Month} into month
group month by month.Key.Year into year
select new { Year=year.Key,Months=year.OrderBy (y =>y.Key.Month )};
foreach (var year in q) {
Console.WriteLine("year {0}",year.Year);
foreach (var month in year.Months) {
Console.WriteLine("month {0}",month.Key.Month);
foreach (var item in month) {
Console.WriteLine("{0:dd/MM/yyyy},{1}",item.dob,item.name);
}
}
}

Categories

Resources