Expression<Func> method gives convertion error when used in linq query - c#

I know there are lots of similar questions about this, but I could not find my answer among them.
My function is like this:
public static Expression<Func<DateTime, DateTime?, DateTimeOffset>> GetDTOFromLocalAndUTC(DateTime localTime, DateTime? utcTime)
{
int documentDateOffset = 0;
DateTimeOffset result;
if (utcTime.HasValue)
{
documentDateOffset = ((TimeSpan)(localTime - utcTime.Value)).Hours;
}
result = new DateTimeOffset(localTime, TimeSpan.FromHours(documentDateOffset));
return (a, b) => result;
}
And my linq query is like this:
var q = from a in context.MyDBEntity
where {some condition}
select new MyDomainClass
{
DocumentDateInDateTimeOffsetFormat = GetDTOFromLocalAndUTC(a.LocalDocumentDate, a.UTCDocumentDate)
};
But it gives convertion error.

You can't do it that way...
public static DateTimeOffset GetDTOFromLocalAndUTC(DateTime localTime, DateTime? utcTime)
{
int documentDateOffset = 0;
DateTimeOffset result;
if (utcTime.HasValue)
{
documentDateOffset = ((TimeSpan)(localTime - utcTime.Value)).Hours;
}
result = new DateTimeOffset(localTime, TimeSpan.FromHours(documentDateOffset));
return result;
}
and then
var q = (from a in context.MyDBEntity
where {some condition}
select new
{
a.LocalDocumentDate,
a.UTCDocumentDate
})
.AsEnumerable()
.Select(a => new MyDomainClass
{
DocumentDateInDateTimeOffsetFormat = GetDTOFromLocalAndUTC(a.LocalDocumentDate, a.UTCDocumentDate)
});
First you select the two fields you need, then locally (AsEnumerable()) you call your function.

Related

check discontinuity of multiple ranges in a list

I would like to ask you if there's a way by Linq to check discontinuity of multiple ranges, for example we have a class AgeRange:
public class AgeRange
{
public int firstValue {get;set;}
public int secondValue {get;set;}
}
var ageRange1 = new AgeRange(0,2); // interval [0,2]
var ageRange2 = new AgeRange(4,10); // interval [4,10]
var ageRange3 = new AgeRange(11,int.MaxValue); // interval [11,+oo[
var ageRangeList = new List<AgeRange>();
ageRangeList.Add(ageRange1);
ageRangeList.Add(ageRange2);
ageRangeList.Add(ageRange3);
in this example we have a discontinuity between first range and second range.
is there a way in Linq to check discontinuity between elements in ageRangeList ?
Thanks for you help.
Assuming firstValue always <= secondValue (for the same element), you can try to use Aggregate:
var start = ageRangeList
.OrderBy(a => a.firstValue).Dump()
.First();
var result = ageRangeList
.OrderBy(a => a.firstValue)
.Aggregate(
(hasGap: false, s: start.secondValue),
(tuple, range) =>
{
if (tuple.hasGap)
{
return tuple;
}
else
{
var max = Math.Max(tuple.s, tuple.s+1); //hacky overflow protection
if (max < range.firstValue)
{
return (true, tuple.s);
}
else
{
return (false, Math.Max(tuple.s, range.secondValue));
}
}
})
.hasGap;
The downside of such approach is that it still will need to loop through all age ranges.
If you want to find first discontinuity and use that information elsewhere
public static IEnumerable<AgeRange> FindDiscontinuity(List<AgeRange> ageRangeList) {
foreach(var ageRange in ageRangeList.Zip(ageRangeList.Skip(1), (a, b) => new {Prev = a, Current = b})) {
if(ageRange.Prev.SecondValue != ageRange.Current.FirstValue) {
yield return ageRange.Prev;
yield return ageRange.Current;
break;
}
}
}
public static void Main()
{
var ageRange1 = new AgeRange(0, 2);
var ageRange2 = new AgeRange(4, 10);
var ageRange3 = new AgeRange(11, int.MaxValue);
var ageRangeList = new List<AgeRange>();
ageRangeList.Add(ageRange1);
ageRangeList.Add(ageRange2);
ageRangeList.Add(ageRange3);
var result = FindDiscontinuity(ageRangeList);
foreach(var ageRange in result) {
Console.WriteLine("{0}, {1}", ageRange.FirstValue, ageRange.SecondValue);
}
}
You can change the function so it can return boolean value instead of data.

C# Convert List<string> to List<TimeSpan>

