Avg,max,min,sum from string? - c#

Hello i got array of string, and they are durations made by myself in format H:M:S:MS
Example strings:
0:0:4:410
0:0:1:425
0:0:1:802
0:0:1:509
0:0:1:674
0:0:1:628
0:0:2:76
How can i sum/avg/min/max values of these items in arraylist?
Arraylist name is arrayLL.
I'm new in c# so hope someone will show me how to work with strings.
The function that adds to array is:
if (Session["DT"].ToString() != "")
{
TimeSpan ts = ((DateTime)Session["DT2"]).Subtract((DateTime)Session["DT"]);
Session["TimeL"] = ts.Hours.ToString() + ":"
+ ts.Minutes.ToString() + ":"
+ ts.Seconds.ToString() + ":"
+ ts.Milliseconds.ToString();
}

Assuming the numbers represent hours, minutes, seconds, and milliseconds you can try the following:
// Empty list you will populate:
List<int> durationsInMilliseconds = new List<int>();
// Loop through your existing data, and calculate all
// durations into milliseconds:
foreach (string word in yourDurationArray)
{
string[] values = s.Split(':');
int hoursAsMilliseconds = Integer.parse(values[0]) * 60 * 60 * 1000;
int minutesAsMilliseconds = Integer.parse(values[1]) * 60 * 1000;
int secondsAsMilliseconds = Integer.parse(values[2]) * 1000;
int sumDurationAsMilliseconds = hoursAsMilliseconds +
minutesAsMilliseconds +
secondsAsMilliseconds +
Integer.parse(values[3]);
durationsInMilliseconds.add(sumDurationAsMilliseconds);
}
Now you should have a list of type Integer (durationsInMilliseconds) which contains the numbers in a single comparable format. With this, you should be able to do whichever calculations you need.
(PS: If you need the result in the same format as the original input-data, you will have to add an operation for calculating back from MS into hours, minutes and seconds..)

