List with dates, times and a string ref - c#

I have a sorted list of data to populate a gridview control.
The list is ordered by datetime
I need to find the most recent 3 times per unique ref within each day (there may be more than 1 unique refs within a day). If there is only 1 ref row within a day then it is to be ignored.
I suppose it needs to be chunked into days and refs, then ordered by most recent with a count of refs within that 'chunk'. Any ideas appreciated.
It's a standard list of objects (the object has Date, Time and Ref properties) as:
private List<Reading> _listReadings = new List<Reading>();
and is bound to a grid:
DataRow newRow = MyTable.NewRow();
newRow.ItemArray = new object[]
{
new
DateTime(_listReadings.TimeStamp.Year,listReadings.TimeStamp.Month),
GetTime(_listReadings.TimeStamp),
_listReadings.Ref,
};
MyTable.Rows.Add(newRow);

Given a backing source of List<Reading>,
Group your source data by day using GroupBy
For-each day (the value in the group):
Group your source data by ref using GroupBy
Test if the group has 3+ values, or filter the ones that do not (Where and Count)
Order by datetimes of the values using OrderByDescending (latest is first)
Take 3 dates
So something like this:
public static IEnumerable<Reading> Filter(List<Reading> readings)
{
List<Reading> result = new List<Reading>();
var dayGroupings = readings.GroupBy(r => r.Day);
foreach (var dayGroup in dayGroupings)
{
var refGroupings = dayGroup.GroupBy(g => g.Ref);
foreach (var refGroup in refGroupings.Where(g => g.Count() >= 3))
{
result.AddRange(refGroup.OrderByDescending(g => g.Time).Take(3));
}
}
return result;
}

Related

LINQ - Decide which date is the oldest for each record

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

Iterate through list in paged manner, within another loop

I have three collections. First, a collection of days. Next, a collection of time spans in each day. These time spans are the same for each day. Next, I have a collection of sessions.
There are 4 days. There are 6 time spans. There are 30 sessions.
I need to iterate through each day, assigning all of the time spans to each day the same way for each day. However, I need to assign the sessions to time blocks in sequence. For example, day 1 gets all 6 time spans, but only the first 6 sessions, 1-6. Day 2 gets the same time spans, but gets the next 6 sessions, 7-12.
How can I do this within the same method?
Here's what I have so far, but I'm having trouble wrapping my head around the paged iteration part.
var timeSlots = TimeSlotDataAccess.GetItems(codeCampId);
var assignableSlotCount = timeSlots.Where(t => !t.SpanAllTracks);
// determine how many days the event lasts for
agenda.NumberOfDays = (int)(agenda.CodeCamp.EndDate - agenda.CodeCamp.BeginDate).TotalDays;
// iterate through each day
agenda.EventDays = new List<EventDayInfo>(agenda.NumberOfDays);
var dayCount = 0;
while (dayCount <= agenda.NumberOfDays)
{
var eventDate = agenda.CodeCamp.BeginDate.AddDays(dayCount);
var eventDay = new EventDayInfo()
{
Index = dayCount,
Day = eventDate.Day,
Month = eventDate.Month,
Year = eventDate.Year,
TimeStamp = eventDate
};
// iterate through each timeslot
foreach (var timeSlot in timeSlots)
{
var slot = new AgendaTimeSlotInfo(timeSlot);
// iterate through each session
// first day gets the first set of assignableTimeSlotCount, then the next iteration gets the next set of that count, etc.
slot.Sessions = SessionDataAccess.GetItemsByTimeSlotId(slot.TimeSlotId, codeCampId).ToList();
// iterate through each speaker
foreach (var session in slot.Sessions)
{
session.Speakers=SpeakerDataAccess.GetSpeakersForCollection(session.SessionId, codeCampId);
}
}
agenda.EventDays.Add(eventDay);
dayCount++;
}
I ended up using LINQ in a new method based upon the GetItemsByTimeSlot() method. The new signature and example of getting a matching subset of that collection is below.
Here's how I'm calling it:
slot.Sessions = SessionDataAccess.GetItemsByTimeSlotIdByPage(slot.TimeSlotId,
codeCampId, dayCount + 1, timeSlotCount).ToList();
Here's what it looks like:
public IEnumerable<SessionInfo> GetItemsByTimeSlotIdByPage(int timeSlotId, int codeCampId, int pageNumber, int pageSize)
{
var items = repo.GetItems(codeCampId).Where(t => t.TimeSlotId == timeSlotId);
items.Select(s => { s.RegistrantCount = GetRegistrantCount(s.SessionId); return s; });
// this is the important part
var resultSet = items.Skip(pageSize * (pageNumber - 1)).Take(pageSize);
foreach (var item in resultSet)
{
item.Speakers = speakerRepo.GetSpeakersForCollection(item.SessionId, item.CodeCampId);
}
return resultSet;
}

