C# Remove duplicate times within range and mark them - c#

So ive come across a slight issue with Lists in C#
I have a List that has some very similar dates. Now what i need to do is remove duplicates but where a duplicate is if its within one minute of another time, so not exactly the same.
As well as this, if there were duplicates of a specific time the object should have a flag saying so in the final list.
Now i can see how this could be done with lots of for loop, but i was hoping theres a nice way with LINQ to make this simple. So far my attempts havent been going very well though.
As an example a list of the following dates:
21/12/12 12:13:00
21/12/12 12:13:20
13/12/12 10:13:00
21/10/15 07:13:00
should turn into the list of DateDuplicate objects:
{date = 21/12/12 12:13:00, isDuplcicate=true}
{date = 13/12/12 10:13:00, isDuplcicate=false}
{date = 21/10/15 07:13:00, isDuplcicate=false}
public class DateDuplicate{
public DateTime date;
public bool isDuplicate = false;
}
List<DateDuplicate> RemoveAndMark(List<DateTime> dates){
///something nice here hopefully
}
thanks for any help!

Try this
List<DateDuplicate> RemoveAndMark(List<DateTime> dates)
{
var dateValues = dates.GroupBy(y => new { AddHours = y.Date.Date.AddHours(y.Hour), y.Minute}).Select(x =>
new DateDuplicate {isDuplicate = x.Count() > 1, date = x.Key.AddHours.AddMinutes(x.Key.Minute)}
);
return dateValues;
}

Related

How to sort stock months using linq C#

I have some stock data that I'm trying to sort using linq, however I'm very unfamiliar with linq and cannot understand the documentation.
Right now I have a list of bars (a class I've created that holds the stock data) and it's all of the stock data for each day since 1990. Now I'm trying to group this stock data by year and month so I can turn daily stock data into monthly stock data (the resolution of the stock).
public class Stock
{
private string stockSymboll;
private string period;
private List<bar> aBar = new List<bar>();
private DateTime startingDate;
private DateTime endingDate;
enum period { DAILY, WEEKLY, MONTHLY };
private period PeriodType;
}
public class bar
{
private double open;
private double high;
private double low;
private double close;
private double volume;
private DateTime stockDate;
}
within the stock class I have a function that I'm trying to use to convert from a list of daily data to a list of monthly data, in order from most recent to least recent.
Here is what I've attempted:
stock convertPeriod(Period pt)
{
stock newStock = new Stock(stockName, startingDate, endingDate, period);
if (pt == Periode.MONTHLY)
{
List<bar> monthlyGroup = new List<bar>();
var group1 = (from b in bar group c by b.getDate().Month);
var group2 = from g in group1 group g by g.getDate().Year)
return...;
}
}
However I've discovered that you cannot sort a var. So I was thinking the best way would be to attempt a nested query in linq, however I can hardly seem to even get basic queries to work. Any help would be greatly appreciated.
I'm not 100% clear on what contract you're supposed to be setting up, but as I understand it, you want to:
Receive an IEnumerable<Bar>, and
Return an IEnumerable<Bar> such that StockDate is the first day of the year and month for the summarized stocks, all sorted descending by date.
As I understand it, Stock is more or less irrelevant to your true question here. If I'm incorrect in that, let me know and I can help you take this that one step further.
You have a good start on this in your LINQ. I'm a little confused about your use of private fields in the types, but I'm assuming those to be typos, and that your actual code uses public, probably properties.
I'll do this as a couple separate methods just to make what I'm doing more clear, but you may want to bring them together in the interest of performance. Particularly since I order the collection twice, effectively.
This method groups and sorts the data, based on the first day of their year-month set. Note that you can actually perform a grouping on an anonymous type. Sorting doesn't work on the anonymous object itself, as you noted, but it does work on its properties.
public IEnumerable<IEnumerable<Bar>> GroupIntoMonths(IEnumerable<Bar> bars)
{
return bars.GroupBy(c => new { c.StockDate.Year, c.StockDate.Month })
.OrderByDescending(c => c.Key.Year)
.ThenByDescending(c => c.Key.Month);
}
The choice is yours whether you prefer to group on an instantiated DateTime object with a date set of one, or what I've done here. I don't touch the Key property again, so I was fine effectively losing track of it after I left the method. Other implementations might drive you to make a different decision on that.
Once you've got that, it's a matter of converting an IEnumerable<Bar> into a single Bar that summarizes the whole period.
public IEnumerable<Bar> GroupIntoBars(IEnumerable<IGrouping<DateTime, Bar>> groups)
{
return groups.Select(GetBar);
}
public Bar GetBar(IEnumerable<Bar> bars)
{
Bar ret = new Bar();
Bar last = null;
int index = -1;
foreach(var v in bars.OrderBy(c => c.StartingDate))
{
index++;
if(index == 0)
{
ret.Open = v.Open;
ret.StockDate = v.StockDate;
ret.High = v.High;
ret.Low = v.Low;
}
else
{
ret.High = Math.Max(ret.High, v.High);
ret.Low= Math.Max(ret.Low, v.Low);
}
last = v;
}
if(last == null) throw new ArgumentException("Collection cannot be empty!");
ret.Close = last.Close;
return ret;
}
I think that method is pretty straight-forward, but let me know if I can clear anything up.
You can group by multiple properties at once by specifying them in an anonymous object that will be the group key:
var monthlyGroup = aBar.OrderBy(bar => bar.stockDate)
.GroupBy(bar => new { Year = bar.stockDate.Year, Month = bar.stockDate.Month })
//create bars from groups
.Select(g => new bar()
{
open = g.First().open,
high = g.Max(b => b.high),
low = g.Min(b => b.low),
close = g.Last().close,
volume = g.Average(b => b.volume),
stockDate = new DateTime(g.Key.Year, g.Key.Month, 1)
})
.ToList();
Sorry but I prefer the functions syntax of linq.
I noticed that fields in bar class are private so they will be inaccessible. However, I assume that you have properties for each field.
In that case you will have to replace field names with property names in the code above.

