Find minimal and maximal date in array using LINQ? - c#

I have an array of classes with a property Date, i.e.:
class Record
{
public DateTime Date { get; private set; }
}
void Summarize(Record[] arr)
{
foreach (var r in arr)
{
// do stuff
}
}
I have to find the earliest (minimum) and the latest (maximum) dates in this array.
How can I do that using LINQ?

If you want to find the earliest or latest Date:
DateTime earliest = arr.Min(record => record.Date);
DateTime latest = arr.Max(record => record.Date);
Enumerable.Min, Enumerable.Max
If you want to find the record with the earliest or latest Date:
Record earliest = arr.MinBy(record => record.Date);
Record latest = arr.MaxBy(record => record.Date);
See: How to use LINQ to select object with minimum or maximum property value

old school solution without LINQ:
DateTime minDate = DateTime.MaxValue;
DateTime maxDate = DateTime.MinValue;
foreach (var r in arr)
{
if (minDate > r.Date)
{
minDate = r.Date;
}
if (maxDate < r.Date)
{
maxDate = r.Date;
}
}

The two in one LINQ query (and one traversal):
arr.Aggregate(
new { MinDate = DateTime.MaxValue,
MaxDate = DateTime.MinValue },
(accDates, record) =>
new { MinDate = record.Date < accDates.MinDate
? record.Date
: accDates.MinDate,
MaxDate = accDates.MaxDate < record.Date
? record.Date
: accDates.MaxDate });

Using lambda expressions:
void Summarise(Record[] arr)
{
if (!(arr == null || arr.Length == 0))
{
List<Record> recordList = new List<Record>(arr);
recordList.Sort((x,y) => { return x.Date.CompareTo(y.Date); });
// I may have this the wrong way round, but you get the idea.
DateTime earliest = recordList[0];
DateTime latest = recordList[recordList.Count];
}
}
Essentially:
Sort into a new list in order of date
Select the first and last elements of that list
UPDATE: Thinking about it, I'm not sure that this is the way to do it if you care at all about performance, as sorting the entire list will result in many more comparisons than just scanning for the highest / lowest values.

I'd just make two properties Min,Max, assign them the value of the first item you add to the array, then each time you add a new item just check if its DateTime is less or greater than the Min Max ones.
Its nice and fast and it will be much faster than iterating through the array each time you need to get Min Max.

Related

Combined to date and time fields into one when both datetime stamps