Get Sitecore Items Sorted By Creation Date

I am trying to get some Sitecore items and then sort them by their creation date with newest items first.
I have the following code (snippet):
itemID = Constants.BucketIds.NEWS;
Item pressItem = context.GetItem(itemID);
var pressChildItems = context
.SelectItems("/sitecore/content" + pressItem.Paths.ContentPath + "/*")
.OrderByDescending(x => x.Fields[Sitecore.FieldIDs.Created]);
foreach (Item childItem in pressChildItems)
{
// DO SOMETHING
}
I am getting the following error back:
At least one object must implement IComparable.
I am not sure how I am supposed to fix this.
Do not order by Field, sort by its value. Remove .Fields from your line:
var pressChildItems = context
.SelectItems("/sitecore/content" + pressItem.Paths.ContentPath + "/*")
.OrderByDescending(x => x[Sitecore.FieldIDs.Created]);
Dates are stored as yyyyMMddHHmmss... strings, so sorting by value as string will give you exactly the same effect as getting date value from field and ordering using the date.
Since it looks like you have your items in a Bucket, you should really use the ContentSearch API (and limit the results if you are using them on the front-end since a bucket could potentially contain thousands of items).
using Sitecore.ContentSearch;
using Sitecore.ContentSearch.Linq;
using Sitecore.ContentSearch.SearchTypes;
using Sitecore.Data.Items;
List<Item> ResultsItems = new List<Item>();
SitecoreIndexableItem bucket = Context.Database.GetItem(Constants.BucketIds.NEWS);
using (var searchcontext = ContentSearchManager.GetIndex(bucket).CreateSearchContext())
{
IQueryable<SearchResultItem> searchQuery =
searchcontext.GetQueryable<SearchResultItem>()
.OrderByDescending(x => x.CreatedDate)
.Take(10);
SearchResults<SearchResultItem> results = searchQuery.GetResults();
// fetch the Sitecore Items if you do not want to work with the SearchResultItem
foreach (var hit in results.Hits)
{
Item item = hit.Document.GetItem();
if (item != null)
{
ResultsItems.Add(item);
}
}
}

Grouping usage data per week

