Fetch month and Year Inside a List using Linq - c#

I have list like this
public class Result
{
public string id { get; set; }
public string name { get; set; }
public string startDate { get; set; } //smaple date 2014-03-31T12:30:03
}
List<Result>
I want to fetch all distinct month comes inside this list .
I have tried something like this
List<string> monthNamesList = eventListResponse.result.Select(s => Convert.ToDateTime(s.startDate).ToString("MMMM")).Distinct().ToList();
And it does the job, Only Problem is that if the list comtains two elements
2014-03-31T12:30:03
2013-03-31T12:30:03
My code will return only one month, where I want to get it like 2014 March and 2013 March.
So I created a new model class with year and month
public class MonthYearMOdel
{
public string month;
public string year;
}
Can any one point out how I can fetch distinct months from my first list and store in List<MonthYearMOdel>.
Where 2014 March and 2013 March both will be stored.

try this :
List<MonthYearMOdel> monthNamesList = eventListResponse.result.Select(s => new
{
M = Convert.ToDateTime(s.startDate).ToString("MMMM"),
Y = Convert.ToDateTime(s.startDate).ToString("yyyy")
})
.Distinct()
.Select(u => new MonthYearMOdel()
{
month = u.M,
year = u.Y,
})
.ToList();

Simple way (each string contains month and year):
List<string> monthNamesList = eventListResponse.result.Select(s => Convert.ToDateTime(s.startDate).ToString("yyyy MMMM")).Distinct().ToList();
With MonthYearModel:
public class MonthYearModel
{
public string month;
public string year;
public MonthYearModel(string dateTime)
{
var date = Convert.ToDateTime(dateTime);
this.month = date.ToString("MMMM");
this.year = date.ToString("yyyy");
}
public bool Equals(object arg)
{
var model = arg as MonthYearModel;
return (model != null) && model.month == this.month && model.year == this.year;
}
public int GetHashCode()
{
return (month.GetHashCode() * 397) ^ year.GetHashCode();
}
}
List<MonthYearModel> = eventListResponse.result.Select(s => new MonthYearModel(s.startDate)).Distinct().ToList();

Related

Check if Sequence of Year and Month Is Complete (Without Gaps) Given Start and End

The task is pretty easy and I have an iterative solution but I am thinking that there is probably a more efficient of cleaner solution. I have a list of objects which contain a year and a month property. I want to make sure every month from a given start year + month to a given end year + month is covered.
This is my current solution:
for (int year = startYear; year <= endYear; year++)
{
int startM = year == startYear ? startMonth : 1;
int endM = year == endYear ? endMonth : 12;
for (int month = startM; month <= endM; month++)
{
if (!someList.Any(x => x.Year == year && x.Month == month))
throw new Exception("List contains gaps.");
}
}
The extension methods for DateTime below create a time series that can be used to solve your problem by joining with Linq - works for daily gaps as well.
This is a more extensible solution, not necessarily more efficient given it uses Linq vs interation.
Usage
void Main()
{
var startDate = new DateTime(2014, 1, 1);
var months = 36;
//sample w/ 3 month gaps
var monthsWithGaps = Enumerable.Range(0, months).Where(i=> i % 3 == 0)
.Select(h=> startDate.AddMonths(h))
.Dump();
//usage
startDate.GetTimeSlices(monthsWithGaps.Last(), true)
.Where(d=> d.DayOfMonth == 1)
.GroupJoin(monthsWithGaps, slice => slice.Date, set => set, (slice, set) =>
new {
slice.Date,
set
})
.Where(result => !result.set.Any()) //identify gaps
.Count(result => !result.set.Any()) //count gaps
.Dump();
}
Extension Method Implementation
public static class DateTimeExtensions
{
public static List<TimeSlice> GetTimeSlices(this DateTime date, int numberOfDays)
{
int count = 1;
return Enumerable.Range(0, numberOfDays).Select(x => date.AddDays(x)).Select(x => new TimeSlice
{
Date = x.Date,
Year = x.Year,
MonthOfYear = x.Month,
MonthOfSet = ((count - 1) / 30) + 1,
WeekOfSet = ((count - 1) / 7) + 1,
DayOfMonth = x.Day,
DayOfSet = count++
}).ToList();
}
public static List<TimeSlice> GetTimeSlices(this DateTime date, DateTime endDate, bool includeEndDate = true)
{
return GetTimeSlices(date, (endDate - date).Days + (includeEndDate ? 1 : 0));
}
public class TimeSlice
{
public DateTime Date { get; set; }
public int Year { get; set; }
public int MonthOfYear { get; set; }
public int MonthOfSet { get; set; }
public int WeekOfSet { get; set; }
public int DayOfMonth { get; set; }
public int DayOfSet { get; set; }
}
}

