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();
Related
I want to find max time from a list of time formats, Its not exactly TimeSpan so parsing it won't help.
Please suggest a solution.
var duration = new List<string>() { "116:48:28", "110:36:28", "16:30:28"};
var maxts = duration.Max(x => TimeSpan.Parse(x));
You could try this. it will work in case, that you don't have something such as "1:70:10"...
duration.Select(d=>d.Replace(":", string.Empty)).Select(int.Parse).OrderBy(s=>s)
Or, to get tha value of maximal timestamp:
duration.Select(d => new {Text =d, Val = int.Parse(d.Replace(":", string.Empty))})
.OrderByDescending(x=>x.Val)
.First()
.Text;
You can use LINQ:
var sortedDuraion = duration
.OrderByDescending((s) => Convert.ToInt32(s.Split(':')[0]))
.ThenByDescending((s) => Convert.ToInt32(s.Split(':')[1]))
.ThenByDescending((s) => Convert.ToInt32(s.Split(':')[2]));
var max = sortedDuration.ElementAt(0);
Also you can parse this string to int (delete ":") and order as int values:
var sortedDuration = duration.OrderByDescending((s) => Convert.ToInt32(s.Replace(":", String.Empty)));
var max = sortedDuration.ElementAt(0);
you can try like this
var duration = new List<string>() { "116:48:28", "110:36:28", "16:30:28" };
List<TimeSpan> lst = new List<TimeSpan>();
foreach (var item in duration)
{
string[] data=item.Split(new char[]{':'});
lst.Add(new TimeSpan(int.Parse(data[0]),int.Parse(data[1]),int.Parse(data[2])));
}
var max = lst.Max();
You could use a regex:
internal static TimeSpan ParseSpecialTimespan(string toParse)
{
string pattern = #"^(\d+):([0-5]?\d{1}):([0-5]?\d{1})$";
var match = Regex.Match(toParse, pattern);
if (!match.Success) throw new ArgumentException(#"The provided string is not valid...");
int hours = int.Parse(match.Groups[1].ToString());
int minutes = int.Parse(match.Groups[2].ToString());
int seconds = int.Parse(match.Groups[3].ToString());
TimeSpan t = new TimeSpan(hours, minutes, seconds);
return t;
}
Here's how it's used:
var duration = new List<string>() { "116:48:28", "110:36:28", "16:30:28" };
string largestInput = "";
TimeSpan largestTimespan = new TimeSpan(0);
foreach (string d in duration)
{
TimeSpan parse = ParseSpecialTimespan(d);
if (parse > largestTimespan)
{
largestTimespan = parse;
largestInput = d;
}
}
System.Diagnostics.Debug.Print(#"Largest timespan is ""{0}"" from input ""{1}"".", largestTimespan.ToString(), largestInput);
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.
Hi guys i have a string Enumerable which consist of laptimes in this format "00:30" "1:50" (min:sec). And my ultimate goal is to return an enumerable that consists of TimeSpans of time differences between each time with these string objects converted into TimeSpans.
So for example if we have this: "00:30" "1:50" " "2:00" this will return 00:30 , 1:20 , 0:10.
I currently have this:
var laps = lapTimes.Select(s => TimeSpan.ParseExact(s, "mm:ss", System.Globalization.CultureInfo.CurrentCulture)
But it is not able to parse it. Also i dont know how i would do the time difference using linq because if i try subtracting the current time span from the one in the next index eventually i will receive an index out of bound exception.
Thanks , would appreciate the help.
I don't think LINQ fits to your case, when you need the previous item while iterating.
string format = #"h\:mm";
string[] laps = new[]{"00:30", "1:50", "2:00"};
var spans = new List<TimeSpan>();
spans.Add(TimeSpan.ParseExact(laps[0], format, null));
for (int i = 1; i < laps.Length; i++)
{
spans.Add(
TimeSpan.ParseExact(laps[i ], format, null) -
TimeSpan.ParseExact(laps[i -1], format, null)
);
}
I would use DateTime.ParseExact. Then you can use the indexer of the ordered List to access the previous TimeSpan and subtract it from the next TimeSpan:
var lapTimes = new[]{"00:30", "1:50","2:00"};
var laps = lapTimes.Select(s => DateTime.ParseExact(s, "m:ss", null).TimeOfDay)
.OrderBy(ts => ts)
.ToList();
var diffs = laps.Take(1) // take the first fix TimeSpan
.Concat(laps.Skip(1).Select((ts, i) => ts - laps[i])) // add all differences
.ToList();
DEMO
Edit: For the sake of completeness, i always forget that you need to escape the colons in TimeSpan.ParseExact, so this works also:
var laps = lapTimes.Select(s => TimeSpan.ParseExact(s, #"m\:ss", null))
....
Details: Custom TimeSpan Format Strings
One solution:
string[] lapTimes = { "00:30", "1:50", "2:00"};
var laps = lapTimes.Select(s => s.Split(":".ToCharArray()));
var times = laps.Select(s=> new TimeSpan(0, int.Parse(s[0]), int.Parse(s[1]))).Reverse();
List<TimeSpan> diffs = new List<TimeSpan>();
for (int i = 0; i < times.Count() - 1; i++)
{
diffs.Add(times.ElementAt(i) - times.ElementAt(i+1));
}
It's not LINQ, but this can also be done with a foreach loop:
List<string> stringList = new List<string>();
stringList.Add("00:30");
stringList.Add("01:50");
stringList.Add("02:00");
List<TimeSpan> timeSpanList = new List<TimeSpan>();
TimeSpan ts1 = new TimeSpan(0, 0, 0);
TimeSpan ts2 = new TimeSpan(0, 0, 0);
foreach (string item in stringList)
{
ts1 = TimeSpan.ParseExact(item, #"mm\:ss", System.Globalization.CultureInfo.CurrentCulture);
if (ts2.Equals(new TimeSpan(0,0,0)))
{
timeSpanList.Add(ts1);
}
else
{
timeSpanList.Add(ts1 - ts2);
}
ts2 = ts1;
}
I have a file which look like this (list is sorted):
2013-05-02 07:45:15
2013-05-02 09:25:01
2013-05-02 18:15:15
2013-05-04 08:45:15
2013-05-04 17:45:35
I would like to get only first and last time of each day in that list, so it means I would like to get this:
2013-05-02 07:45:15
2013-05-02 18:15:15
2013-05-04 08:45:15
2013-05-04 17:45:35
What would be most efficient way to achieve this?
I think I would be able to get this using cycle and inside of it compare dates. But maybe there is more efficient way ? Can this could be achieved using linq or some other things which could give me the result faster/cleaner way than using cycles?
var result = File.ReadAllLines("yourPath")
.Select(line => DateTime.ParseExact(line,
"yyyy-MM-dd HH:mm:ss",
CultureInfo.CurrentCulture))
.GroupBy(d => d.Date)
.SelectMany(g => new[] { g.Min(), g.Max() });
Group by the dates, and get the first and last line for each date. Example:
string[] dates = {
"2013-05-02 07:45:15",
"2013-05-02 09:25:01",
"2013-05-02 18:15:15",
"2013-05-04 08:45:15",
"2013-05-04 17:45:35"
};
var days =
dates.GroupBy(s => s.Substring(0, 10))
.Select(g => new { First = g.First(), Last = g.Last() });
foreach (var day in days) {
Console.WriteLine("{0} - {1}", day.First, day.Last);
}
Output:
2013-05-02 07:45:15 - 2013-05-02 18:15:15
2013-05-04 08:45:15 - 2013-05-04 17:45:35
To get the latest date, you can use:
var latestDate = datetimeCollection.Max();
and for the oldest one:
var oldestDate = datetimeCollection.Min();
Take note that datetimeCollection is a List/Collection of DateTime object.
Something similar to this should work.
var allDates = new[]
{
"2013-05-02 07:45:15",
"2013-05-02 09:25:01",
"2013-05-02 18:15:15",
"2013-05-04 08:45:15",
"2013-05-04 17:45:35"
};
var earliest = allDates.GroupBy(date => Convert.ToDateTime(date).Day).Select(g => new
{
first = g.Min(),
last = g.Max()
});
static void Main(string[] args)
{
var dates = System.IO.File.ReadAllLines(#"C:\dates.txt").Select(o => DateTime.Parse(o));
var res = new List<DateTime>();
foreach (var item in (from o in dates group o by o.Date into g select g.Key))
{
var dummy = dates.Where(o => o.Date == item);
res.Add(dummy.Max());
res.Add(dummy.Min());
}
}
You can group the dates by day, then sort the group contents and get the first and last element:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
class Program {
static void Main(string[] args) {
string[] fileContents = new string[] {
"2013-05-02 07:45:15",
"2013-05-02 09:25:01",
"2013-05-02 18:15:15",
"2013-05-04 08:45:15",
"2013-05-04 17:45:35"
};
List<DateTime> dates = fileContents.ToList().ConvertAll(s => DateTime.ParseExact(s, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture));
foreach (var datesInSameDay in dates.GroupBy(d => d.Day)) {
var datesList = datesInSameDay.ToList();
datesList.Sort();
Console.WriteLine(datesList.First());
Console.WriteLine(datesList.Last());
}
}
}
"What would be most efficient way to achieve this?"
Since you say that the list is sorted, then we can produce the required times with a single pass through the source times.
This avoids the grouping done by all the other answers. Such grouping would be necessary if the input data was unsorted, but because it is sorted in ascending date/time, we do not need to explicitly group.
First, let's define a simple converter to convert the strings to DateTime:
public IEnumerable<DateTime> ParseTimes(IEnumerable<string> times)
{
return times.Select(time => DateTime.ParseExact(time, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture));
}
Then we can write a simple method that returns the first and last time for each day (or the only time for a day if there is only one time given for that day) like so:
public IEnumerable<DateTime> FirstAndLastTimesForEachDay(IEnumerable<DateTime> times)
{
DateTime previous = DateTime.MinValue;
DateTime current = DateTime.MinValue;
foreach (var time in times)
{
if (previous.Date < time.Date)
{
if (previous != current)
yield return previous;
yield return time;
current = time;
}
previous = time;
}
if (previous != current)
yield return previous;
}
Then you can use it like so:
var times = new []
{
"2013-05-02 07:45:15",
"2013-05-02 09:25:01",
"2013-05-02 18:15:15",
"2013-05-03 12:34:45",
"2013-05-04 08:45:15",
"2013-05-04 17:45:35",
"2013-05-05 20:00:00"
};
foreach (var time in FirstAndLastTimesForEachDay(ParseTimes(times)))
Console.WriteLine(time);
Note that the implementation above only outputs a single DateTime for days that only contain a single DateTime. If instead you want the output to contain twice the single occurring DateTime for a day (so you always have a pair of DateTime per day even if the times are the same), then change the implementation to this:
public IEnumerable<DateTime> FirstAndLastTimesForEachDay(IEnumerable<DateTime> times)
{
DateTime previous = DateTime.MinValue;
foreach (var time in times)
{
if (previous.Date < time.Date)
{
if (previous != DateTime.MinValue)
yield return previous;
yield return time;
}
previous = time;
}
if (previous != DateTime.MinValue)
yield return previous;
}
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!