I am trying to combine the date and time part of a table so that I can set appointments correctly I was wondering if someone could help me my syntax is not being compiled
public List<Appointment> getAppointments (DateTime AppointmentDate)
{
List<Appointment> query = _sourceEntities.Appointments.Where(a => a.ApptDate == AppointmentDate && a.ClientID==6).ToList();
return _sourceEntities.Appointments.Select(r =>
{
var newAppointment = new Appointment();
DateTime date = new DateTime(r.ApptDate.Year, r.ApptDate.Month, r.ApptDate.Day, r.ApptTime.Hour, r.ApptTime.Minute, r.ApptTime.Second);
newAppointment.ApptDate = date;
return newAppointment();
});
}
The error is hapening here return newAppointment(); I am not sure why its saying method name expected I want to have all the fields of the old list but also this new combined date time field.
Here is the example of the data to best explain
https://i.imgur.com/rCtx0lt.png
Edit 2
The _sourceEntites is decalred heree at the top of the class
public class SourceContext
{
public SMBASchedulerEntities _sourceEntities = new SMBASchedulerEntities();
public static List<Appointment> getAppointments(DateTime apptDate)
List<Appointment> query = _sourceEntities.Appointments.Where(a => a.ApptDate == AppointmentDate && a.ClientID==6).ToList();
return _sourceEntities.Appointments.Select(r =>
{
var newAppointment = new Appointment();
DateTime date = new DateTime(r.ApptDate.Year, r.ApptDate.Month, r.ApptDate.Day, r.ApptTime.Hour, r.ApptTime.Minute, r.ApptTime.Second);
newAppointment.ApptDate = date;
return newAppointment();
});
}
newAppointment is an object variable, by using ('s the compiler treats newAppointment as a method, that is what the error message states. Removing the ('s should solve the problem.
An alternative way to return the result would be
public List<Appointment> getAppointments (DateTime AppointmentDate)
{
List<Appointment> query = _sourceEntities.Appointments.Where(a => a.ApptDate == AppointmentDate && a.ClientID==6).ToList();
return _sourceEntities.Appointments.Select(r => new Appointment
{
newAppointment.ApptDate = ew DateTime(r.ApptDate.Year, r.ApptDate.Month, r.ApptDate.Day, r.ApptTime.Hour, r.ApptTime.Minute, r.ApptTime.Second);
//map other variables here
});
}
The problem with your code is in the line: return newAppointment();. You are treating the object newAppointment like a method when you add the parenthesis after it. Instead, you can just do:
return newAppointment;
A slightly simpler approach would be to create a new Appointment in your Select statement. This will return an IEnumerable of Appointment objects, which we can the call ToList() on before returning. I also included a .Where() clause to match what you had in your query. You can remove that line if it's not needed.
public static List<Appointment> getAppointments(DateTime apptDate)
{
return _sourceEntities.Appointments
.Where(a => a.ApptDate == apptDate && a.ClientID == 6) // Remove if not needed
.Select(r =>
new Appointment
{
ApptDate = new DateTime(r.ApptDate.Year, r.ApptDate.Month, r.ApptDate.Day,
r.ApptTime.Hour, r.ApptTime.Minute, r.ApptTime.Second)
})
.ToList();
}
One other thing to note is that you are doing an equality comparison on the two date objects, so you will only be getting appointments for the exact date and time of the argument passed in. In case you want to get all the appointments for the day, you can just use the Date portion of the DateTime objects for comparison:
// Compare the Date portion of the argument to get all appointments for that day
.Where(a => a.ApptDate.Date == appointment.Date && a.ClientID == 6)

how to get the records between specific time in entity framework

i want to fetch the records between 7am and 3pm . How to write linq query for the same?
code
var items = Pirs.Where(a => !a.dataFrame.EndsWith("AAAAAAAAAAA=") && (fromDate == null || fromDate.Value.Date <= TimeZoneInfo.ConvertTimeFromUtc(Convert.ToDateTime(a.timestamp), TimeZoneInfo.FindSystemTimeZoneById("India Standard Time")).Date) && (toDate == null || toDate.Value.Date >= TimeZoneInfo.ConvertTimeFromUtc(Convert.ToDateTime(a.timestamp), TimeZoneInfo.FindSystemTimeZoneById("India Standard Time")).Date) && (a.timestamp.Hour > 7 || a.timestamp.Hour < 15))
.GroupBy(a => a.dataFrame.Substring(a.dataFrame.Length - 12))
.Select(g => g.First()).OrderBy(a => a.timestamp);
sample data
timestamp "2017-07-21T14:06:02.203Z"
The way your question is written, it is difficult to understand exactly what you're asking, so I will give you a general way of achieving what (I think) you want.
Say you have a class, Foo:
class Foo
{
public DateTime date { get; set; }
}
Given a List<Foo>, you can get all instances of Foo in that list who's date property is between certain hours like so:
var myList = new List<Foo>();
int startHour = 7; // 7am
int endHour = 15; // 3pm
var result = myList.Where(item => item.date.Hour >= startHour && item.date.Hour <= endHour);
The result enumerations contains all items in myList whos date property's Hour field is between startHour and endHour inclusive.
Something to note, you could use an extension method to greatly simplify this:
public static bool Between(DateTime input, int x, int y)
{
return (input.Hour => x && input.Hour <= y);
}
var result = myList.Where(item => item.date.Between(7, 15));
However, you mentioned you were using Entity Framework, which this second solution is incompatible with, since when you query a DbSet, that operation isn't performed by your program, but translated into SQL by EF and executed by the DB provider instead. Using an extension method in your query would require loading up everything into memory first, which I do not recommend.

Linq for a list of lists

class Bar
{
public DateTime Time { get; set; }
public double Price { get; set; }
}
class Instrument
{
public List<Bar> Bars { get; set; }
public string Name { get; set; }
public Instrument(string name, string path) {
// set the Bars list here reading from files
}
}
Here are two simplified versions of my classes. I'm creating a custom backtesting platform for currencies. The current problem is to chop off bars where I don't have price data on every instrument.
I read prices from an XML file.
var xlinqBarLists = xlinqInstruments.Select(i => i.Bars);
which is basically
List<List<Bar>> xlinqBarLists
What I want to do is loop through each individual list, find the latest start date and the earliest end date, then lop off all bars outside of that time window. My hacked together code is
var xlinqInstruments = root.Elements("instrument").Select( a =>
new Instrument( a.Element("name").Value, a.Element("path").Value ) );
var xlinqBarLists = xlinqInstruments.Select(i => i.Bars);
DateTime latestStartDate = DateTime.MinValue;
DateTime earliestEndDate = DateTime.MaxValue;
foreach (List<Bar> bars in xlinqBarLists)
{
if (bars.Min(b => b.Time) > latestStartDate)
latestStartDate = bars.Min(b => b.Time);
if (bars.Max(b => b.Time) < earliestEndDate)
earliestEndDate = bars.Max(b => b.Time);
}
foreach (List<Bar> barList in xlinqBarLists)
{
var timeWindowBars = from bar in barList
where bar.Time >= latestStartDate && bar.Time <= earliestEndDate
select bar;
// I need some way to overwrite the original Instrument.Bars property with timeWindowBars
// Suggestions?
}
Can I do this more quickly and efficiently by skipping the foreach loops?
For latest start date and earliest end date you can use
DateTime latestStartDate = xlinqInstruments.Max(i => i.Bars.Min(bar => bar.Time));
DateTime earliestEndDate = xlinqInstruments.Min(i => i.Bars.Max(bar => bar.Time));
And for the last part maybe you would like to add a parameterless constructor for ´Instrument´ and then
var result = xlinqInstruments
.Select(i=>new Instrument()
{
Name = i.Name,
Bars = i.Bars.Where(bar => bar.Time >= latestStartDate
&& bar.Time <=earliestEndDate)
.ToList()
});
Here's an answer if my comment above, (which I've copy/pasted below) turns out to be the case.
Are you wanting to set the Bars property of each instrument to a
List containing only those objects that fall within the minimum
end date and maximum start date across all Bar objects (including
those present on other Instruments)?
// Get the value of the earliest Bar.Time on each Instrument, and select the most recent of those.
DateTime latestStartDate = xlinqInstruments.Max(instrument => instrument.Bars.Min(bar => bar.Time));
// Get the value of the latest Bar.Time on each Instrument, and select the earliest of those.
DateTime earliestEndDate = xlinqInstruments.Min(instrument => instrument.Bars.Max(bar => bar.Time));
// Overwrite the Bars collection of each instrument with its contents truncated appropriately.
// I'd suggest doing this with a foreach loop as opposed to what I've provided below, but that's just me.
xlinqInstruments.ForEach(instrument =>
{
instrument.Bars = instrument.Bars.Where(obj => obj.Time >= latestStartDate && obj.Time <= earliestEndDate).ToList();
});
It may be worth nothing that the ForEach method requires you to call .ToList() on the xlinqInstruments collection first. In my code, I assume the collection has already been materialized to List<Instrument>
You may also be interested in linq's Enumerable.SelectMany method.
Enumerable.SelectMany Method (IEnumerable, Func>)
Projects each element of a sequence to an IEnumerable and flattens the resulting sequences into one sequence.
Call ToList before assigning to xlinqBarLists:
var xlinqBarLists = xlinqInstruments.Select(i => i.Bars).ToList();
Otherwise you're parsing the same XML over and over again.
You should most likely call ToList when creating xlinqInstruments as well, if you want to update them later.