Since i guess they are Durations so i you should do this
var enu_ts = yourvariable.OfType<string>().Select(x =>
TimeSpan.Parse(x, #"h\:m\:s\:fff", CultureInfo.InvariantCulture));
Max
var max = enu_ts.Max().ToString();
Min
var max = enu_ts.Min().ToString();

foreach(string s in dateString)
{
spanList[i] = TimeSpan.Parse(s);
total=total.Add(spanList[i++]);
}
Response.Write("Max TimeSpan:"+spanList.Max<TimeSpan>());
Response.Write("Min TimeSpan:" + spanList.Min<TimeSpan>());
Response.Write("Total Sum of TimeSpan:"+total);

Related

How to sort a List<String> in descending order as fast as possible

I am trying to find a way to sort a List as fast as possible. I know about "bucket sort" which I will use(I think?). But before doing that. I think I am looking for the fastest possible clean algorithm first, - and then use that in bucket sort?
The strings looks like below where I add 100.000 elements in a loop:
-0,awb/aje - ddfas/asa - asoo/qwa
-1,awb/aje - ddfas/asa - asoo/qwa
-2,awb/aje - ddfas/asa - asoo/qwa
So I want to sort in a descending order for the first comma delimited argument which is a double like: -0, -1, -2 etc..
I have tried 3 Approaches where only the Approach 1 actually sorts correctly. Approach 2 and 3 do not sort entirely correctly in a descending order numeric wise.
So the approach 1 which sorts correctly do this in 30 seconds.
The thing is that I will have about 3 Million elements and not only 100.000 like in this example which then should take at least 900 seconds or more.
My question is how can we sort 100.000 or more correctly 3 million elements as fast as possible?
Running: sortingtestBENCHMARKS() will show the results
public void sortingtestBENCHMARKS()
{
List<String> dataLIST = new List<String>(); List<String> sortedLIST = new List<String>(); String resultString = ""; int index = 0;
DateTime starttime = DateTime.Now; DateTime endtime = DateTime.Now; TimeSpan span = new TimeSpan();
for (double i = 0; i < 100000; i+=1)
{
dataLIST.Add("-" + i + "," + "awb/aje" + " - " + "ddfas/asa" + " - " + "asoo/qwa");
}
dataLIST = shuffle(dataLIST);
/*--------------------------------------------------------------------------*/
//APPROACH 1: 30 seconds (Sorts correctly in descending order)
starttime = DateTime.Now;
dataLIST = sortLIST(dataLIST);
endtime = DateTime.Now;
span = endtime - starttime;
resultString = "Approach 1: " + span.TotalSeconds;
dataLIST = shuffle(dataLIST);
/*--------------------------------------------------------------------------*/
//APPROACH 2: 55 seconds (Sorts INcorrectly in descending order)
starttime = DateTime.Now;
for (int i = 0; i < dataLIST.Count; i++)
{
index = sortedLIST.BinarySearch(dataLIST[i]);
if (index < 0)
{
sortedLIST.Insert(~index, dataLIST[i]);
}
}
endtime = DateTime.Now;
span = endtime - starttime;
resultString = resultString + "\nApproach 2: " + span.TotalSeconds;
/*--------------------------------------------------------------------------*/
//APPROACH 3: 2 seconds (Sorts INcorrectly in descending order)
starttime = DateTime.Now;
dataLIST.Sort(); //1.6 seconds
endtime = DateTime.Now;
span = endtime - starttime;
resultString = resultString + "\nApproach 3: " + span.TotalSeconds;
/*--------------------------------------------------------------------------*/
MessageBox.Show("Elapsed Times:\n\n" + resultString);
}
List<String> sortLIST(List<String> theLIST)
{
System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
theLIST.Sort(new Comparison<String>((a, b) =>
{
int result = 0;
double ad = 0;
double bd = 0;
NumberFormatInfo provider = new NumberFormatInfo();
provider.NumberGroupSeparator = ",";
provider.NumberDecimalSeparator = ".";
provider.NumberGroupSizes = new int[] { 5 };
ad = Convert.ToDouble(a.Replace("a", "").Replace("c", "").Split(',')[0], provider);
bd = Convert.ToDouble(b.Replace("a", "").Replace("c", "").Split(',')[0], provider);
if (ad < bd)
{
result = 1;
}
else if (ad > bd)
{
result = -1;
}
return result;
}));
return theLIST;
}
List<String> shuffle(List<String> list)
{
var randomizedList = new List<String>();
var rnd = new Random();
while (list.Count != 0)
{
var index = rnd.Next(0, list.Count);
randomizedList.Add(list[index]);
list.RemoveAt(index);
}
return randomizedList;
}
It seems to me that you can just split your string on the , character, strip the - off the first item in the split array, and then use OrderBy on that result:
var sorted = dataLIST.OrderBy(i => double.Parse(i.Split(',')[0].TrimStart('-'))).ToList();
I made a copy of your code and then used the one working method you have and compared it to running an OrderBy on the split string method mentioned above. The OrderBy/Split method is a little more than 30 times faster.
public static void sortingtestBENCHMARKS()
{
var dataLIST = new List<string>();
// Create the list
for (var i = 0; i < 100000; i ++)
{
dataLIST.Add("-" + i + "," + "awb/aje" + " - " + "ddfas/asa" + " - " + "asoo/qwa");
}
// Shuffle the list
dataLIST = shuffle(dataLIST);
// Make two copies of the same shuffled list
var copy1 = dataLIST.ToList();
var copy2 = dataLIST.ToList();
// Use a stopwatch for measuring time when benchmark testing
var stopwatch = new Stopwatch();
/*--------------------------------------------------------------------------*/
//APPROACH 1: 2.83 seconds (Sorts correctly in descending order)
stopwatch.Start();
copy2 = sortLIST(copy2);
stopwatch.Stop();
Console.WriteLine($"sortLIST method: {stopwatch.Elapsed.TotalSeconds} seconds");
/*--------------------------------------------------------------------------*/
//APPROACH 2: 0.09 seconds (Sorts correctly in descending order)
stopwatch.Restart();
copy1 = copy1.OrderBy(i => double.Parse(i.Split(',')[0].TrimStart('-'))).ToList();
stopwatch.Stop();
Console.WriteLine($"OrderBy method: {stopwatch.Elapsed.TotalSeconds} seconds");
// Ensure that the lists are sorted identically
Console.WriteLine($"Lists are the same: {copy1.SequenceEqual(copy2)}");
}
Output
var sortedList = theLIST.OrderByDescending(s=>s);
You should optimize the inner loop as hard as you can, because most of the processing time is spent there. I suggest implementing the string tokenizer yourself, since you only need the first token and the string is very uniform. The second optimization you might have is multiply all the number with -1 so sorting the list in reverse order is straightforward. Something like this:
private static double getNumberFromString(String s){
int posFirstComma=0;
for (; posFirstComma<s.length() && s.charAt(posFirstComma)!=','; posFirstComma++);
return Convert.toDouble(s.subString(0, posFirstComma)*(-1);
}
myData.sort(new Comparision<String>((a,b)=> getNumberFromString(a)-getNumberFromString(b));
I personally won't touch the sort algorithm in the library itself because it is already thoroughly optimized. Just optimize everything in the for loop.

how to format string to hh:mm:ss?

I am trying to make a digital clock in C#. I have 3 counter objects for hours, minutes and seconds. It has to be in format hh:mm:ss.
What I managed to do
String hours = _hours.Value.ToString();
String minutes = _minutes.Value.ToString();
String seconds = _seconds.Value.ToString();
if (hours.Length == 1)
{
hours = "0" + hours;
}
if (minutes.Length == 1)
{
minutes = "0" + minutes;
}
if (seconds.Length == 1)
{
seconds = "0" + seconds;
}
return hours + ":" + minutes + ":" + seconds;
It works but I am trying for a more efficient way of doing it using String.format. I have tried few different regular expressions but have been unsuccessful.
string b = string.Format("{0:D2}:{1:00}:{2:d2}", hours, minutes, seconds);
Cheers
Have you tried converting the string into int, before trying your string.Format? Something like below
string.Format("{0:00}:{1:00}:{2:00}",
int.Parse(hours), int.Parse(minutes), int.Parse(seconds));
If _hours.Value, _minutes.Value and _seconds.Value are numeric types, then you can use the below code which is more efficient
string.Format("{0:00}:{1:00}:{2:00}",
_hours.Value, _minutes.Value, _seconds.Value);
You can always try something a bit different:
DateTime dt = new DateTime(2000, 1, 1, hours, minutes, seconds); // just ignore year, month and day
var x = dt.ToString("HH:mm:ss");
IMO if you already have a string type it's useless to cast it to int or DateTime just to stringify it again after that.
I would use String.PadLeft method :
string h = hours.PadLeft(2, '0');
string m = minutes.PadLeft(2, '0');
string s = seconds.PadLeft(2, '0');
string result = h + ":" + m + ":" + s;
PadLeft will make your string to always have length at least of the value passed as a first parameter ( 2 in this case ) and fill "empty" places with the value passed in second parameter ( 0 in this case ).
Check this online
You can use the formatting options available on the ToString() method of DateTime like below:
(new DateTime(1900, 1, 1, _hours.Value, _minutes.Value, _seconds.Value)).ToString("HH:mm:ss");
You can try using:
string b = DateTime.Now.ToString("HH:mm:ss");
If you have some date variable, you can do so:
string b = dateVar.ToString("HH:mm:ss");
If you want solution exactly for your example, then:
return string.Format("{0:00}:{1:00}:{2:00}",
int.Parse(hours),
int.Parse(minutes),
int.Parse(seconds)
);

Timespan data type C#

I have a timespan value which is difference of two Datetime values. It is like this :
myvalue = {41870.01:44:22.8365404}
I have also a string which represents another timespan value and it is like this :
ttime= "23:55" // which means 23 minutes and 55 seconds
I want to check if myvalue is smaller than ttime. Here is my attempt:
if(myvalue < Timespan.Parse(ttime))
//do this
else
//do that
Is my approach correct enough? Do I need to do something else? Thanks.
The only problem i can see is that TimeSpan.Parse is limited to 24 hours. So you would need this workaround:
string ttime = "48:23:55"; // 48 hours
TimeSpan ts = new TimeSpan(int.Parse(ttime.Split(':')[0]), // hours
int.Parse(ttime.Split(':')[1]), // minutes
int.Parse(ttime.Split(':')[2])); // seconds
Apart from that it's absolutely fine to compare two timespan in this way.
If it can contain two or three parts(with or without hours), this should be safer:
string[] tokens = ttime.Split(':');
// support only two or three tokens:
if (tokens.Length < 2 || tokens.Length > 3)
throw new NotSupportedException("Timespan could not be parsed successfully: " + ttime);
TimeSpan ts;
if(tokens.Length == 3)
ts = new TimeSpan(int.Parse(tokens[0]), int.Parse(tokens[1]),int.Parse(tokens[2]));
else
ts = new TimeSpan(0, int.Parse(tokens[0]), int.Parse(tokens[1]));
For what it's worth, here is a method which is written from scratch which parses a string to TimeSpan by allowing two or three tokens(hh,mm,ss or mm,ss) and also supports weird formats like 100:100:100 (100 hours + 100 minutes + 100 seconds):
public static TimeSpan SaferTimeSpanParse(string input, char delimiter = ':')
{
if (string.IsNullOrEmpty(input))
throw new ArgumentNullException("input must not be null or empty", "input");
string[] tokens = input.Split(delimiter);
if (tokens.Length < 2 || tokens.Length > 3)
throw new NotSupportedException("Timespan could not be parsed successfully: " + input);
int i;
if (!tokens.All(t => int.TryParse(t, out i) && i >= 0))
throw new ArgumentException("All token must contain a positive integer", "input");
int[] all = tokens.Select(t => int.Parse(t)).ToArray();
int hoursFinal = 0, minutesFinal = 0, secondsFinal = 0;
int seconds = all.Last();
secondsFinal = seconds % 60;
minutesFinal = seconds / 60;
int minutes = (all.Length == 3 ? all[1] : all[0]) + minutesFinal; // add current minutes which might already be increased through seconds
minutesFinal = minutes % 60;
hoursFinal = minutes / 60;
hoursFinal = all.Length == 3 ? all[0] + hoursFinal : hoursFinal; // add current hours which might already be increased through minutes
return new TimeSpan(hoursFinal, minutesFinal, secondsFinal);
}
tested with your string, my string and a weird one:
Console.WriteLine(SaferTimeSpanParse("23:55"));
Console.WriteLine(SaferTimeSpanParse("48:23:55"));
Console.WriteLine(SaferTimeSpanParse("100:100:100"));
Output:
00:23:55
2.00:23:55
4.05:41:40

Displaying bandwidth speed in a user-friendly format

I'm looking for a string conversion method that will receive an input of KB/s and converts it to the easiest readable format possible.
e.g. 1500 b/s = 1.46 Kb/s
e.g. 1500 Kb/s = 1.46 Mb/s
e.g. 1500 Mb/s = 1.46 Gb/s
Thanks
Try this:
var ordinals = new [] {"","K","M","G","T","P","E"};
long bandwidth = GetTheBandwidthInBitsPerSec();
decimal rate = (decimal)bandwidth;
var ordinal = 0;
while(rate > 1024)
{
rate /= 1024;
ordinal++;
}
output.Write(String.Format("Bandwidth: {0} {1}b/s",
Math.Round(rate, 2, MidpointRounding.AwayFromZero),
ordinals[ordinal]));
The ordinals (prefixes) available here are Kilo-, Mega-, Giga-, Tera-, Peta-, Exa-. If you really think your program will be around long enough to see Zettabit and Yottabit network bandwidths, by all means throw the Z and Y prefix initials into the array.
To convert from one formatted string to the other, split on spaces, look at the term that will be the number, and then search the term immediately following for one of the prefixes. Find the index of the ordinal in the array, add 1, and multiply by 1024 that many times to get to bits per second:
var bwString= GetBandwidthAsFormattedString(); //returns "Bandwidth: 1056 Kb/s";
var parts = String.Split(bwString, " ");
var number = decimal.Parse(parts[1]);
var ordinalChar = parts[2].First().ToString();
ordinalChar = ordinalChar = "b" ? "" : ordinalChar;
var ordinal = ordinals.IndexOf(ordinalChar)
... //previous code, substituting the definition of ordinal
I made this code in about 30 secondes, so there is no validation, but I think it does what you want
string vInput = "1500 Kb/s";
string[] tSize = new string[] { "b/s", "Kb/s", "Mb/s", "Gb/s" };
string[] tSplit = vInput.Split(new string[] {" "}, StringSplitOptions.RemoveEmptyEntries);
double vSpeed = Double.Parse(tSplit[0]) / 1024.0;
vSpeed = Math.Round(vSpeed, 2);
int i = 0;
for(i = 0; i < tSize.Length;++i)
{
if(tSplit[1].StartsWith(tSize[i]))
{
break;
}
}
string vOutput = vSpeed.ToString() + " " + tSize[i+1];

Converting String to time and back again

I am converting a fractional hour value in this format '00.000' to a hour and minute format. So that '02.500' will be '02:30' and I also have to do this in reverse.
I built a test to see how this will work,
<%
TimeSpan tspan;
TimeSpan tspan2;
string TimeString;
string TimeString2;
string Converted;
double ConvTime;
for (int HH = 0; HH < 24; HH++)
{
for (int M1 = 0; M1 < 6; M1++)
{
for (int M2 = 0; M2 < 10; M2++)
{
TimeString = HH.ToString() + ":" + M1.ToString() + M2.ToString();
// Convert Time String to Fractional Hours
tspan = TimeSpan.Parse(TimeString);
ConvTime = Convert.ToDouble(tspan.TotalHours);
Converted = String.Format("{0:00.000}", ConvTime);
// Convert Fractional Hour String to Time String
tspan2 = TimeSpan.FromHours(Convert.ToDouble(Converted));
TimeString2 = string.Format("{0:D1}:{1:D2}", tspan2.Hours, tspan2.Minutes);
Response.Write(TimeString + " = ");
Response.Write(Converted + " = ");
Response.Write(TimeString2 + "<br>");
}
}
}
%>
The problem I am having is that when I convert to a string I loose the remainder form the converstion, which throws off the value when converting back.
Here is the output for the first 10 seconds.
0:00 = 00.000 = 0:00
0:01 = 00.017 = 0:01
0:02 = 00.033 = 0:01
0:03 = 00.050 = 0:03
0:04 = 00.067 = 0:04
0:05 = 00.083 = 0:04
0:06 = 00.100 = 0:06
0:07 = 00.117 = 0:07
0:08 = 00.133 = 0:07
0:09 = 00.150 = 0:09
0:10 = 00.167 = 0:10
as you can see what I started with is not always what I end up with.
How can I correctly convert a time value string "HH:MM" to a fractional string "00.000" and back again correctly.
Thanks
EDIT:
I tried this,
TimeString = "02:02";
Converted = TimeSpan.Parse(TimeString).TotalHours.ToString("00.000");
tspan2 = TimeSpan.FromHours(Convert.ToDouble(Converted));
TimeString2 = string.Format("{0:D1}:{1:D2}", tspan2.Hours, tspan2.Minutes);
But I get Converted = "02.033" and TimeString2 = "2:01". It should equal "2:02". I'm thinking that I'm loosing the remainder when I force it to "00.000".
To convert a string to a hours, use this:
string time = "1:30";
double hours = TimeSpan.Parse(time).TotalHours; // -> 1.5
For the reverse, you can use this:
double hours = 1.5;
string time = TimeSpan.FromHours(hours).ToString(); // -> "00:01:30"
Why don't you just use DateTime.Parse?
//Parse both strings to TimeSpan
var ts = TimeSpan.FromHours(Double.Parse("02.500"));
var ts2 = TimeSpan.Parse("02:30");
//Convert a TimeSpan to string format
var firstFormat = ts.Hours.ToString.PadLeft(2, '0')
+ ":" + ts.Minutes.ToString.PadLeft(2, '0');
//Result: 02:30
var secondFormat = ts.TotalHours.ToString("00.000");
//Result: 02.500

Categories

Resources