I've seen examples of how to convert a string to a TimeSpan, here is one example:
How to Convert string "07:35" (HH:MM) to TimeSpan
But what is the most efficient way to convert a List<string> to List<TimeSpan>?
I've tried something along these lines, but isn't working:
var result = new TimeSpan;
var appointmentStartTimesConverted = appointmentStartTimes
.Select(i => result = TimeSpan.TryParse(i, out result))
.ToList();
Should do the job:
var appointmentStartTimes = new List<string>{"7:45", "0:0","a"};
var appointmentStartTimesConverted = appointmentStartTimes
.Select(i =>
{
TimeSpan result;
if(TimeSpan.TryParse(i, out result))
return new Nullable<TimeSpan>(result);
return null;
})
.Where(x => x.HasValue)
.ToList();
No Linq solution - a simple loop is enough:
List<TimeSpan> result = new List<TimeSpan>(appointmentStartTimes.Count);
foreach (var item in appointmentStartTime)
if (TimeSpan.TryParse(item, out var span)) // out var - C# 7.0 feature
result.Add(span);
in below code, I'm having a list of string (which is supposed to have Timespan string but it can hold any string)
so first I'm checking for the condition if it passes TryParse and only then I am converting particular string into Timespan and add it into List of Timespan
List<string> str = new List<string>();
str.Add("07:30");
str.Add("amit");
TimeSpan res = new TimeSpan();
List<TimeSpan> ts = str.Where(x => TimeSpan.TryParse(x, out res) != false).Select(y => res).ToList();
This will take care of invalid time span string.
TryParse returns bool. Use Parse in try/catch block to ensure all values will be processed or use output parameter from TryParse
There is no need to declare local variable result.
var appointmentStartTimes = new List<string>();
//fill values here
var appointmentStartTimesConverted = appointmentStartTimes
.Select(i =>
{
try
{
return TimeSpan.Parse(i);
}
catch
{
return default(TimeSpan);
}
}
).ToList();
//anothey way
appointmentStartTimesConverted = appointmentStartTimes
.Select(i =>
{
if (TimeSpan.TryParse(i, out var result))
return result;
return default(TimeSpan);
}
).ToList();
I'd write something using Linq, but with an intermediate Function, like this:
List<string> lst = new List<string>() { "7:35", "3:45", "0:23" };
Func<string, TimeSpan> GetTS = (str) =>
{
TimeSpan.TryParse(str, out TimeSpan ts);
return ts;
};
var tsLst = lst.Select(r => GetTS(r)).ToList();

How to sum Timespan of subtraction between two datetimes in Linq when using group by?