I am trying to create a usage report where I can plot the number of files created per week by each user. I'm trying to put the data into a DataTable so I can use it in a Chart. The path I'm headed down is clunky and I'm guessing there is a much more elegant way to do this in Linq.
The File class has an OpenDate and a LastModUser value. I want to sum up all the files created for a week for each user. In table form this looks like this:
File# OpenDate LastModUser
1 1/1/2015 ASmith
2 1/2/2015 ASmith
3 1/2/2015 DJones
4 1/2/2015 CBanks
The result of this query would return:
Week# ASmith DJones CBanks
1 2 1 1
2 etc etc etc
Here is what I have thus far.
public static DataTable GetFileCountByUserByClient(Int32 clientID, Int32 weeks)
{
using (EtaDataModelContainer12 etaDbContext = new EtaDataModelContainer12())
{
DataTable table = new DataTable();
table.Columns.Add("week", typeof(Int32));
// These are all the client's files
List<File> files = etaDbContext.Files.Where(b => b.ClientClientId == clientID).ToList();
// Get a list of all users
List<string> users = files.GroupBy(b => b.LastModUser).Select(b =>b.Key).Distinct().ToList();
for (int i = 1; i < users.Count(); i++)
{
// Create one column per user
table.Columns.Add(users[i], typeof(string));
}
// Add rows to the table based on how many files created in a given week
DateTime today = DateTime.Today;
int filecount = 0;
// Loop through the number of selected weeks (rows in DataTable) and populate sums
for (int j = 0; j < weeks; j++)
{
// Look at each file and determine if it fits in the selected week
foreach (File item in files)
{
// If a match is found determine what column in the DataTable should be incremented
}
table.Rows.Add(j, filecount);
}
return table;
}
}
There has to be a more elegant way to do this.
I think, this can solve your problem. You may need to tune some constant to get it to your grid. The week returned by this query will be number of week from year 0.
const long TicksPerWeek = TimeSpan.TicksPerDay * 7;
var userFiles = files.GroupBy(f => f.LastModUser);
var userStats = userFiles.Select(u =>
u.GroupBy(f => file.Date.Ticks / TicksPerWeek)
.Select(f => new { week = f.Key, modifiedCount = f.Count()))

Sorting months in a list

I have a list of strings which contains months of the year. I need to be able to sort this list so the months are in order by month, not alphabetically. I have been searching for awhile but I can't see to wrap my head around any of the solutions I've found.
Here's an example of how the months might be added. They are added dynamically based off of fields in a SharePoint list so they can be in any order and can have duplicates (I am removing these with Distinct()).
List<string> monthList = new List<string>();
monthList.Add("June");
monthList.Add("February");
monthList.Add("August");
Would like to reorder this to:
February
June
August
You could parse the string into a DateTime and then sort using the month integer property. See here for supported month names: http://msdn.microsoft.com/en-us/library/system.globalization.datetimeformatinfo.monthnames.aspx
Something like this:
var sortedMonths = monthList
.Select(x => new { Name = x, Sort = DateTime.ParseExact(x, "MMMM", CultureInfo.InvariantCulture) })
.OrderBy(x => x.Sort.Month)
.Select(x => x.Name)
.ToArray();
You can parse month names into dates (it assumes the current year and day 1):
monthList = monthList.OrderBy(s=> DateTime.ParseExact(s, "MMMM", new CultureInfo("en-US"))).ToList();
You can use a Dictionary<int,string> instead, using the int as a month number for sorting, then sorting by key.
IDictionary<int,string> monthList = new Dictionary<int,string>();
monthList.Add(6, "June");
monthList.Add(2, "February");
monthList.Add(8, "August");
var sorted = monthList.OrderBy(item => item.Key);
If you insist on having a list of only strings representing months, then you must use another data source to retrieve the index of that month by which you can sort the list. For example, you could populate a dictionary with the month names as string keys and an int index as the value. You can then use the overloaded method List<T>.Sort(Comparison<T>) and pass in a comparison function that returns the index of the months by name (by passing them into the dictionary).
However, I would recommend not using a raw string in the first place, but rather a more structured data type representing a month. You can then embed the index in the data structure itself and sort based on that value, thus giving you a more self-contained solution.
You need a SortedList<> .. such as
SortedList<int,string> monthList=new SortedList<int,string>();
monthList.Add(6,"June");
monthList.Add(2,"February");
monthList.Add(8,"August");
IList<string> sortedMonthList=monthList.Values;
then use sortedMonthList for the rest.
This could be improved by using seldon's answer to create a function, just like
public static int MonthNumFromName(String monthname)
{ ... }
and then use
monthList.Add(MonthNumFromName("June"),"June");
above.
Well, I guess there aren't any sorting techniques for this with just pure month as string.
You can just use a Dictionary<int, string> and use the int to sort your months.
If I haven't mistaken, you are actually having a List of months as string, then why don't you do this?
List<string> months = new List<string> { "January", "February", ..., "December" };
var yourSortedMonthsInYourOriginalList = months.Where(m =>
originalList.Any(o => o == m)).ToList();
You can build your own sort class:
static void Main(string[] args)
{
List<string> monthList = new List<string>();
monthList.Add("June");
monthList.Add("February");
monthList.Add("August");
monthList.Sort(new _mysort());
}
private class _mysort : IComparer<string>
{
public int Compare(string x, string y)
{
if (x=="February" && y=="June")
{
return -1;
}
return 0;
}
}
But i think you should use an Enum, and convert it to string, then you can use the numeric to sort it.
enum months
{
Jan =0,
Feb =1
}
like:
List<months> mlist = new List<months>() { months.Feb, months.Jan };
//sort
mlist = mlist.OrderBy(e => (int)e).ToList();
//print
mlist.ForEach(e => Console.WriteLine(e.ToString()));
Create an enum and assign int values for each month. Sort that thing with linq.

Categories

Resources