Extracting information out of a dictionary...An alternative to if/else statements

I have a dictionary that looks like this:
Dictionary<string, DateTime> Seasons = new Dictionary<string, DateTime>
{
{ "WINTER_START", Date1},
{ "WINTER_END", Date2 },
{ "SUMMER_START", Date3 },
{ "SUMMER_END", Date4 }
};
Let's say I have today's date var today = DateTime.Today.Date and I want to know if today's date falls in winter or summer. I've done this using an If/Else statement, but something inside me tells me there is a better way..
string currentSeason = "";
if (today >= Seasons["WINTER_START"] && today <= Seasons["WINTER_END"])
{
currentSeason = "WINTER";
}
else if (today >= Seasons["SUMMER_START"] && today <= Seasons["SUMMER_END"])
{
currentSeason = "SUMMER";
}
Not sure if you are locked down to using a Dictionary for this, but i think this is a friendlier version and how i would've tackled the problem:
void Main()
{
var seasons = new List<Season>
{
new Season("Winter", Date1, Date2),
new Season("Summer", Date3, Date4)
};
var today = DateTime.Today;
// null if no matching season was found
string currentSeason = seasons.FirstOrDefault(season => season.InSeason(today))?.Name;
}
public class Season
{
public Season(string name, DateTime start, DateTime end)
{
Name = name;
Start = start;
End = end;
}
public string Name { get; set; }
public DateTime Start { get; set; }
public DateTime End { get; set; }
public bool InSeason(DateTime input)
{
return input >= Start && input <= End;
}
}
Also a minor remark: Current_Season is not a good variable name for a local variable. This might help you improve your naming.

Group By ranges in Linq

Thanks all, I am sorry but I am very beginner. Let me explain it in details: I have a table "theTable" and has two columns "Date" in DateTime and "Value" in double.
What I want as a result: 1.check which date is the minimum 2. Take this as firstdat
I have tried but seems not working
from cf in theTable.
group t by t.Month.Month / 8 + t.Month.Year into g
select new { sum = g.Sum(x=>x.Amount) ,year = g.Key}
You can try to do something like that:
private static void Main(string[] args)
{
var columns = new List<Column>();
var dateRanges = new List<DateRange>()
{
new DateRange(new DateTime(2013, 09, 03), new DateTime(2014, 09, 03))
};
var result = columns.GroupBy(
column => dateRanges.FirstOrDefault(dr => dr.IsInRange(column.Date)),
(dr, drc) => new KeyValuePair<DateRange, int>(dr, drc.Sum(v => v.Value)));
}
public struct DateRange
{
public DateRange(DateTime from, DateTime to)
: this()
{
From = from;
To = to;
}
public bool IsInRange(DateTime date)
{
return date >= From && date <= To;
}
public DateTime From { get; private set; }
public DateTime To { get; private set; }
}
public class Column
{
public int Value { get; set; }
public DateTime Date { get; set; }
}
theTable.GroupBy(t => t.Date.Month / 8 + t.Date.Year)
.Select(g => g.Sum(x = > x.Value));
If you take the range 8/1991-7/1992 for instance they will both end up in the same group:
8 / 8 + 1991 = 7 / 8 + 1992 = 1992
myList.Where(x=> x.Date < lastDate && x.Date > firstDate).Sum(x=>x.Value)
Not sure of the answer because the question is not really clear but this is the sum of all the property "Value" of your list which property "Date" is between a date firstDate and a date LastDate.

Data from List<T> split into another List<T> with 2 nested List<T>