C# - sorting by a property

I am trying to sort a collection of objects in C# by a custom property.
(For context, I am working with the Twitter API using the Twitterizer library, sorting Direct Messages into conversation view)
Say a custom class has a property named label, where label is a string that is assigned when the class constructor.
I have a Collection (or a List, it doesn't matter) of said classes, and I want to sort them all into separate Lists (or Collections) based on the value of label, and group them together.
At the moment I've been doing this by using a foreach loop and checking the values that way - a horrible waste of CPU time and awful programming, I know. I'm ashamed of it.
Basically I know that all of the data I have is there given to me, and I also know that it should be really easy to sort. It's easy enough for a human to do it with bits of paper, but I just don't know how to do it in C#.
Does anyone have the solution to this? If you need more information and/or context just ask.
Have you tried Linq's OrderBy?
var mySortedList = myCollection.OrderBy(x => x.PropertyName).ToList();
This is still going to loop through the values to sort - there's no way around that. This will at least clean up your code.
You say sorting but it sounds like you're trying to divide up a list of things based on a common value. For that you want GroupBy.
You'll also want ToDictionary to switch from an IGrouping as you'll presumably be wanting key based lookup.
I assume that the elements within each of the output sets will need to be sorted, so check out OrderBy. Since you'll undoubtedly be accessing each list multiple times you'll want to collapse it to a list or an array (you mentioned list) so I used ToList
//Make some test data
var labels = new[] {"A", "B", "C", "D"};
var rawMessages = new List<Message>();
for (var i = 0; i < 15; ++i)
{
rawMessages.Add(new Message
{
Label = labels[i % labels.Length],
Text = "Hi" + i,
Timestamp = DateTime.Now.AddMinutes(i * Math.Pow(-1, i))
});
}
//Group the data up by label
var groupedMessages = rawMessages.GroupBy(message => message.Label);
//Convert to a dictionary for by-label lookup (this gives us a Dictionary<string, List<Message>>)
var messageLookup = groupedMessages.ToDictionary(
//Make the dictionary key the label of the conversation (set of messages)
grouping => grouping.Key,
//Sort the messages in each conversation by their timestamps and convert to a list
messages => messages.OrderBy(message => message.Timestamp).ToList());
//Use the data...
var messagesInConversationA = messageLookup["A"];
var messagesInConversationB = messageLookup["B"];
var messagesInConversationC = messageLookup["C"];
var messagesInConversationD = messageLookup["D"];
It sounds to me like mlorbetske was correct in his interpretation of your question. It sounds like you want to do grouping rather than sorting. I just went at the answer a bit differently
var originalList = new[] { new { Name = "Andy", Label = "Junk" }, new { Name = "Frank", Label = "Junk" }, new { Name = "Lisa", Label = "Trash" } }.ToList();
var myLists = new Dictionary<string, List<Object>>();
originalList.ForEach(x =>
{
if (!myLists.ContainsKey(x.Label))
myLists.Add(x.Label,new List<object>());
myLists[x.Label].Add(x);
});

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.

Querying Distinct Events from Google Calendar

Right now I have a very simple query that pulls up entries that have a string and a specific date range.
EventQuery eQuery = new EventQuery(calendarInfo.Uri.ToString());
eQuery.Query = "Tennis";
eQuery.StartDate = startDate;
eQuery.EndDate = endDate;
EventFeed myResultsFeed = _service.Query(eQuery);
After querying, myResultsFeed will contain an atomEntryCollection. Each atomEntry has a Title. The way I have it set up, there could be multiple entries with the same title.
I would like my Query to be able to select UNIQUE titles. Is this possible?
Link to the API Docs
I hypothesized that I could use a WHERE object
Where x = new Where();
x.yadayada();
but it can't be passed to _service.Query()
I'm also exploring the .extraparameters object. is it possible to do something like this?
eQuery.ExtraParameters = "distinct";
Looking into the "Partial Response" feature..
http://code.google.com/apis/gdata/docs/2.0/reference.html#PartialResponse
it looks pretty promising..
I don't think what you're trying to do is possible using the Google Data API.
However, extenting upon #Fueled answer, you could do something like this if you need a collection of AtomEntry's.
// Custom comparer for the AtomEntry class
class AtomEntryComparer : IEqualityComparer<AtomEntry>
{
// EventEntry are equal if their titles are equal.
public bool Equals(AtomEntry x, AtomEntry y)
{
// adjust as needed
return x.Title.Text.Equals(y.Title.Text);
}
public int GetHashCode(AtomEntry entry)
{
// adjust as needed
return entry.Title.Text.GetHashCode();
}
}
EventFeed eventFeed = service.Query(query)
var entries = eventFeed.Entries.Distinct(new AtomEntryComparer());
It's probably not the solution you were looking for, but since you have in hand an AtomEntryCollection (which down the line implements IEnumerable<T>), you could use LINQ to retrieve the distinct titles, like so:
EventFeed feed = service.Query(query);
var uniqueEntries =
(from e in feed.Entries
select e.Title.Text).Distinct();
And then loop over them with a simple foreach:
foreach (var item in uniqueEntries)
{
Console.WriteLine(item);
}
But then you have only a collection of string representing the Event titles, and not a collection of AtomEntry. I guess you could link them together in a Dictionary.
Not optimal, but should work.

How can I shorten this linq to sql query?

I am am making a calendar and to make it easier on myself I break up appointments that span over multiple weeks.
For instance Jan 1st to Jan 31st spans like 6 weeks(my calendar is always 42 cells - 6 by 7). So I would basically have 6 rows stored in my database.
However somethings I do require to me to put all these rows back together into one row. For instance if I want to export my calendar in Ical format.
I have a field in my database called bindingClassName all these rows get the same unquie id to that group of tasks so I am able to get all the weeks easily.
// get all of the task rows by binding class name.
var found = plannerDb.Calendars.Where(u => u.UserId == userId && u.BindingClassName == bindingClassName)
.GroupBy(u => u.BindingClassName);
List<Calendar> allAppoingments = new List<Calendar>();
// go through each of the results and add it to a list of calendars
foreach (var group in found)
{
foreach (var row in group)
{
Calendar appointment = new Calendar();
appointment.AppointmentId = row.AppointmentId;
appointment.AllDay = row.AllDay;
appointment.BindingClassName = row.BindingClassName;
appointment.Description = row.Description;
appointment.EndDate = row.EndDate;
appointment.StartDate = row.StartDate;
appointment.Title = row.Title;
appointment.Where = row.Where;
appointment.UserId = row.UserId;
allAppoingments.Add(appointment);
}
}
// order
var test = allAppoingments.OrderBy(u => u.StartDate);
var firstAppointment = test.First();
var LastAppointment = test.Last();
Calendar newAppointment = new Calendar();
newAppointment.UserId = firstAppointment.UserId;
newAppointment.Description = firstAppointment.Description;
newAppointment.AllDay = firstAppointment.AllDay;
newAppointment.StartDate = firstAppointment.StartDate;
newAppointment.Title = firstAppointment.Title;
newAppointment.Where = firstAppointment.Where;
newAppointment.BindingClassName = firstAppointment.BindingClassName;
newAppointment.EndDate = LastAppointment.EndDate;
return newAppointment;
So basically that big blob finds all the appointments with the same binding name. Then I go through each one and make it into a Calendar object then finally once it is all made I get the first and last record to get the startDate and endDate.
So I am not good with linq but I am not sure if I can just add something after the groupBy to do what I want.
Edit.
I am trying group all my appointments together once I get all of them from the user.
So I have this so far
I tried something like this.
var allApointments = calendar.GetAllAppointments(userId);
var group = allApointments.GroupBy(u => u.BindingClassName).Select(u => new Calendar()).ToList
I was hoping that it would fill each group automatically but it does not. So I am not sure if don't need groupby again.
Edit # admin
Hi thanks for explaining sorting and grouping. How you explained it though it seems either one would work.
Like the code you have for getting the first and last date works great and does what I wanted it to.
I think grouping might have worked because in the end though I am looking just to have one row that has the startdate of the first record and the end date of the last record all the other information would be the same.
So I don't know if it would harder to write that instead or what but like I said your query does what I want.
However that query is used on a single basis. Like I use that query only when a user clicks to view that appointment on my calendar. By clicking on the appointment I get all the information about that appointment and thats where I need to look at if that task spans over multiple days and figure out when the appointment started and when it is going to end.
Now I need another query and I think it would be better if I could actually group them as how I understand it from your explanation it will make one row. the reason I think this is because I want to export all the records in the table from that user.
So if I order them into one continues block by binding name I still going to need some loops that goes through all the records and gets the first and start date. So if I could just group it in one go and the final result would be just one record for each group of binding names and it would have the first start date and the last end date from the first and last record would be better.
Why are you grouping the appointments if you aren't actually using the group? It looks like you're just using them individually. In any case, you're already filtering the rows on a single value for BindingClassName in the Where clause, so you would only end up with 1 (or 0) group(s) anyway.
You can rewrite that series of foreach loops into a Select and ToList() like this:
var allAppointments =
plannerDb.Calendars.Where(
row => row.UserId == userId &&
row.BindingClassName == bindingClassName).OrderBy(
row => row.StartDate).Select(
row => new Calendar()
{
AppointmentId = row.AppointmentId,
AllDay = row.AllDay,
BindingClassName = row.BindingClassName,
Description = row.Description,
EndDate = row.EndDate,
StartDate = row.StartDate,
Title = row.Title,
Where = row.Where,
UserId = row.UserId
}).ToList();
This will give you back the full list in the order you wanted. However, I'm curious why you're retrieving the whole list when it looks like you're only interested in the first and last appointment. You could instead do this:
var baseQuery =
plannerDb.Calendars.Where(
row => row.UserId == userId &&
row.BindingClassName == bindingClassName);
var first = baseQuery.OrderBy(row => row.StartDate).First();
var last = baseQuery.OrderByDescending(row => row.StartDate).Select(
row => row.EndDate).First();
return new Calendar()
{
AppointmentId = first.AppointmentId,
AllDay = first.AllDay,
BindingClassName = first.BindingClassName,
Description = first.Description,
EndDate = last,
StartDate = first.StartDate,
Title = first.Title,
Where = first.Where,
UserId = first.UserId
});
This should produce outputs that are the same as what you have now. I would question, however, if this is exactly what you want. Say you have two appointments:
Appointment 1 starts January 5 and ends on January 10
Appointment 2 starts January 6 and ends on January 7
Using this (and your) logic, you would get the end date as January 7, since Appointment 2 has the larger start date, but Appointment 1 actually ends later. I would recommend changing the second query to this:
var last = baseQuery.OrderByDescending(row => row.EndDate).Select(
row => row.EndDate).First();
This will give you the largest end date, which I think is what you're actually after.
EDIT
I think you're making the (very common) mistake of confusing grouping with sorting. When you say you want to "group the appointments by the binding name", it sounds like you want a full, complete list of appointments, and you want those appointments arranged in such a way as all appointments with a particular binding name form a contiguous block. If that's the case, you want to order the list by the binding name, not group them. Grouping takes the whole list and produces one row per grouping clause and allows you to perform aggregation functions on the remaining columns. For example, let's say I group the appointments on the binding name. This means that my result set will contain one row per binding name, and I can then do things like find the maximum start or end date or something like that; more formally, you can specify aggregation operations, which are operations that take a set of data (i.e. a list of start dates) and return a single piece of data (i.e. the maximum start date).
Unless I'm misunderstanding, it sounds like you still want to retrieve all of the individual assignments, you just want them arranged by binding name. If this is the case, just OrderBy(row => row.BindingName) and it will do the trick. In addition, you may want to avoid using the word "group", as people will think you mean the sort of grouping that I described above.
Just as a side point not concerning the linq, have you looked at AutoMapper? I am currently using this for populating data objects from linq and I've found it really useful for getting rid of the large sections of code where you just map to dtos. It wouldn't make the query parts of your code any shorter but would reduce:
return new Calendar()
{
AppointmentId = first.AppointmentId,
AllDay = first.AllDay,
BindingClassName = first.BindingClassName,
Description = first.Description,
EndDate = last,
StartDate = first.StartDate,
Title = first.Title,
Where = first.Where,
UserId = first.UserId
});
to:
return Mapper.Map(first,new Calendar{EndDate = last});

Categories

Resources