Find max time from string list - c#

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);

Related

How to get total annual sales

In my ASP.NET Core 6 Web API, I have used the code below to calculate Total Annual Sales (from Transactions):
private async Task<decimal> GetAllTotalMonthlyTransactions()
{
string transactionMonth = DateTime.Now.ToString("MM");
decimal totalMonthlyTransactions = 0;
var sales = await _dbContext.Sales.ToListAsync();
foreach (var item in sales)
{
var salesDate = item.CreatedAt.ToString();
var salesMonth = salesDate.Substring(3, 2);
if (transactionMonth == salesMonth)
{
totalMonthlyTransactions += item.Amount;
}
}
return totalMonthlyTransactions;
}
How to I re-write the code above to get TotalAnnualSales (Transactions)?
private async Task<decimal> GetAllTotalMonthlyTransactions()
{
int currentYear = DateTime.Now.Year;
return (await dbContext.Sales.Where(t=>t.CreatedAt.Year==currentYear).ToListAsync()).Sum(item=>item.Amount);
}
private async Task<decimal> GetAllTotalMonthlyTransactions()
{
decimal totalAnnualTransactions = 0;
int _year = DateTime.Now.Year;
var sales = await dbContext.Sales.Where(y=>y.CreatedAt.Year==_year).ToListAsync();
// The first method
foreach (var item in sales)
{
totalAnnualTransactions += item.Amount;
}
// The second method
//double totalAnnualTransactions= sales.Sum(item => item.Amount);
return totalAnnualTransactions;
}
Instead of converting the datetimes to strings, try accessing them as ints. You can do this with .Month or .Year respectively
You can try to calculate from and to date range based on a specified month, then query/filter sales data with these from and to date range, like below.
var date1 = DateTime.Now;
var days = DateTime.DaysInMonth(date1.Year, date1.Month);
var from = new DateTime(date1.Year, date1.Month, 1, 0, 0, 0);
var to = new DateTime(date1.Year, date1.Month, days, 23, 59, 59);
var totalMonthlyTransactions = sales.AsEnumerable().Where(s => s.CreatedAt >= from && s.CreatedAt <= to).Select(s => s.Amount).Sum();
I hope following code will work for you...
private async Task<IEnumerable<object>> GetMonthlyTransactions(int year)
{
var starting = new DateTime(year, 1, 1).Date;
var sales = await context.Sales.Where(f => f.SalesDate >= starting).Select(s => new
{
Date = s.SalesDate.ToString("MM-yyyy"),
Amount = s.NetAmount
}).ToListAsync();
var finaldata = sales.GroupBy(s => s.Date, (d, a) => new { Date = d, Am = a }).Select(l => new
{
Date = l.Date,
Amount = l.Am.Sum(h => h.Amount)
});
return finaldata;
}

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();

Loading CSV in C#

