Merge two list of TimeRanges into one - c#

I have a class named TimeRange and another Interval both have same following structure
public class TimeRange
{
public TimeOfDay start{get; set;}
public TimeOfDay end{get; set;}
}
I have two list
List<TimeRange> timeRanges = new List<TimeRange>();
timeRanges.Add(new TimeRange(Timespan.FromHours(5), Timespan.FromHours(6)));
timeRanges.Add(new TimeRange(Timespan.FromHours(8), Timespan.FromHours(9)));
List<Interval> interval = new List<Interval>();
interval.Add(new Interval(Timespan.FromHours(1), Timespan.FromHours(7)));
interval.Add(new Interval(Timespan.FromHours(10), Timespan.FromHours(15)));
I want to merge these lists into one so that final result will contain this
Timespan.FromHours(1), Timespan.FromHours(5)
Timespan.FromHours(5), Timespan.FromHours(6)
Timespan.FromHours(6), Timespan.FromHours(7)
Timespan.FromHours(8), Timespan.FromHours(9)
Timespan.FromHours(10), Timespan.FromHours(15)
Another case:
timeRange.Add(new Interval(TimeSpan.FromHours(9), TimeSpan.FromHours(17))
timeRange.Add(new Interval(TimeSpan.FromHours(17), TimeSpan.FromHours(19))
interval.Add(new Interval(TimeSpan.FromHours(0), TimeSpan.FromHours(4))
interval.Add(new Interval(TimeSpan.FromHours(4), TimeSpan.FromHours(5))
interval.Add(new Interval(TimeSpan.FromHours(5), TimeSpan.FromHours(9))
interval.Add(new Interval(TimeSpan.FromHours(9), TimeSpan.FromHours(10))
interval.Add(new Interval(TimeSpan.FromHours(12), TimeSpan.FromHours(13))
Expected Result:
Timespan.FromHours(0), Timespan.FromHours(4)
Timespan.FromHours(4), Timespan.FromHours(5)
Timespan.FromHours(5), Timespan.FromHours(9)
Timespan.FromHours(9), Timespan.FromHours(10)
Timespan.FromHours(10), Timespan.FromHours(12)
Timespan.FromHours(12), Timespan.FromHours(13)
Timespan.FromHours(13), Timespan.FromHours(17)
Timespan.FromHours(17), Timespan.FromHours(19)

After realizing I was thinking about this problem in the wrong way I came to an approach that should work in most scenarios. If you come across some data that this doesn't work on please provide it to me and I'll fix it. Thanks!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
List<TimeRange> timeRanges = new List<TimeRange>();
timeRanges.Add(new TimeRange(TimeSpan.FromHours(2), TimeSpan.FromHours(3)));
timeRanges.Add(new TimeRange(TimeSpan.FromHours(8), TimeSpan.FromHours(9)));
timeRanges.Add(new TimeRange(TimeSpan.FromHours(1), TimeSpan.FromHours(5)));
timeRanges.Add(new TimeRange(TimeSpan.FromHours(3), TimeSpan.FromHours(6)));
List<Interval> intervals = new List<Interval>();
intervals.Add(new Interval(TimeSpan.FromHours(1), TimeSpan.FromHours(7)));
intervals.Add(new Interval(TimeSpan.FromHours(10), TimeSpan.FromHours(15)));
timeRanges.AddRange(intervals.Select(x => new TimeRange(x.start, x.end)));
timeRanges = TimeRange.ResolveOverlaps(timeRanges);
timeRanges.ForEach(x => Console.WriteLine($"{x.start} - {x.end}"));
Console.Read();
}
}
public class TimeRange
{
public TimeSpan start { get; set; }
public TimeSpan end { get; set; }
public TimeRange(TimeSpan st, TimeSpan en)
{
start = st;
end = en;
}
public static List<TimeRange> ResolveOverlaps(List<TimeRange> timeRanges)
{
var times = new List<TimeSpan>();
times.AddRange(timeRanges.Select(x => x.start));
times.AddRange(timeRanges.Select(x => x.end));
times = times.Distinct().OrderBy(x => x.Ticks).ToList();
timeRanges.Clear();
while (times.Count > 1)
{
timeRanges.Add(new TimeRange(times[0], times[1]));
times.RemoveAt(0);
}
return timeRanges;
}
}
public class Interval
{
public TimeSpan start { get; set; }
public TimeSpan end { get; set; }
public Interval(TimeSpan st, TimeSpan en)
{
start = st;
end = en;
}
}
}

Related

LINQ query for DateTime ranges overlapping in collection items

In the following case, the method CanUnloadAll checks if there are no overlap in the unloading times, considering all trucks.
The current scenario, should returns TRUE but it is returning FALSE.
What's wrong with the logic in the LINQ query?
using System;
using System.Collections.Generic;
using System.Linq;
public class UnloadingTime
{
public DateTime Start { get; private set; }
public DateTime End { get; private set; }
public UnloadingTime(DateTime start, DateTime end)
{
this.Start = start;
this.End = end;
}
}
public static class UnloadingTrucks
{
public static bool CanUnloadAll(IEnumerable<UnloadingTime> unloadingTimes)
{
return unloadingTimes.Any(
TruckA => unloadingTimes.Any(
TruckB => TruckB != TruckA &&
!((
TruckA.Start.Date >= TruckB.End.Date ||
TruckB.Start.Date >= TruckA.End.Date
))
));
}
public static void Main(string[] args)
{
var format = System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat;
UnloadingTime[] unloadingTimes = new UnloadingTime[]
{
new UnloadingTime(DateTime.Parse("3/4/2019 19:00", format), DateTime.Parse("3/4/2019 20:30", format)),
new UnloadingTime(DateTime.Parse("3/4/2019 22:10", format), DateTime.Parse("3/4/2019 22:30", format)),
new UnloadingTime(DateTime.Parse("3/4/2019 22:40", format), DateTime.Parse("3/4/2019 23:00", format))
};
Console.WriteLine(UnloadingTrucks.CanUnloadAll(unloadingTimes));
}
}
To make it easier, I am using .NET Fiddle.
https://dotnetfiddle.net/Mis663
Regards
Solution:
public static bool CanUnloadAll(IEnumerable<UnloadingTime> unloadingTimes)
{
bool hasOverlap = unloadingTimes.Any(
TruckA => unloadingTimes.Any(
TruckB => TruckB != TruckA &&
!((
TruckA.Start >= TruckB.End ||
TruckB.Start >= TruckA.End
))
));
return !hasOverlap;
}
You are using the DateTime.Date property, which is the date component without the time part. You must use the DateTime:
bool flag = TruckA.Start >= TruckB.End || TruckB.Start >= TruckA.End

How do I check if delegate returns true inside of a method?

I am trying to solve a task from my exam but I'm having trouble with delegates. The task asks me to define a method for class FoodTraker which takes a delegate of type Func<Portion,bool> as an argument and deletes all portions from the list which satisfy the delegate criteria. Also in main test the code by giving an argument to the method which will filter all the portions whose price is greater than 30 per weight.
public class Portion
{
public Portion(decimal price, double weight, double kcal)
{
Price = price;
Weight = weight;
Kcal = kcal;
}
public decimal Price { get; private set; }
public double Weight { get; private set; }
public double Kcal { get; private set; }
}
public class FoodTracker
{
public List<Portion> portions;
public Func<Portion, bool> PortionDelegate;
public FoodTracker()
{
portions = new List<Portion>();
}
public void Track(Portion portion)
{
portions.Add(portion);
}
public virtual Decimal CalculateProfit()
{
Decimal sum = 0;
foreach(var portion in portions)
{
sum += portion.Price;
}
return sum;
}
public void PortionRemover(Func<Portion,bool> portionDelegate)
{
portions.Remove(portion);
}
}
public class Program
{
static void Main(string[] args)
{
Portion portion1 = new Portion(10m, 500, 1000);
Portion portion2 = new Portion(20m, 200, 200);
Portion portion3 = new Portion(5m, 50, 500);
Portion portion4 = new Portion(30m,1000, 700);
NutritionalTracker tracker = new NutritionalTracker(699, 501);
List<Portion> portions = new List<Portion>();
tracker.Track(portion1);
tracker.Track(portion2);
tracker.Track(portion3);
tracker.Track(portion4);
portions.Add(portion1);
portions.Add(portion2);
portions.Add(portion3);
portions.Add(portion4);
Console.WriteLine(tracker.CalculateProfit());
}
My first problem is that I don't know how to check the bool condition of the delegate inside the method which leads to me not being able to do the rest of the task.
You can remove like this
Add Linq extension functions on using block;
using System.Linq;
change your method for filter the list and set it.
public void PortionRemover(Func<Portion, bool> portionDelegate)
{
portions = portions.Where(portionDelegate).ToList();
}
call example
FoodTracker tracker = new FoodTracker();
tracker.PortionRemover(p => p.Price > 30);

Generic type and field accessing in C#

Instead of T (Type) if I use class name i.e. sampleclass this code works properly,
but if I use T, then it shows that
'T' does not contain a definition for 'TimeStamp' and no accessible
extension method 'TimeStamp' accepting a first argument of type 'T'
could be found (are you missing a using directive or an assembly
reference?)
//Get collection.
var collection = this.GetDatabaseConnection().GetCollection<T>
(collectionName);
//filter to read specific data.
var filter = Builders<T>.Filter.Where(result => result.TimeStamp >=
startTime && result.TimeStamp <= endTime);
List < T > queryData = collection.Find<T>(filter, null).ToList();
Previously it was as follows and working finely:
//Get collection.
var collection = this.GetDatabaseConnection().GetCollection<Sampleclass>.
(collectionName);
//filter to read data using specific timestamp.
var filter = Builders<Sampleclass>.Filter.Where(result =>
result.TimeStamp >=
startTime && result.TimeStamp <= endTime);
List < Sampleclass > queryData = collection.Find<Sampleclass>
(filter, null).ToList();
i would do it using a base class like this
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using System;
using System.Linq;
namespace StackOverflow
{
public class Program
{
public class Entity
{
[BsonId]
public ObjectId Id { get; set; }
public DateTime TimeStamp { get; set; }
}
public class Sample : Entity
{
public string Something { get; set; }
}
private static void Main(string[] args)
{
var collection = new MongoClient("mongodb://localhost:27017")
.GetDatabase("Test")
.GetCollection<Entity>("Samples");
var sample = new Sample
{
Id = ObjectId.GenerateNewId(),
Something = "something",
TimeStamp = DateTime.UtcNow
};
collection.InsertOne(sample);
var result = collection.AsQueryable()
.Where(s =>
s.TimeStamp >= DateTime.UtcNow.AddMinutes(-1) &&
s.TimeStamp <= DateTime.UtcNow.AddMinutes(1))
.ToArray();
}
}
}
The problem is that T doesn't have a TimeStamp property as it's a generic type. If you always have to access the TimeStamp of whatever type you process in your collection, you could consider using an interface that has TimeStamp as a get function. All the types you want to process would have to implement that interface.
public interface MyInterface
{
TimeSpan TimeStamp { get; } //or whatever type your are using for your TimeStamp property
}
var collection = this.GetDatabaseConnection().GetCollection<MyInterface>
(collectionName);
var filter = Builders<MyInterface>.Filter.Where(result => result.TimeStamp >=
startTime && result.TimeStamp <= endTime);
List<MyInterface> queryData = collection.Find<MyInterface>(filter,null).ToList();
This way it's still quite generic. If you want to still use type T you would have to work with reflection to find the property TimeStamp on your processed type.
Edit: Some advice for using type T: You always have to consider that T can be anything, from an integer to any other object imaginable. Sometimes it can be quite useful if you have a method for example that doesn't need to access any properties on the type T object it is given but is used for many different types. I lately used T to build a little ParseOrDefault method to catch values that can't be converted:
private T ParseOrDefault<T>(Object value, T defaultValue)
{
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch (Exception)
{
return defaultValue;
}
}
As soon as you have to deal with more specific use cases T is mostly not that useful.
after reading your comments on my first answer, i'd like to propose the following solution using MongoDB.Entities
hope this is more or less what you're trying to achieve...
using MongoDB.Entities;
using System;
using System.Collections.Generic;
namespace StackOverflow
{
public class Program
{
public class MySample : Entity
{
public DateTime TimeStamp { get; set; }
}
public class Sample1 : MySample
{
public string SomeProp { get; set; }
}
public class Sample2 : MySample
{
public string AnotherProp { get; set; }
}
private static void Main(string[] args)
{
new DB("test");
var sample1 = new Sample1 { SomeProp = "some prop value", TimeStamp = DateTime.UtcNow };
var sample2 = new Sample2 { AnotherProp = "another prop", TimeStamp = DateTime.UtcNow };
DB.Save(sample1);
DB.Save(sample2);
var s1 = FindSamples<Sample1>();
var s2 = FindSamples<Sample2>();
List<T> FindSamples<T>() where T : MySample
{
return DB.Find<T>()
.Many(s =>
s.TimeStamp >= DateTime.UtcNow.AddMinutes(-1) &&
s.TimeStamp <= DateTime.UtcNow.AddMinutes(1));
}
}
}
}

C# Constructor of class initialising and running script

Got some strange behaviour with a script, the constructor of my FileHandler class appears to be calling the class and running the script.
The class itself is only being referenced in VS15 once and that is by its constructor, the main method has not yet even have an object of FileHandler, which is not mentioned anywhere else in the code.
Surely this code should not be running?
Edit: I placed a breakpoint at the start of the Program.cs and began stepping through, but when I did this I noticed that the public class FileHandler becomes class Program and my constructor is 'replaced' by a Main method.
Is this something that C# does by design?
Programs.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using QuantConnect;
using QuantConnect.Securities;
using QuantConnect.Securities.Forex;
namespace TradingDaysFileChecker
{
class Program
{
static void Main(string[] args)
{
var securityType = SecurityType.Forex;
var ticker = TickType.Trade;
var marketHoursDatabase = MarketHoursDatabase.FromDataFolder();
var market = Market.FXCM;
var symbol = Symbol.Create(ticker.ToString(), securityType, market);
var marketHoursDbEntry = marketHoursDatabase.GetEntry(symbol.ID.Market, symbol.Value, symbol.ID.SecurityType);
var exchange = new ForexExchange(marketHoursDbEntry.ExchangeHours);
Console.ReadLine();
}
}
}
FileHandler.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using QuantConnect.Securities.Forex;
namespace TradingDaysFileChecker
{
public class FileHandler
{
private readonly StreamWriter _writeToFile;
private readonly List<Tuple<string, string>> _missingDays;
private readonly string _dataFilePath;
private readonly DateTime _startDate;
private readonly DateTime _endDate;
private readonly ForexExchange _exchange;
private readonly IEnumerable<DateTime> _validTradingDays;
private readonly string[] _forexSecuritiesFolders;
public FileHandler(ForexExchange exchange)
{
_startDate = new DateTime(2007, 04, 01);
_endDate = new DateTime(2016, 07, 25);
_exchange = exchange;
_writeToFile = new StreamWriter(#"C:\Users\RichardsPC\Documents");
_dataFilePath = #"C:\Users\RichardsPC\Desktop\export\exporter\forex\fxcm\minute\";
_forexSecuritiesFolders = Directory.GetDirectories(_dataFilePath);
_missingDays = new List<Tuple<string, string>>();
_validTradingDays = IterateOverDateRange(_exchange, _startDate, _endDate);
}
public void CheckForMissingFiles()
{
foreach (var validDay in _validTradingDays)
{
foreach (var forexSecurity in _forexSecuritiesFolders)
{
var fxPair = new DirectoryInfo(forexSecurity).Name;
var formattedDate = FormatDate(validDay);
var path = SetPath(_dataFilePath, fxPair, formattedDate);
if (!File.Exists(path))
{
_missingDays.Add(Tuple.Create(fxPair, formattedDate));
}
}
}
Results();
}
public void Results()
{
if (_missingDays.Count > 0)
{
foreach (var missingDay in _missingDays.OrderBy(md => md.Item1))
{
var formattedTupleOutput = missingDay.ToString().TrimStart('(').TrimEnd(')');
Console.WriteLine(formattedTupleOutput);
WriteResultsToFile(formattedTupleOutput);
}
}
else
{
var noFilesMissing = "No results missing";
Console.WriteLine(noFilesMissing);
WriteResultsToFile(noFilesMissing);
}
Console.WriteLine("Records missing: " + _missingDays.Count);
}
public void WriteResultsToFile(string result)
{
_writeToFile.WriteLine(result);
}
public static string FormattedFileName(string tradingDay)
{
return tradingDay + "_quote.zip";
}
public string FormatDate(DateTime validDay)
{
return validDay.ToString("yyyyMMdd");
}
public static string SetPath(string dataFilePath, string fxPair, string formattedDate)
{
return dataFilePath + fxPair + #"\" + FormattedFileName(formattedDate);
}
public IEnumerable<DateTime> IterateOverDateRange(ForexExchange exchange, DateTime start, DateTime end)
{
for (var day = start.Date; day.Date <= end.Date; day = day.AddDays(1))
if (exchange.IsOpenDuringBar(day.Date, day.Date.AddDays(1), false))
{
yield return day;
}
}
}
}
I figured out what was happening.
I had an old version of TradingDaysFileChecker.cs in the Documents folder of my system that I had backed up for some reason.
In that version all the file handling logic was inside Program.cs.
I refactored and extracted out the file handling to the new class.
For some reason when I was running the script, it was still using that old copy, even though it was not in the solution folder.
That's why the change in the class name and appeared to happen, it was jumping into the Program.cs and Main method of that other file, pulling it up from my Documents folder.
How that happened, I do not know.
I deleted the file from my Documents folder and now it is behaving correctly.

the binary expression cannot be converted to a predicate expression in LINQ

I want to get the week number of a certain given DateTime.
public static int WeekOf(DateTime? date)
{
if (date.HasValue)
{
GregorianCalendar gCalendar = new GregorianCalendar();
int WeekNumber = gCalendar.GetWeekOfYear(date.Value, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
return WeekNumber;
}
else
return 0;
}
And then I use the above method in:
public static List<ExpressionListDictionary> MyMethod(int weeknr)
{
using (DataAccessAdapter adapter = CreateAdapter())
{
LinqMetaData meta = new LinqMetaData(adapter);
var q = (from i in meta.Test
where WeekOf(i.StartDate) == weeknr
select new ExpressionListDictionary()
{
{"SomeId", i.Id}
}
);
return q.ToList();
}
}
And finally:
List<ExpressionListDictionary> someIDs = MyMethod(weeknr);
/* weeknr = 19 -> step by step debugging */
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: SD.LLBLGen.Pro.ORMSupportClasses.ORMQueryConstructionException: The binary expression '(WeekOf(Convert(EntityField(LPLA_1.StartDate AS StartDate))) == 19)' can't be converted to a predicate expression.
I do get the title error at return q.ToList(); . How can I achieve this?
I haven't ever used the LLBLGen library/framework... But probably this is the same problem that happens with Entity Framework/LINQ-to-SQL: you can't put in a query C# methods: the query must be executed by your db server, not locally, and your db server doesn't know how to execute C# code. So the problem would be in the
**WeekOf(i.StartDate)** == weeknr part of code (that is the only BinaryExpression of your query)
The exception you have posted is quite clear that the point of the error is the one that I have suggested. Then the reason is probably the one I gave you.
Taken from https://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=22861
If you are using SQL Server, that supports DATEPART(isowk, ...) (or if you have MySQL, that supports WEEK(...))
public class SQLFunctionMappings : FunctionMappingStore
{
public SQLFunctionMappings()
{
Add(new FunctionMapping(
typeof(SQLFunctions),
"WeekOf",
1,
"DATEPART(isowk, {0})") // For SQL Server
// "WEEK({0}, 1)") For MySQL
);
}
}
public class SQLFunctions
{
public static int? WeekOf(DateTime? date)
{
return null;
}
}
and then you would use it like:
Where there is the row with LinqMetaData meta = new LinqMetaData(adapter), add:
meta.CustomFunctionMappings = new SQLFunctionMappings();
and change the where:
where SQLFunctions.WeekOf(i.StartDate) == weeknr
Here there is the list of the functions already mapped by llblgen, and how to map other functions.
you can try to Make the method you have WeekOf takes string instead of Datetime?
public static int WeekOf(String dateAsString)
{
//if (!string.IsNullOrEmpty(dateAsString))
if (!dateAsString.equals(string.empty))
{
GregorianCalendar gCalendar = new GregorianCalendar();
int WeekNumber = gCalendar.GetWeekOfYear(date.Value, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
return WeekNumber;
}
else
return 0;
}
And then you use the above below in:
public static List<ExpressionListDictionary> MyMethod(int weeknr)
{
using (DataAccessAdapter adapter = CreateAdapter())
{
LinqMetaData meta = new LinqMetaData(adapter);
var q = (from i in meta.Test
where i.startDate != null && WeekOf(i.StartDate.tostring()) == weeknr
select new ExpressionListDictionary()
{
{"SomeId", i.Id}
}
);
return q.ToList();
}
}
Try something like this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyMethod(5);
}
public static int WeekOf(DateTime? date)
{
if (date.HasValue)
{
GregorianCalendar gCalendar = new GregorianCalendar();
int WeekNumber = gCalendar.GetWeekOfYear(date.Value, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
return WeekNumber;
}
else
return 0;
}
public static List<ExpressionListDictionary> MyMethod(int weeknr)
{
using (DataAccessAdapter adapter = CreateAdapter())
{
LinqMetaData meta = new LinqMetaData(adapter);
List<ExpressionListDictionary> q = (from i in meta.Test
where WeekOf(i.StartDate) == weeknr
select new ExpressionListDictionary()
{
Id = "SomeId"
}
).ToList();
return q;
}
}
public static DataAccessAdapter CreateAdapter()
{
return new DataAccessAdapter();
}
}
public class ExpressionListDictionary
{
public string Id { get; set; }
}
public class LinqMetaData
{
public List<LinqMetaData> Test {get;set;}
public DateTime StartDate {get;set;}
public int Id { get; set; }
public LinqMetaData(DataAccessAdapter adapter)
{
}
}
public class DataAccessAdapter : IDisposable
{
public void Dispose()
{
}
}
}
​

Categories

Resources