Hey i got a list having many records inside, there are 3 DateTime inside (nullable). What i need is to get a LINQ expression determining which one of this three is the latest then select that record and check again. Here is the code how i created the List.
List<ActivityJoinUserTopicVote> ActivityList = new List<ActivityJoinUserTopicVote>();
foreach (var p in postdate)
{
ActivityList.Add(new ActivityJoinUserTopicVote(p.R_Posted, p.Forum_id, p.R_Posted_By, p.R_Message, p.Reply_ID, p.Topic_ID, p.User));
}
foreach (var v in votedate)
{
ActivityList.Add(new ActivityJoinUserTopicVote(v.data, v.Topic_ID, v.vote, v.Member_ID, v.Vote_Member_ID));
}
foreach (var t in topicdate)
{
ActivityList.Add(new ActivityJoinUserTopicVote(t.T_date, t.Forum_id, t.T_Originator, t.T_Replies, t.T_ukaz, t.Topic_ID, t.User, t.T_Url, t.T_subject));
}
return ActivityList;
Before returning the ActivityList i need it to determine which one was the most recent and sort it this way. Maybe i could do this somehow while creating the list? The problem is i got 3 different columns inside i need to check in (R_Posted, data and T_date)
Get the maximum of 3 dates using ticks, and sort by that value:
Helper:
private long MaxOfThreeDate(DateTime? date1, DateTime? date2, DateTime? date3)
{
long max1 = Math.Max(date1.GetValueOrDefault().Ticks, date2.GetValueOrDefault().Ticks);
return Math.Max(max1, date3.GetValueOrDefault().Ticks);
}
Usage:
return ActivityList.OrderByDescending(x => MaxOfThreeDate(x.data, x.R_Posted, x.T_date)).ToList();
replace
return ActivityList;
with
return ActivityList.OrderByDescending(x => x.data.HasValue ? x.data : ( x.R_Posted.HasValue ? x.R_Posted : x.T_date)).ToList();
or
return ActivityList.OrderByDescending(x => new DateTime?[]{ x.data, x.R_Posted, x.T_date}.Max()).ToList();
to return the ActivityList sorted by the date fields descending
You can get the most recent one using this
var list = ActivityList.OrderByDescending(x=>x.Date).First();
So, I have a Basket here containing BasketItems. Each of this Items has a MenuDate. Now I want to create Orders out of these Items, where the condition is: Every Order should have the Items of one week (starts on monday here) in it.
Now I know, I can create multiple foreach-constructs, maybe even a while(true) where I check all the dates and everytime a monday is hit, but this all seems a bit hacky to me. Optimal it would be, to have a LINQ-Query somehow like this Code I'Ve written, but this does not give the expected values back:
var basketItems = currBasket.Items.OrderBy(y => y.MenuDate).ToList();
if (basketItems.Count() > 0)
{
List<List<BasketDay>> listOfItemsPerWeek = basketItems
.GroupBy(p => p.MenuDate >= basketItems.First().MenuDate.StartOfWeek(DayOfWeek.Monday)
&& p.MenuDate < basketItems.First().MenuDate.StartOfWeek(DayOfWeek.Monday).AddDays(7))
.Select(g => g.ToList())
.ToList();
}
where startofweek is an extensionmethod I use giving me the previous monday of a given datetime (or monday if its monday, for sure).
which gives me a number of List<BasketDay> containing all menus out of currBasket.Items whose MenuDates are lying inbetween monday and sunday (23:59:59).
I've tried everything so far and now I am out of ideas and hope you could help me out here.
Beste regards
edit:
To make it clear, assume we're in the food branche here: BasketDay contains a menu for a day.
Here's an example use case (removed year for its not that important here):
Customer adds menus to a basket for the following dates in january:
monday 01.01.,
wednesday 03.01.
friday 05.01.
sunday 07.01.
monday 08.01.
tuesday 09.01.
so his basket contains 6 items.
Now what I need is: To Separate this one List of BasketDay instances into weekly List<BasketDay>.
The LINQ-Query above should do this for me. After that, I could create separate order-objects like this:
foreach(weekOrder in listOfItemsPerWeek){...}
where listOfItemsPerWeek contains 2 List<BasketDay>, one with 4 items (01.01. - 07..01.) and the second with 2 items (08.01. and 09.01.)
hope this makes it more clear.
I recommend to declare a temporary variable that can be used to group by, e.g. calendar week might be a good choice here.
var calendar = CultureInfo.CurrentCulture.Calendar;
var items = (from item in basketItems
let calendarWeek = calendar.GetWeekOfYear(item.MenuDate, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday)
group item by calendarWeek into baskets
select baskets);
Edit: CalendarWeekRule selects how the first week of the year is determined. This differs from culture to culture and I chose the ISO definition which is used here in Germany. Since you are only interested in grouping from monday to sunday the absolut value might be irrelevant to you and so you can chose whatever rule you like. If you prefer Lambda the code looks like this:
var calendar = CultureInfo.CurrentCulture.Calendar;
var items = basketItems.GroupBy(i => calendar.GetWeekOfYear(i.MenuDate, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday));
You need to calculate week of the year(week number) for each item then you can simply group on week of the year. Here is how i will do it, i have also assumed that we have multiple year date range, so the grouping will be on year and week number, i have set the starting of the week to Monday you can change it if you like
public class Item
{
public string Name { get; set; }
public DateTime ItemDate { get; set; }
}
public class ItemWithWeekInfo
{
public int Year { get; set; }
public int WeekOfTheYear { get; set; }
public Item Item { get; set; }
}
public class Order
{
public int Year { get; set; }
public int WeekOfTheYear { get; set; }
public List<Item> Items { get; set; }
}
DateTimeFormatInfo dfi = DateTimeFormatInfo.CurrentInfo;
Calendar cal = dfi.Calendar;
var groups = items.Select(p => new ItemWithWeekInfo
{
Year = p.ItemDate.Year,
WeekOfTheYear = cal.GetWeekOfYear(p.ItemDate, dfi.CalendarWeekRule, DayOfWeek.Monday),
Item = p
}).GroupBy(p => new { p.Year , p.WeekOfTheYear}).Select(p=>new Order
{
Year = p.Key.Year,
WeekOfTheYear = p.Key.WeekOfTheYear,
Items = p.Select(x=> x.Item).ToList()
}).ToList();
This will work if you have the list already, but if you are making the database call you will not be able to do this, then you will need to use some database supported function to calculate the year and week number.
I am trying to compare a list AAA that contain Date with a range of dates. I want to see if any of the range of date is present in the list or not. If the date is present I copy the list items to another list BBB else I add empty values to the list BBB.
The problem I am having is that with my actual code, is I don`t know how not pass through the false statement of the while loop, till it reaches the end of the comparison.
With the code below, it is passing both the true and false in the while loop, which is falsifying the required result. The result I am obtaining is for every time that is present, I am having the same time as false. In short, lets say the list contains the date 6/5/2010, and the range of date is 4/5/2010 to 7/5/2010. so I will have an item created in the true part and AN ITEM CREATED INTHE FALSE PART, which is wrong. The date present can either be in true or false part. Not both, such that I have two items bing created!
How can I achieve the right result? Any other method or suggetsion please.
My code is as follows:
DateTime StartDate;
DateTime EndDate;
Datetime tempDate = StartDate;
List<DateTime> dateToEvaluate;
bool TimeIsPresent = false;
foreach (var tempItem in TaskList)
{
while (EndDate.AddDays(1) != tempDate)
{
if (tempItem.Date[0] == tempDate)
{
TimeIsPresent = True;
break;
}
else
{
if (TimeIsPresent == False)
{
if (!(tempDate.DayOfWeek == DayOfWeek.Sunday)
{
dateToEvaluate = new List<DateTime>();
dateToEvaluate.Add(tempDate);
tempTask.Add(new GroupedTask { ID = null,
TaskID = null,
Date = dateToEvaluate });
}
}
}
tempDate = tempDate.AddDays(1);
}
if (TimeIsPresent == True)
{
tempTask.Add(new GroupedTask { ID = tempItem.ID,
TaskID = tempItem.TaskID,
Date = tempItem.Date });
TimeIsPresent = false;
}
}
let me give you an example. My range of date is as follows: Mon 8 Aug - Sunday 14 Aug.
Now my tasklist is as follows: item1: Date 9Aug, item2: Date 11Aug.
So my tempTask must be as follows:
item1: Date 8 Aug, taskID: null, ID: null,
item2: Date 9 Aug, taskID: 678, ID: 7,
item3: Date 10Aug, taskID: null, ID: null,
item4: Date11 Aug, taskID:890, ID: 34,
item5: Date 12 Aug, taskID: null, ID: null,
item6: Date 13 Aug, taskID: null, ID: null
Second example:
My range of date is as follows: Mon 8 Aug - Sunday 14 Aug.
Now my tasklist is as follows: item1: Date 9Aug, item2: Date 11Aug, item3: Date 14Aug
So my tempTask must be as follows:
item1: Date 8 Aug, taskID: null, ID: null,
item2: Date 9 Aug, taskID: 678, ID: 7,
item3: Date 10Aug, taskID: null, ID: null,
item4: Date11 Aug, taskID:890, ID: 34,
item5: Date 12 Aug, taskID: null, ID: null,
item6: Date 13 Aug, taskID: null, ID: null,
item4: Date14 Aug, taskID:894, ID: 74,
I think you're making things more difficult than they really are. As I understand it, you're taking each item in TaskList and seeing if the date falls in a certain range. If it does, you add it to another List and go to the next item, otherwise you add a blank entry to the other list and keep checking.
If my understanding is correct, try this:
EDITED based on OP's comment
The code now goes through the entire range for each item in TaskList, and adds either an empty object with the date or the corresponding task for the date.
No need to use a bool to determine if the date is present in this scenario.
// Note that you'll have to assign values to StartDate and EndDate, otherwise you'll get
// a Null Reference Exception
DateTime StartDate;
DateTime EndDate;
Datetime tempDate = StartDate;
List<DateTime> dateToEvaluate;
foreach (var tempItem in TaskList)
{
// Reset tempDate to the starting date before each loop
tempDate = StartDate;
while (EndDate.AddDays(1) != tempDate)
{
if (tempDate.DayOfWeek != DayOfWeek.Sunday)
{
if (tempItem.Date[0] == tempDate)
{
tempTask.Add(new GroupedTask { ID = tempItem.ID,
TaskID = tempItem.TaskID,
Date = tempItem.Date });
}
else
{
dateToEvaluate = new List<DateTime>();
dateToEvaluate.Add(tempDate);
tempTask.Add(new GroupedTask { ID = null,
TaskID = null,
Date = dateToEvaluate });
}
}
tempDate = tempDate.AddDays(1);
}
}
EDITED to add
Assume a 2 week range, with 7/1 starting on a Monday going through 7/14. Assume two tasks - task 1 with a date of 7/3 and task 2 with a date of 7/12. I would expect the following in tempTask:
26 elements (13 dates for each of the two task items), with all elements having a null ID except for one each for the two tasks.
Are you actually wanting a consolidated list with no repeats? I.e., with my example, there would be 13 elements, and 2 would have non-null IDs? What happens if two or more tasks have the same date?
I did find one error, in that I wasn't resetting the tempDate to the start before each loop.
EDIT Based on new understanding
Ok, so you're attempting to get a second list that has all the dates in a given range, and the GroupedTask object will either be an existing GroupedTask for that date, or a null GroupedTask for that date, if there is no match.
I suggest you take a look at Enigmativity's answer, as that may be a more elegant solution (I haven't looked at it in detail), but here's another approach. The biggest change is that I flipped the while loop and foreach loops.
// Note that you'll have to assign values to StartDate and EndDate, otherwise you'll get
// a Null Reference Exception
DateTime StartDate;
DateTime EndDate;
// Declare an instance of GroupedTask for use in the while loop
GroupedTask newTask;
Datetime tempDate = StartDate;
// Loop through the entire range of dates
while (EndDate.AddDays(1) != tempDate)
{
// You included Sundays in your example, but had earlier indicated they
// weren't needed. If you do want Sundays, you can remove this outer if
// block
if (tempDate.DayOfWeek != DayOfWeek.Sunday)
{
// Create a "null" GroupedTask object
// The Date property in GroupedTask appears to be a List<DateTime>,
// so I chose to initialize it along with the other properties.
newTask = new GroupedTask() { ID = null,
TaskID = null,
Date = new List<DateTime>() { tempDate }};
// For each date in the range, check to see if there are any tasks in the TaskList
foreach (var tempItem in TaskList)
{
// If the current item's date matches the current date in the range,
// update the newTask object with the current item's values.
// NOTE: If more than one item has the current date, the last one in
// will win as this code is written.
if (tempItem.Date[0] == tempDate)
{
newTask.ID = tempItem.ID;
newTask.TaskID = tempItem.TaskID;
newTask.Date = tempItem.Date;
}
}
// Add the newTask object to the second list
tempTask.Add(newTask);
}
}
I'm not sure what is EndDate, tempDate and some other things in your example. But if you are trying to loop through a DateRange and checking the existence of a particular date, then you could consider the following example:
static void yourFunction()
{
//
//Some Stuffs
//
foreach (var tempItem in TaskList)
{
if (DateRange.Contains(tempItem.Date[0]))
{
//Do Task
}
else
{
//Do Task
}
}
//
//Some Stuffs
//
}
public static IEnumerable<DateTime> DateRange
{
get
{
for (DateTime day = startDate; day < EndDate; day = day.AddDays(1))
{
yield return day;
}
}
}
Encapsulating a range of dates on a property is the idea of Jon Skeet, I learned it from his book C# in Depth
I found your code a little confusing, but if I understand your intent then I have a solution for you using LINQ. My approach might be a bit confusing to start with, but I'm happy to help you work through it.
My understanding is that you have a range of dates that you want to create a matching list of GroupedTask objects where you will take the object(s) from an existing TaskList for each matching date in the range or create a "dummy" instance if there isn't a match.
I assume that you have defined a StartDate variable along with the EndDate variable you used in your question.
My solution (which I have tested) looks like this:
var query =
from d in dates
from t in getTasksForDate(d)
where (t.ID != null) || (d.DayOfWeek != DayOfWeek.Sunday)
select t;
tempTask.AddRange(query);
Ignoring for the moment the two parts that need to be defined (dates & getTasksForDate) the query works by running through each date from the start to the end and selecting the tasks for that date (either from the TaskList or a "dummy" task if none exist for the date) and then filtering out any "dummy" tasks that fall on a Sunday. The tasks are then added to the tempTask list.
Now for the missing parts.
To get the dates list, just do this:
var days = EndDate.Date.Subtract(StartDate.Date).Days + 1;
var dates =
Enumerable
.Range(1 - days, days)
.Select(d => EndDate.AddDays(d));
As long as StartDate is on or before EndDate you will now have a list of dates that starts on StartDate and ends on EndDate.
The getTasksForDate is the trickier part.
You need to first turn the TaskList list into a lookup function that turns any date into a list of GroupedTask objects for that date. With LINQ it's easy:
var lookup = TaskList.ToLookup(x => x.Date[0].Date, x => new GroupedTask()
{
ID = x.ID,
TaskID = x.TaskID,
Date = x.Date,
});
Next you need to create the getTasksForDate function that will take a date and return either the list of GroupedTask from the lookup for the date or a single "dummy" GroupedTask object if there were no tasks for the date.
Func<DateTime, IEnumerable<GroupedTask>> getTasksForDate = d =>
{
return lookup[d].DefaultIfEmpty(new GroupedTask()
{
ID = null,
TaskID = null,
Date = new List<DateTime>() { d, },
});
};
That's it.
If you want to define StartDate and/or EndDate based on actual values from TaskList you can use this code:
var StartDate = TaskList.Select(t => t.Date[0].Date).Min();
var EndDate = TaskList.Select(t => t.Date[0].Date).Max();
I've used .Date after most of the DateTime references to ensure that there is no time component to the date.
Yell out if you'd like any further explanation.
If I understand what you are trying to do, I would change the way you are doing it in this way. First I would find all the dates in your range that have one or mor associated tasks (and put themn in a Dictionary in order to be able to get them knowing the date), then I would create tempTask. Something like this:
DateTime StartDate;
DateTime EndDate;
DateTime tempDate = StartDate;
List<DateTime> dateToEvaluate;
Dictionary<DateTime, List<Task>> dateTaskDict = new Dictionary<DateTime, List<Task>>();
bool TimeIsPresent = false;
foreach (Task tempItem in TaskList)
{
while (EndDate.AddDays(1) != tempDate)
{
if (tempItem.Date[0] == tempDate)
{
List<Task> tasksForDate;
if (!dateTaskDict.TryGetValue(tempDate, out tasksForDate))
{
tasksForDate = new List<Task>();
dateTaskDict[tempDate] = tasksForDate;
}
tasksForDate.Add(tempItem);
break;
}
tempDate = tempDate.AddDays(1);
}
}
tempDate = StartDate;
while (EndDate.AddDays(1) != tempDate)
{
List<Task> tasks;
if (dateTaskDict.TryGetValue(tempDate, out tasks))
{
foreach (Task aTask in tasks)
tempTask.Add(new GroupedTask { ID = aTask.ID,
TaskID = aTask.TaskID,
Date = tempDate });
}
else
{
if (tempDate.DayOfWeek != DayOfWeek.Sunday)
{
tempTask.Add(new GroupedTask { ID = null
TaskID = null,
Date = tempDate });
}
}
}
I'm using the VSTA C# on an infopath 2010 form, whereby by using cascading drop downs (Course Title & Course Details) information is displayed.
So when a user selects the Course Title drop down, the Course details is populated with the StartTime, EndTime, Location and Development Category information from a Sharepoint 2010 list.
Now the problem I have is that I want the user to only view the course details for today and onwards, and not view course details for the past. This is the code whereby I display the coursedetails. I've tried declaring a dateTime variable and using it to compare with a string that converts to DateTime with Today, to make it later than the DateTime variable, but it gives me an error after I select a course title, it says "Object Reference not set to an instance of an object". With the troubleshooting tips: "Use the new keyword to create an object instance. Check to determine if the object is null before calling the method. Get gengeral help for this exception"
using (web = site.OpenWeb())
{
try
{
//SPSecurity.RunWithElevatedPrivileges(new SPSecurity.CodeToRunElevated(delegate()
//{
SPList lstDocs = web.Lists["Training Calander"] as SPList;
string sTitle = "";
string sSDate = "";
string sEDate = "";
string sLocation = "";
string SDCategory = "";
string CourseDetails = "";
//DateTime TodayDate = DateTime.Today;
//DateTime dt1 = Convert.ToDateTime(sEDate);
if (lstDocs != null)
{
SortedList<string, string> lstDetails = new SortedList<string, string>();
foreach (SPListItem item in lstDocs.Items)
{
try
{
sTitle = item["Title"].ToString();
sSDate = item["StartTime"].ToString();
sEDate = item["EndTime"].ToString();
sLocation = item["Location"].ToString();
SDCategory = item["Development Category"].ToString();
}
catch { }
if (sTitle == nValue) //&& (dt >= TodayDate))
{
try
{
CourseDetails = sSDate + " - " + sEDate + " | " + sLocation + " | " + SDCategory;
lstDetails.Add(CourseDetails,CourseDetails);
}
catch { }
}
}
I believe the problem is best solved before you execute your foreach loop. You need to create a query that will select only the Items that meet your criteria using a Where clause. They you can iterate through your loop without having to test the date on each pass, which is going to be slower.
Assuming Startdate is stored as a date variable, this should be a trivial query to write.
Apologies if I have misunderstood your issue.
foreach (SPListItem item in lstDocs.Items.Where(item => item.StartTime.Date >= DateTime.Now.Date))
This is assuming there is a property called StartTime in the SPListItem class and that you are using .NET 3+ and have access to Linq.