I want to search the penultimate row in the first.csv which date is 1975-01-03 and the Lemon value is 17.0, after I search in the second.csv the same date which lemon is 19.0
After catching both values, I compute the difference 17.0 - 19.0 = -2.0
The next step is to sum the difference -2 to all Lemon's values in second.csv from the date 1975-01-03 to the end 1975-01-09
The final step is to write the third.csv where we add the first.csv until the date 1975-01-02 and the sum we've done with second.csv from 1975-01-03 to the end 1975-01-09
first.csv
Date,Lemon
1974-12-31,19.0
1975-01-02,18.0
1975-01-03,17.0
1975-01-06,16.0
second.csv
Date,Lemon
1975-01-02,18.0
1975-01-03,19.0
1975-01-06,19.5
1975-01-07,19.5
1975-01-08,18.0
1975-01-09,17.0
third.csv
Date,Lemon
1974-12-31,19.0
1975-01-02,18.0
1975-01-03,17.0
1975-01-06,17.5
1975-01-07,17.5
1975-01-08,16.0
1975-01-09,15.0
All in all, the read from CSV is not as important as to obtain the third result in an Array, DataTable, Dictionary or whatever. Thanks
Start with a handy struct to make the coding easier:
public struct Line
{
public DateTime Timestamp;
public decimal Lemon;
}
Then you can write a simple function to load your CSV files:
Func<string, Line[]> readCsv =
fn =>
File
.ReadLines(fn)
.Skip(1)
.Select(x => x.Split(','))
.Select(y => new Line()
{
Timestamp = DateTime.Parse(y[0]),
Lemon = decimal.Parse(y[1])
})
.ToArray();
Now the rest is just a reading the files and a couple of LINQ queries before writing out the results:
Line[] first = readCsv(#"C:\_temp\first.csv");
Line[] second = readCsv(#"C:\_temp\second.csv");
Line difference =
(
from pen in first.Skip(first.Length - 2).Take(1)
from mtch in second
where mtch.Timestamp == pen.Timestamp
select new Line()
{
Timestamp = pen.Timestamp,
Lemon = pen.Lemon - mtch.Lemon
}
).First();
IEnumerable<string> result =
new [] { "Date,Lemon" }
.Concat(
first
.Where(x => x.Timestamp < difference.Timestamp)
.Concat(
second
.Where(x => x.Timestamp >= difference.Timestamp)
.Select(x => new Line()
{
Timestamp = x.Timestamp,
Lemon = x.Lemon + difference.Lemon
}))
.Select(x => String.Format(
"{0},{1}",
x.Timestamp.ToString("yyyy-MM-dd"),
x.Lemon)));
File.WriteAllLines(#"C:\_temp\third.csv", result);
The result I get is:
Date,Lemon
1974-12-31,19.0
1975-01-02,18.0
1975-01-03,17.0
1975-01-06,17.5
1975-01-07,17.5
1975-01-08,16.0
1975-01-09,15.0
This looks like homework, I strongly advice you to do this exercice by yourself by learning about LINQ (just google it). If you are stuck or can't find the solution here is a way to do it :
class LemonAtDate
{
public DateTime Date { get; set; }
public double Value { get; set; }
public LemonAtDate(DateTime Date, double Value)
{
this.Date = Date;
this.Value = Value;
}
public static List<LemonAtDate> LoadFromFile(string filepath)
{
IEnumerable<String[]> lines = System.IO.File.ReadLines(filepath).Select(a => a.Split(','));
List<LemonAtDate> result = new List<LemonAtDate>();
int index = 0;
foreach (String[] line in lines)
{
index++;
if (index == 1) continue; //skip header
DateTime date = DateTime.ParseExact(line[0], "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture);
double value = Double.Parse(line[1], System.Globalization.CultureInfo.InvariantCulture);
result.Add(new LemonAtDate(date, value));
}
return result;
}
public static void WriteToFile(IEnumerable<LemonAtDate> lemons, string filename)
{
//Write to file
using (var sw = new System.IO.StreamWriter(filename))
{
foreach (LemonAtDate lemon in lemons)
{
sw.WriteLine("Date,Lemon");//Header
string date = lemon.Date.ToString("yyyy-MM-dd");
string value = lemon.Value.ToString();
string line = string.Format("{0},{1}", date, value);
sw.WriteLine(line);
}
}
}
}
static void Main(string[] args)
{
//Load first file
List<LemonAtDate> firstCsv = LemonAtDate.LoadFromFile("first.csv");
//Load second file
List<LemonAtDate> secondCsv = LemonAtDate.LoadFromFile("second.csv");
//We need at least two rows
if (firstCsv.Count >= 2)
{
//Penultimate row in first file
LemonAtDate lemonSecondLast = firstCsv[firstCsv.Count - 2];
//Find the value 19 in the second file
LemonAtDate lemonValue19 = secondCsv.Where(x => x.Value == 19).FirstOrDefault();
//Value found
if (lemonValue19 != null)
{
double delta = lemonSecondLast.Value - lemonValue19.Value;
//Get the items between the dates and add the delta
DateTime dateStart = new DateTime(1975, 1, 3);
DateTime dateEnd = new DateTime(1975, 1, 9);
IEnumerable<LemonAtDate> secondFileSelection = secondCsv.Where(x => x.Date >= dateStart && x.Date <= dateEnd)
.Select(x => { x.Value += delta; return x; });
//Create third CSV
List<LemonAtDate> thirdCsv = new List<LemonAtDate>();
//Add the rows from the first file until 1975-01-02
DateTime threshold = new DateTime(1975, 1, 2);
thirdCsv.AddRange(firstCsv.Where(x => x.Date <= threshold));
//Add the rows from the second file
thirdCsv.AddRange(secondFileSelection);
//Write to file
LemonAtDate.WriteToFile(thirdCsv, "third.csv");
}
}
}
There are better ways of doing this, I took a quick and dirty procedural approach instead of an OO one. I also took a peek at the other answer and I see he parsed out the datetimes. I decided not to since you weren't doing any math specifically based on that. However his answer would be more flexible as with datetimes you can do more operations in the future.
List<string> csvfile1Text = System.IO.File.ReadAllLines("file1.csv").ToList();
List<string> csvfile2Text = System.IO.File.ReadAllLines("file2.csv").ToList();
Dictionary<string, double> csv1Formatted = new Dictionary<string, double>();
Dictionary<string, double> csv2Formatted = new Dictionary<string, double>();
Dictionary<string, double> csv3Formatted = new Dictionary<string, double>();
foreach (string line in csvfile1Text)
{
var temp= line.Split(',');
csv1Formatted.Add(temp[0], Double.Parse(temp[1]));
}
foreach (string line in csvfile2Text)
{
var temp = line.Split(',');
csv2Formatted.Add(temp[0], Double.Parse(temp[1]));
}
//operation 1
var penultimate = csv1Formatted["1974-01-03"];
var corrsponding = csv2Formatted["1974-01-03"];
var difference = penultimate - corrsponding;
//operation 2
var start = csv2Formatted["1974-01-03"];
var end = csv2Formatted["1974-01-09"];
var intermediate = csv2Formatted.Keys.SkipWhile((element => element != "1974-01-03")).ToList();
Dictionary<string, double> newCSV2 = new Dictionary<string, double>();
foreach (string element in intermediate)
{
var found = csv2Formatted[element];
found = found + difference;
newCSV2.Add(element, found);
}
//operation 3
intermediate = csv1Formatted.Keys.TakeWhile((element => element != "1975-01-03")).ToList();
foreach (string element in intermediate)
{
var found = csv1Formatted[element];
csv3Formatted.Add(element, found);
}
foreach (KeyValuePair<string,double> kvp in newCSV2)
{
csv3Formatted.Add(kvp.Key,kvp.Value);
}
//writing CSV3
StringBuilder sb = new StringBuilder();
foreach (KeyValuePair<string,double> kvp in csv3Formatted)
{
sb.AppendLine(kvp.Key + "," + kvp.Value);
}
System.IO.File.WriteAllText("C:\\csv3.csv", sb.ToString());
This is my favorite to use with CSV
https://github.com/kentcb/KBCsv
and if you want to work with csv entries as model for each row:
http://www.filehelpers.net/quickstart/
I hope you find this helpful.
Good luck :) Enjoy coding

How to find the postFix of a string

I have a list of filename in my folder and I convert the folder into a list. The file name is the same except for their postfix (number after the file name).
I want to get the latest postfix in that file list.
For example: In my list, I have 4 files. file_20160101 has the largest postfix. There for I want to get the name 'file_20160101' and add to my string.
[0] C:\\Desktop\\file_20130101.csv
[1] C:\\Desktop\\file_20140101.csv
[2] C:\\Desktop\\file_20150101.csv
[3] C:\\Desktop\\file_20160101.csv
Here is my code:
string fileDir = "C:\\Desktop\\"
List<string> list = new List<string>(Directory.GetFiles(fileDir));
string largestPrefix = //file_20160101.csv
You can get it like this:
List<string> list = new List<string>(Directory.GetFiles(fileDir));
var numericParts = Directory.GetFiles(fileDir).Select(f => int.Parse(Regex.Match(f, #"\d+").Value)).ToArray();
var max = numericParts.Max(); //do whatever you want with the largest number
You can even call the Max directly in the case you don't need other numeric parts:
var max = Directory.GetFiles(fileDir).Select(f => int.Parse(Regex.Match(f, #"\d+").Value)).ToArray().Max();
Considering you are looking for the most recent file.You can try this (but yeah linq solution is better.:))
string output = string.Empty;
DateTime max = new DateTime();
foreach(string str in list)
{
DateTime tempDate = new DateTime();
DateTime.TryParseExact(str.Substring(str.IndexOf("_") + 1, 8), "yyyyMMdd", CultureInfo.GetCultureInfo("en-Us"), DateTimeStyles.None , out tempDate);
if(tempDate > max)
{
max = tempDate;
output = str;
}
}
Below code can give you result very quickly if you know that prefix will be always like "file_"
list.Max(f => int.Parse(f.Replace("file_", string.Empty)))
You can do something like this
DateTime date = new DateTime();
string result = "";
foreach (var item in list)
{
int year = int.Parse(item.Substring(5 , 4));
int month = int.Parse(item.Substring(9, 2));
int day = int.Parse(item.Substring(11, 2));
DateTime currentDate = new DateTime(year, month, day);
if (currentDate > date)
{
date = currentDate;
result = item;
}
}
Console.WriteLine(result);

Getting the time difference using LINQ from a list of TimeSpans and parsing the time in as only minutes and seconds

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;
}

Categories

Resources