Compare 2 lists with LINQ

I want to get all date values from dateRange where each datetime is the DayOfWeek.Friday,..Monday,..Sunday.
The Intersect did not work because I guess those 2 lists are not real intersections... and they have a different type: DateTime vs. Enum.DayOfWeek
This gives me only all fridays but I also want the Mondays,Sundays... without using the OR operator.
var dateRange = _dateService.GetDateRange(startDate, endDate).Where(d => visibleWeekDays.Any(v => d.DayOfWeek == v)).ToList();
These are both lists I have to compare somehow:
IEnumerable<DateTime> dateRange = _dateService.GetDateRange(startDate, endDate);
IEnumerable<DayOfWeek> visibleWeekDays = new List<DayOfWeek>
{
DayOfWeek.Friday,
DayOfWeek.Monday,
DayOfWeek.Sunday,
};
Please DO not write the full ling query as solution.
Just write the linq extension methods in random order I have to use to solve the riddle.
Just for the funs and learning sake :)
UPDATE
See my input datetime values and the output I want:
BUT be aware, the visibleWeekDays list is not static. There can be a dynamic number of values in this collection. Therefore I can and do not want to use the && or || operator.
When you find yourself in a position of wanting an intersection, but where the types of the two collections aren't the same, it usually means you want a Join. An intersection, by definition, is a join in which the two collections are of the same type, where the key selector is "itself", and where the result selector just picks one of the items at random (since they must be equal, by the definition of intersection). Since not all of these restrictions apply to you all you need to do is step out to the more general Join.
Just as a demonstration of this, here is an implementation of Intersect using just Join:
public static IEnumerable<T> Intersect<T>(this IEnumerable<T> first
, IEnumerable<T> second, IEqualityComparer<T> comparer)
{
return first.Join(second, x => x, x => x, (a, b) => a, comparer);
}
The DayOfWeek can just select itself as a key, and then you just need a method of getting a DayOfWeek object out of a DateTime for the key selector. For your result selector you only need to grab the DateTime object; you shouldn't need the DayOfWeek object anymore.
Whenever you see yourself writing a LINQ solution that has a Where(x => collection.Any(... or Contains or some other search operation inside of a Where odds are you should be using a Join instead, if applicable (you should at least ask yourself if you should be using a Join).
Since you don't want a full implementation I'll put it below in a spoiler tag. Don't look at it if you want to write the code yourself:
public static IEnumerable FilterDaysOfWeek(IEnumerable dates
, IEnumerable daysOfWeek)
{
return dates.Join(daysOfWeek
, date => date.DayOfWeek
, day => day
, (date, day) => date);
}
If you convert DayOfWeek to an integer, you can do a simple > and < comparison to get valid values, so just a .Where query should be good.
You could use Contains on the visibleWeekDays in your Where clause.
If you want to get all the dates in GetDateRange that have a DayOfWeek that matches those in visibleWeekdays you could use the following Linq statement:
_dateService.GetDateRange(startDate, endDate)
.Where(d=> visibleWeekdays.Contains(d.DayOfWeek));
Below is a full test of this in action:
class Test
{
static void Main(string[] args)
{
var weekdays = new[] { DayOfWeek.Friday, DayOfWeek.Saturday, DayOfWeek.Sunday };
var result = GetDateRange(DateTime.Today, DateTime.Today.AddDays(14))
.Where(d => weekdays.Contains(d.DayOfWeek));
}
public static IEnumerable<DateTime> GetDateRange(DateTime start, DateTime end)
{
DateTime date = start;
do
{
yield return date;
date = date.AddDays(1);
}
while (date < end);
}
}

How to compare only date components from DateTime in EF?

I am having two date values, one already stored in the database and the other selected by the user using DatePicker. The use case is to search for a particular date from the database.
The value previously entered in the database always has time component of 12:00:00, where as the date entered from picker has different time component.
I am interested in only the date components and would like to ignore the time component.
What are the ways to do this comparison in C#?
Also, how to do this in LINQ?
UPDATE:
On LINQ to Entities, the following works fine.
e => DateTime.Compare(e.FirstDate.Value, SecondDate) >= 0
Use the class EntityFunctions for trimming the time portion.
using System.Data.Objects;
var bla = (from log in context.Contacts
where EntityFunctions.TruncateTime(log.ModifiedDate) == EntityFunctions.TruncateTime(today.Date)
select log).FirstOrDefault();
Source: http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/84d4e18b-7545-419b-9826-53ff1a0e2a62/
UPDATE
As of EF 6.0 and later EntityFunctions is replaced by DbFunctions.
NOTE: at the time of writing this answer, the EF-relation was unclear (that was edited into the question after this was written). For correct approach with EF, check Mandeeps answer.
You can use the DateTime.Date property to perform a date-only comparison.
DateTime a = GetFirstDate();
DateTime b = GetSecondDate();
if (a.Date.Equals(b.Date))
{
// the dates are equal
}
I think this could help you.
I made an extension since I have to compare dates in repositories filled with EF data and so .Date was not an option since it is not implemented in LinqToEntities translation.
Here is the code:
/// <summary>
/// Check if two dates are same
/// </summary>
/// <typeparam name="TElement">Type</typeparam>
/// <param name="valueSelector">date field</param>
/// <param name="value">date compared</param>
/// <returns>bool</returns>
public Expression<Func<TElement, bool>> IsSameDate<TElement>(Expression<Func<TElement, DateTime>> valueSelector, DateTime value)
{
ParameterExpression p = valueSelector.Parameters.Single();
var antes = Expression.GreaterThanOrEqual(valueSelector.Body, Expression.Constant(value.Date, typeof(DateTime)));
var despues = Expression.LessThan(valueSelector.Body, Expression.Constant(value.AddDays(1).Date, typeof(DateTime)));
Expression body = Expression.And(antes, despues);
return Expression.Lambda<Func<TElement, bool>>(body, p);
}
then you can use it in this way.
var today = DateTime.Now;
var todayPosts = from t in turnos.Where(IsSameDate<Turno>(t => t.MyDate, today))
select t);
If you use the Date property for DB Entities you will get exception:
"The specified type member 'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported."
You can use something like this:
DateTime date = DateTime.Now.Date;
var result = from client in context.clients
where client.BirthDate >= date
&& client.BirthDate < date.AddDays(1)
select client;
To do it in LINQ to Entities, you have to use supported methods:
var year = someDate.Year;
var month = ...
var q = from r in Context.Records
where Microsoft.VisualBasic.DateAndTime.Year(r.SomeDate) == year
&& // month and day
Ugly, but it works, and it's done on the DB server.
Here's a different way to do it, but it's only useful if SecondDate is a variable you're passing in:
DateTime startDate = SecondDate.Date;
DateTime endDate = startDate.AddDays(1).AddTicks(-1);
...
e => e.FirstDate.Value >= startDate && e.FirstDate.Value <= endDate
I think that should work
You can also use this:
DbFunctions.DiffDays(date1, date2) == 0
you can use DbFunctions.TruncateTime() method for this.
e => DbFunctions.TruncateTime(e.FirstDate.Value) == DbFunctions.TruncateTime(SecondDate);
Just always compare the Date property of DateTime, instead of the full date time.
When you make your LINQ query, use date.Date in the query, ie:
var results = from c in collection
where c.Date == myDateTime.Date
select c;
This is how I do this.
DateTime date_time_to_compare = DateTime.Now;
//Compare only date parts
context.YourObject.FirstOrDefault(r =>
EntityFunctions.TruncateTime(r.date) == EntityFunctions.TruncateTime(date_to_compare));
//Note for Linq Users/Coders
This should give you the exact comparison for checking if a date falls within range when working with input from a user - date picker for example:
((DateTime)ri.RequestX.DateSatisfied).Date >= startdate.Date &&
((DateTime)ri.RequestX.DateSatisfied).Date <= enddate.Date
where startdate and enddate are values from a date picker.
Without time than try like this:
TimeSpan ts = new TimeSpan(23, 59, 59);
toDate = toDate.Add(ts);
List<AuditLog> resultLogs =
_dbContext.AuditLogs
.Where(al => al.Log_Date >= fromDate && al.Log_Date <= toDate)
.ToList();
return resultLogs;
You can user below link to compare 2 dates without time :
private bool DateGreaterOrEqual(DateTime dt1, DateTime dt2)
{
return DateTime.Compare(dt1.Date, dt2.Date) >= 0;
}
private bool DateLessOrEqual(DateTime dt1, DateTime dt2)
{
return DateTime.Compare(dt1.Date, dt2.Date) <= 0;
}
the Compare function return 3 different values: -1 0 1 which means dt1>dt2, dt1=dt2, dt1
Try this... It works fine to compare Date properties between two DateTimes type:
PS. It is a stopgap solution and a really bad practice, should never be used when you know that the database can bring thousands of records...
query = query.ToList()
.Where(x => x.FirstDate.Date == SecondDate.Date)
.AsQueryable();
I have resolved error using EfCore FromSqlRaw method.
var sql =
$"select * from \"ProgressBooks\" where date(\"Date\") = date('{today.Date.ToString("yyyy-MM-dd")}') and \"GroupId\" = {groupId}";
var todayProgressBook = _context.ProgressBooks.FromSqlRaw(sql).FirstOrDefault();

Categories

Resources