I have run into a bit of trouble with this linq query:
I have a list (allStats) filled with data (see allItems class)
the list contains several entries from different days which I need to break down so they are seperated by month and weeknumber like so:
MonthNumber: 1,
List<Weeks> Weeks:
WeekNumber: 1,
List<Days> Days:
PunchedInLate: true,
PunchedOutLate: false,
PunchInDate: 2013-1-1 08:20:10,
PunchOutDate: 2013-1-1 15:00:00
PunchedInLate: true,
PunchedOutLate: false,
PunchInDate: 2013-1-2 08:20:10,
PunchOutDate: 2013-1-2 15:00:00
...
PunchedInLate: true,
PunchedOutLate: false,
PunchInDate: 2013-1-5 08:20:10,
PunchOutDate: 2013-1-5 15:00:00
MonthNumber: 1,
List<Weeks> Weeks:
WeekNumber: 2,
List<Days> Days:
PunchedInLate: true,
PunchedOutLate: false,
PunchInDate: 2013-1-10 08:20:10,
PunchOutDate: 2013-1-10 15:00:00
PunchedInLate: true,
PunchedOutLate: false,
PunchInDate: 2013-1-12 08:20:10,
PunchOutDate: 2013-1-12 15:00:00
PasteBin - Here you can download a sample program so you can run it from your machine
It is essentially an addon to the answer of this question that I am trying to create:
SO - Splitting parts of a List into 2 List's and joining those 2
EDIT: I am sorry, I forgot to mention that I've tried the .ToList() method at the end instead of the cast at the start which produces this error:
Cannot implicitly convert type 'System.Collections.Generic.List<System.Collections.Generic.IEnumerable<TestClass.Weeks>>' to 'System.Collections.Generic.List<TestClass.Weeks>'
public class allItems
{
public DateTime PunchInDate { get; set; }
public DateTime PunchOutDate { get; set; }
public DayOfWeek DayOfWeek { get; set; }
public int WeekNumber { get; set; }
public int MonthNumber { get; set; }
public bool PunchedInLate { get; set; }
public bool PunchedOutLate { get; set; }
}
public class Months
{
public int MonthNumber { get; set; }
public List<Weeks> Weeks { get; set; }
}
public class Weeks
{
public int WeekNumber { get; set; }
public List<Days> Days { get; set; }
}
public class Days
{
public bool PunchedInLate { get; set; }
public bool PunchedOutLate { get; set; }
public DateTime PunchInDate { get; set; }
public DateTime PunchOutDate { get; set; }
public DayOfWeek DayOfWeek { get; set; }
}
And the code:
List<allItems> allStats = getAllStats(userId);
List<Months> stats = new List<Months>();
var asItems =
from item in allStats
group item by new { month = item.MonthNumber } into Month
select new Months()
{
MonthNumber = Month.Key.month,
Weeks = Month.Select(week =>
from weeks in allStats
group weeks by new { week = weeks.WeekNumber } into Week
select new Weeks()
{
//WeekNumber = week.WeekNumber,
WeekNumber = Week.Key.week, // I just noticed that I guess that I
// need this here, so I can group the
Days = Month.Select(days => // days correctly, right?
new Days()
{
PunchedInLate = days.PunchedInLate,
PunchedOutLate = days.PunchedOutLate,
DayOfWeek = days.DayOfWeek,
PunchInDate = days.PunchInDate,
PunchOutDate = days.PunchOutDate
}).ToList()
}).ToList()
};
List<Months> stat = asItems.ToList();
The problem is that your Month.Select(...) doesn't return a List<Weeks>. You can remove the cast and just use:
Week = Month.Select(week =>
... code as before ...
).ToList()
EDIT: Okay, I see what's still wrong. For each week, you're using a query which produces multiple Weeks objects. So this part:
from weeks in allStats
...
select new Weeks() { ... }
is of type IEnumerable<Weeks>() - and that's being used as the body of a projection in Month.Select(week => ...) so you've got a sequence of sequences. It's not clear how you want to turn that into a single list. For example, you could use:
Week = Month.Select(week =>
... code as before ...
).First().ToList()
or:
Week = Month.Select(week =>
... code as before ...
).SelectMany(x => x).ToList()
We simply don't know enough about what you're trying to achieve.
Looks to me like you have an issue where you're casting:
List<allItems> allStats = getAllStats(userId);
List<Months> stats = new List<Months>();
var asItems =
from item in allStats
group item by new { month = item.MonthNumber } into Month
select new Months()
{
MonthNumber = Month.Key.month,
Weeks = Month.Select(week => //Don't cast here, put a ToList() at the end.
from weeks in allStats
group weeks by new { week = weeks.WeekNumber } into Week
select new Weeks()
{
WeekNumber = week.WeekNumber,
Days = Month.Select(days =>
new Days()
{
PunchedInLate = days.PunchedInLate,
PunchedOutLate = days.PunchedOutLate,
DayOfWeek = days.DayOfWeek,
PunchInDate = days.PunchInDate,
PunchOutDate = days.PunchOutDate
}).ToList()
}).ToList(); //*** ToList() added here ***
};
List<Months> stat = asItems.ToList();
Looks like you're missing a ToList after yout 'Months' select:
List<allItems> allStats = getAllStats(userId);
List<Months> stats = new List<Months>();
var asItems =
from item in allStats
group item by new { month = item.MonthNumber } into Month
select new Months()
{
MonthNumber = Month.Key.month,
Weeks = Month.Select(week =>
from weeks in allStats
group weeks by new { week = weeks.WeekNumber } into Week
select new Weeks()
{
WeekNumber = week.WeekNumber,
Days = Month.Select(days =>
new Days()
{
PunchedInLate = days.PunchedInLate,
PunchedOutLate = days.PunchedOutLate,
DayOfWeek = days.DayOfWeek,
PunchInDate = days.PunchInDate,
PunchOutDate = days.PunchOutDate
}).ToList()
}).ToList() // <-- here
};
List<Months> stat = asItems.ToList();
Note that you don't need a cast in that case either.