I have items with the properties :
public int ClientId {get;set;}
public DateTime StartDateTime{get;set;}
public DateTime EndDateTime{get;set;}
And I want to calculate the total of the difference between all the datetimes of each client with group by , but this :
var retVal = (from t items group t by ClientId into z
select new
{
ClientId = z.Key,
TimeSpanClientTotal = z.Sum(h => (h.EndDateTime - h.StartDateTime))
}).ToList();
Doesn't work since Sum doesn't work well for TimeSpan , which is the return value of the difference between two DateTimes object .
Any suggestion how can I get the total TimeSpan of each client ?
Thanks
Enumerable.Sum is just an extension method you call on an IEnumerable. There is nothing special to it so you can easily create another extension method that sums timespans:
static class TimeSpanExtensions
{
public static TimeSpan Sum<TSource>(this IEnumerable<TSource> enumerable,
Func<TSource,TimeSpan?> func )
{
return enumerable.Aggregate(TimeSpan.Zero, (total, it) =>
total+=(func(it)??TimeSpan.Zero);
}
}
Assuming your class definition is
class Record
{
public int ClientId { get; set; }
public DateTime StartDateTime { get; set; }
public DateTime EndDateTime { get; set; }
public Record(int clientId, DateTime startDateTime, DateTime endDateTime)
{
ClientId = clientId;
StartDateTime = startDateTime;
EndDateTime = endDateTime;
}
}
You can write the same code you would for the numeric types:
var items = new[] {
new Record(1, DateTime.Now, DateTime.Now.AddHours(1)),
new Record(1, DateTime.Now, DateTime.Now.AddHours(1)),
new Record(1, DateTime.Now, DateTime.Now.AddHours(1))};
var total=items.Sum(h=>(h.EndDateTime-h.StartDateTime));
var grouped= (from t in items
group t by t.ClientId into z
select new
{
ClientId = z.Key,
TimeSpanClientTotal = z.Sum(h => (h.EndDateTime - h.StartDateTime))
}).ToList();
You can also use Enumerable.Aggregate directly:
var total= items.Aggregate(TimeSpan.Zero, (current, it) =>
current += (it.EndDateTime-it.StartDateTime));
The code can be uglier but you can do a lot more than simple addition.
You can use the .TotalMilliseconds Property
var retVal = (from t items group t by ClientId into z
select new
{
ClientId = z.Key,
TimeSpanClientTotal = z.Sum(h => (h.EndDateTime - h.StartDateTime).TotalMilliseconds)
}).ToList();
You can write it like that:
h.EndDateTime.Subtract(h.StartDateTime).TotalDays();
In my case, all proposed solutions didn't work. They caused error related with LINQ restrictions. So, my workaround looks like this:
var appointments = (from apps in DbContext.ClientAppointments
where apps.StartDate.Value.Date == date.Date
select new
{
SpecialistId = apps.SpecialistId,
Duration = (apps.EndDate.Value - apps.StartDate.Value).TotalSeconds
}).ToList();
var result = (from apps in appointments
group apps by apps.SpecialistId into g
select new AppointmentsDurationDailyDto
{
SpecialistId = g.Key ?? 0,
Date = date.Date,
Duration = g.Sum(apps => apps.Duration)
}).ToList();
In this solution first .ToList(); is important to make next grouping statement on client. In case if you need to get TimeSpan Duration you can easily convert it back by using TimeSpan.FromSeconds(Duration)

Group and Map into Object with an inner aggregate Object

I have many Object1A, say IEnumerable<Object1A>.
public class Object1A {
public string text;
public datetime date;
public decimal percent;
public Object3 obj;
}
Many of these objects have the same text, date, and percent, but have a different obj. I want to transform the list such that the output will be a IEnumerable<Object1B> where
public class Object1B{
public string text;
public datetime date;
public decimal percent;
public IEnumerable<Object3> objs;
}
My current apporach is a bit clunky, and listed below
IEnumerable<Object1A> a = GetSomeConstruct();
var lookup = a.ToLookup( t => t.text);
var b = new List<Object1b>();
foreach(var group in lookup){
var itemA = group.first();
var itemB = new Object1b(){
text = itemA.text,
date = itemA.date,
percent = itemA.percent
};
itemB.objs = pair.Select(t => t.obj);
b.Add(itemB);
}
Can this approach be refined? It doesn't seem to run to slow, but it seems like it could be better. I'm looking for a more terse approach if possible.
edit: yeah, this was a dumb question, cudos to the downvote....
simple answer
var b_objects = a_objects.GroupBy(t => new {t.Text})
.Select( t => new Object1B
{ Text = t.Key.Text,
Percent = t.First().Percent,
Date = t.First().Date,
Objs = t.Select( o => o.Obj).ToList()
});
Guess you want something like this?
var b = from a in GetSomeConstruct()
group a.obj by new { a.text, a.date, a.percent } into grp
select new Object1B
{
text = grp.Key.text,
date = grp.Key.date,
percent = grp.Key.percent,
objs = grp
};
You can use anonymous types with join and group by. Their GetHashCode and Equals overloads operate on each member.

Can I use an anonymous type in a List<T> instead of a helper class?

I need a list with some objects for calculation.
my current code looks like this
private class HelperClass
{
public DateTime TheDate {get;set;}
public TimeSpan TheDuration {get;set;}
public bool Enabled {get;set;}
}
private TimeSpan TheMethod()
{
// create entries for every date
var items = new List<HelperClass>();
foreach(DateTime d in GetAllDatesOrdered())
{
items.Add(new HelperClass { TheDate = d, Enabled = GetEnabled(d), });
}
// calculate the duration for every entry
for (int i = 0; i < items.Count; i++)
{
var item = items[i];
if (i == items.Count -1) // the last one
item.TheDuration = DateTime.Now - item.TheDate;
else
item.TheDuration = items[i+1].TheDate - item.TheDate;
}
// calculate the total duration and return the result
var result = TimeSpan.Zero;
foreach(var item in items.Where(x => x.Enabled))
result = result.Add(item.TheDuration);
return result;
}
Now I find it a bit ugly just to introduce a type for my calculation (HelperClass).
My first approach was to use Tuple<DateTime, TimeSpan, bool> like I usually do this but since I need to modify the TimeSpan after creating the instance I can't use Tuple since Tuple.ItemX is readonly.
I thought about an anonymous type, but I can't figure out how to init my List
var item1 = new { TheDate = DateTime.Now,
TheDuration = TimeSpan.Zero, Enabled = true };
var items = new List<?>(); // How to declare this ???
items.Add(item1);
Using a projection looks like the way forward to me - but you can compute the durations as you go, by "zipping" your collection with itself, offset by one. You can then do the whole method in one query:
// Materialize the result to avoid computing possibly different sequences
var allDatesAndNow = GetDatesOrdered().Concat(new[] { DateTime.Now })
.ToList();
return allDatesNow.Zip(allDatesNow.Skip(1),
(x, y) => new { Enabled = GetEnabled(x),
Duration = y - x })
.Where(x => x.Enabled)
.Aggregate(TimeSpan.Zero, (t, pair) => t + pair.Duration);
The Zip call pairs up each date with its subsequent one, converting each pair of values into a duration and an enabled flag. The Where call filters out disabled pairs. The Aggregate call sums the durations from the resulting pairs.
You could do it with LINQ like:
var itemsWithoutDuration = GetAllDatesOrdered()
.Select(d => new { TheDate = d, Enabled = GetEnabled(d) })
.ToList();
var items = itemsWithoutDuration
.Select((it, k) => new { TheDate = it.d, Enabled = it.Enabled,
TheDuration = (k == (itemsWithoutDuration.Count - 1) ? DateTime.Now : itemsWithoutDuration[k+1].TheDate) - it.TheDate })
.ToList();
But by that point the Tuple is both more readable and more concise!

Categories

Resources