Generating a value dependent on another value with AutoPoco

I'm using the great .NET library AutoPoco for creating test and Seed Data.
In my model I have 2 date properties, StartDate and EndDate.
I want the EndDate to be 3 hours after the start Date.
I've created a custom Data source for autopoco below that returns a random Datetime between a min and max date
class DefaultRandomDateSource : DatasourceBase<DateTime>
{
private DateTime _MaxDate { get; set; }
private DateTime _MinDate { get; set; }
private Random _random { get; set; }
public DefaultRandomDateSource(DateTime MaxDate, DateTime MinDate)
{
_MaxDate = MaxDate;
_MinDate = MinDate;
}
public override DateTime Next(IGenerationSession session)
{
var tspan = _MaxDate - _MinDate;
var rndSpan = new TimeSpan(0, _random.Next(0, (int) tspan.TotalMinutes), 0);
return _MinDate + rndSpan;
}
}
But in AutoPoco's configuration how can i get my EndDate to be say, 3 hours after the autogenerated start Date?
Here's the autopoco config
IGenerationSessionFactory factory = AutoPocoContainer.Configure(x =>
{
x.Conventions(c => { c.UseDefaultConventions(); });
x.AddFromAssemblyContainingType<Meeting>();
x.Include<Meeting>()
.Setup((c => c.CreatedBy)).Use<FirstNameSource>()
.Setup(c => c.StartDate).Use<DefaultRandomDateSource>(DateTime.Parse("21/05/2011"), DateTime.Parse("21/05/2012"));
});
If I am correctly understanding the problem you need: to set EndDate from StartDate. I had to create a new DataSource and get current item which we are constructing and read value from it. I haven't thoroughly checked but it might fail if StartDate is set after EndDate (though I think the properties are set in the order they are setup, read source code for AutoPoco). Also I am using latest version from CodePlex as of today (20 Feb 2012).
public class MeetingsGenerator
{
public static IList<Meeting> CreateMeeting(int count)
{
var factory = AutoPocoContainer.Configure(x =>
{
x.Conventions(c => { c.UseDefaultConventions(); });
x.Include<Meeting>()
.Setup((c => c.CreatedBy)).Use<FirstNameSource>()
.Setup(c => c.StartDate).Use<DefaultRandomDateSource>
(DateTime.Parse("21/May/2012"),
DateTime.Parse("21/May/2011"))
.Setup(c => c.EndDate).Use<MeetingEndDateSource>(0, 8);
});
return factory.CreateSession().List<Meeting>(count).Get();
}
}
public class Meeting
{
public string CreatedBy { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
public class MeetingEndDateSource : DatasourceBase<DateTime>
{
private int mMin;
private int mMax;
private Random mRandom = new Random(1337);
public MeetingEndDateSource(int min, int max)
{
mMin = min;
mMax = max;
}
public override DateTime Next(IGenerationContext context)
{
var node = (TypeGenerationContextNode)((context.Node).Parent);
var item = node.Target) as Meeting;
if (item == null)
return DateTime.Now;
return item.StartDate.AddHours(mRandom.Next(mMin, mMax + 1));
}
}
class DefaultRandomDateSource : DatasourceBase<DateTime>
{
private DateTime _MaxDate { get; set; }
private DateTime _MinDate { get; set; }
private Random _random = new Random(1337);
public DefaultRandomDateSource(DateTime MaxDate, DateTime MinDate)
{
_MaxDate = MaxDate;
_MinDate = MinDate;
}
public override DateTime Next(IGenerationContext context)
{
var tspan = _MaxDate - _MinDate;
var rndSpan = new TimeSpan(0
, _random.Next(0, (int)tspan.TotalMinutes)
, 0);
return _MinDate + rndSpan;
}
}

Categories