Ordering substrings according to date in C# - c#

I have a large string containing multiple ID's in my reservation program for overview purpose.
string example = "ID01-20/05-Table1\n ID02-04/06-Table2\n ID03-21/05-Table1\n"
This is just an example, but the string could grow or shrink as reservations get deleted or added.
Currently, the overview is sorted based on ID's, but is it also possible using date or tables?
So it views as:
string example = "ID01-20/05-Table1\n ID03-21/05-Table1\n ID02-04/06-Table2\n"
Would be best if possible using Console.Display(); but I also wouldn't mind using something to generate a temporary string/list/array/whatever and displaying it that way.
I've seen it on static strings, but I'm not too sure how it would work as ID's get added and deleted.

You can split the initial string into chunks
.Split('\n')
the match the date part in each chunk with a help of regular expression:
// one or two digits followed by slash and then one or two digits
Regex.Match(item, "[0-9]{1,2}/[0-9]{1,2}").Value
parse it into date
DateTime.TryParseExact(
...
"d/M", // day / Month format
null,
DateTimeStyles.AssumeLocal,
out var date)
and then order by for it.
Code:
using System.Linq;
using System.Text.RegularExpressions;
...
string example = "ID01-20/05-Table1\n ID02-04/06-Table2\n ID03-21/05-Table1\n";
string result = string.Join("\n", example
.Split('\n')
.OrderBy(item => DateTime.TryParseExact(
Regex.Match(item, "[0-9]{1,2}/[0-9]{1,2}").Value,
"d/M",
null,
DateTimeStyles.AssumeLocal,
out var date)
? date
: DateTime.MaxValue));

Related

Sorting one array from another's one date

I have Visual Studio 2019. The project is a .Net Windows Form on C# on .Net Framework 4.8.
I have got two arrays.
The first one contains a random number of string values (since they come on a string[]), for example:
AB5XHBC1
NMAK72B8
WB5XHBC1
KCZUH528
NZ9YHF3D
PFKR6WNA
The second one will always have the same length of the first one, but their values are date-strings, and it could be some string.empty or repeated values in some cases:
06/10/2020
08/05/2018
01/12/2020
01/01/2009
01/12/2020
What I need to do is sort the first array based on the second one dates. I tried the Array.Sort method but it does not give me the expected result. I'm not really sure if I have used it wrong.
The "null" values must be the firsts, then the newer date (on the top) to the older date (on the bottom), so the resulting array of this example would have to be sort like this (from 0 to 5 position):
WB5XHBC1
KCZUH528
PFKR6WNA
NMAK72B8
AB5XHBC1
NZ9YHF3D
Thanks in advance.
You could implement a comparer for your dates (which should implement IComparer<string>) and then use Array.Sort(keys, values, comparer) like so:
using System;
using System.Collections.Generic;
namespace Demo
{
static class Program
{
public static void Main()
{
string[] data =
{
"AB5XHBC1",
"NMAK72B8",
"WB5XHBC1",
"KCZUH528",
"NZ9YHF3D",
"PFKR6WNA"
};
string[] dates =
{
"06/10/2020",
"08/05/2018",
"",
"01/12/2020",
"01/01/2009",
"01/12/2020"
};
Array.Sort(dates, data, new CompareDates());
Console.WriteLine(string.Join("\n", data));
}
}
public sealed class CompareDates: IComparer<string>
{
public int Compare(string x, string y)
{
var xd = DateTime.TryParse(x, out DateTime d1) ? d1 : DateTime.MaxValue;
var yd = DateTime.TryParse(y, out DateTime d2) ? d2 : DateTime.MaxValue;
return yd.CompareTo(xd);
}
}
}
However, this gives the output as:
WB5XHBC1
KCZUH528
PFKR6WNA
AB5XHBC1
NMAK72B8
NZ9YHF3D
I believe this is because your expected results have an error. The second-oldest date is "08/05/2018", which corresponds to the string "NMAK72B8". Therefore, according to your specification, "NMAK72B8" should be second from the end - but in your expected results, "AB5XHBC1" is second from the end.
Try it online (.Net Fiddle).
One thing to watch out for is the date parsing. The code above parses the dates in the current locale, which is assumed to be UK (for the dd/mm/yyyy date format). If you want to parse other date formats, you'll have to change the code accordingly.
You can store these in a List of Tuple<string, DateTime>. I've hardcoded the values in the example below - as other commenters have said, you need to properly parse the date strings into proper DateTime objects, etc. I leave that exercise to you.
var list = new List<(string RandomString, DateTime Date)>
{
("AB5XHBC1", DateTime.Parse("06/10/2020")),
("NMAK72B8", DateTime.Parse("08/05/2018")),
("WB5XHBC1", DateTime.MaxValue), // simulating null, see below
("KCZUH528", DateTime.Parse("01/12/2020")),
("NZ9YHF3D", DateTime.Parse("01/01/2009")),
("PFKR6WNA", DateTime.Parse("01/12/2020")),
};
var sorted = list.OrderByDescending(x => x.Date);
For the case of the null dates appearing first, you can check if the particular date string is null. If it is, assign DateTime.MaxValue as a value, otherwise parse it and use the actual value. The logic of this check could resemble the one below:
string.IsNullOrEmpty(whateverStringDate) ? DateTime.MaxValue : DateTime.Parse(whateverStringDate);
This should point you in the right direction, #Matthew Watson's answer would be a much more elegant and complete solution.

Get DateTime from FileName

I have a file named test-2000_01_02-10_12_14.xml.
How do I only get the date from the file?
I was able to get the date if the file has this name: 2000_01_02-10_12_14
with this (b is a StorageFile):
DateTime dateVal;
bool parsed = DateTime.TryParseExact(b.DisplayName,
"yyyy_MM_dd-H_mm_ss",
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.None,
out dateVal);
I then tried to change yyyy_MM_dd-H_mm_ss to something like this *-yyyy_MM-dd-H-mm_ss but it does not seem to be the solution
There are a boatload of ways to do this, it really rather depends on how regular the naming of your files is - is there always some junk text followed by a hyped, then the year?
Post up another 10 different examples if you want more tailored advice. Here's a way for the one you've posted:
DateTime.TryParseExact(
Path.GetFileNameWithoutExtension(b.DisplayName.Substring(b.DisplayName.IndexOf('-')+1)),
"yyyy_MM_dd-H_mm_ss",
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.None,
out dateVal
);
This uses Substring with only one argument (no length) to remove everything after the first hyphen up to the end of the string, and GetFileNameWithoutExtension to remove the .xml - this effectively turns anythinghere-2000_01_01-00_00_00.xml into 2000_01_01-00_00_00 ready for parsing
I could also have gone for a .Remove("last index of period") type thing but it does get a bit messy because you have to subtract the start Index of the hyphen etc
MJWill's comment about splitting on hyphen is also a good one - you could split then take the [1] and [2] indexes and join then back together for parsing..
Lastly don't forget that the file itself might have a created date that is already a good candidate for the date of creation rather than the filename (which might be mangled by humans) so long as it hasn't been transmitted somewhere and re-saved. Take a look at the FileInfo.CreationTime property for that - https://learn.microsoft.com/en-us/dotnet/api/system.io.fileinfo?view=netframework-4.8
First, we have to extract (match) the datetime part from a file name:
using System.Text.RegularExpressions;
...
// Aggravated task: dots and minuses within file's name
string source = #"bla-bla-bla-test.me-2000_01_02-10_12_14.xml";
string datetime = Regex.Match(
Path.GetFileNameWithoutExtension(source),
"[0-9]{4}_[0-9]{2}_[0-9]{2}-[0-9]{2}_[0-9]{2}_[0-9]{2}$").Value;
Then we can parse it
if (DateTime.TryParseExact(
datetime,
"yyyy_MM_dd-H_m_s",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeLocal,
out DateTime result) {
// result is the parsed date
}
else {
// File doesn't contain valid date and time
}
I would suggest you to use regular expression assuming that your file name will be always following the same format you can do something like this:
var pattern = #"\d{4}_\d{2}_\d{2}-\d{2}_\d{2}_\d{2}";
var fileName = "test-2000_01_02-10_12_14.xml";
var match = new Regex(pattern);
var result = match.Match(fileName);
if (result.Success)
{
DateTime.TryParseExact(result.Value,
"yyyy_MM_dd-HH_mm_ss",
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.None,
out DateTime dateVal);
}

How can I parse out a date from string using a regular expression?

I am trying to parse out a DateTime from a string using a regular expression. My issue is that I have a list of strings which need to be sorted by their date in descending order. I want to find a regex that will get the date from the strings. They all look similar but I am not sure how to do it. All help is much appreciated.
This is what the string looks like:
sdfsad[10/16/2014 at 9:52 AM by AJOHNSON]sdfsadf
And I have a list that all have the same type of format.
This is the regex I have so far:
[0-9].[/][0-9].[0-9].*[at]
If all of your lines take this form:
sdfsad[10/16/2014 at 9:52 AM by AJOHNSON]sdfsadf
Then I would suggest using a regular expression to grab everything from the [ to the space before by, and then pass that string to DateTime.ParseExact or DateTime.TryParseExact.
The regex to extract that bit of text should be straightforward, and using the DateTime parser to get the date and time will be easier than trying to do it with a regular expression.
Use a regular expression to extract the components that interest you (date and time), create a datetime from these components and sort each line depending on the date time. you can use a temporary data class in order to help you create the structure you want
public class DataWithTimestamp
{
public string line {get;set;}
public DateTime stamp {get;set;}
public DataWithTimestamp(string data)
{
stamp = regex.extract(pattern); // not the correct syntax, set it here
line = data;
}
}
and in your code change your lines to the class, sort on the
var allLines = File.ReadAllLines("file.txt");
var sortedLines = allLines
.Select(s => new DataWithTimestamp(s))
.OrderBy(data => data.stamp)
.Select(data => data.line);
EDIT: this regex should work:
\[(?<date>[\d\/]+) at (?<time>[\d:]+).*\]
find the catpured groups date and time then ParseExact them into the correct DateTime

C# - How to format datetime to remove trailing zeros

I'm setting up an orchestration class that handles multiple actions as one big transaction. For each of these transactions I give them the same time-stamp instantiated at the beginning of the orchestration.
I user the following line:
var transactionTimestamp = DateTime.UtcNow.ToString("o");
I have a constraint in the system that dictates that the time stamp cannot have any trailing zeros.
For example:
2013-06-26T19:51:38.0083980Z //bad
2013-06-26T19:51:38.008398Z //good
2013-06-26T19:51:38.0083988Z //good
The built-in DateTime format "o" is comparable to the custom format of: "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK". If you just use that format, but replace the lower-case f's with upper case ones, there will be no trailing zeros.
ie
DateTime.UtcNow.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFFK");
Custom date and time format strings
You can achieve this fairly easily using Regex. Here's one way.
string result = Regex.Replace("2013-06-26T19:51:38.0083980Z", "0+Z$", "Z");
// result == "2013-06-26T19:51:38.008398Z"
string result2 = Regex.Replace("2013-06-26T19:51:38.0083988Z", "0+Z$", "Z")
// result2 == "2013-06-26T19:51:38.0083988Z"
I would write my own help method like;
public string GetDtString(DateTime dt)
{
RegEx rgx = new RegEx("[1-9]0+Z\b");
return rgx.Replace(dt.ToString("o"), System.String.Empty);
}
It basically just returns the dt string with all 0's which occur after a digit 1-9 and before Z\b (Z followed by a word boundary) with an empty string.

C# 4.0 - 3 value pairs (possibly more). Sort based on values

This is a tough one to phrase as a search query and I'm having no luck. And the more I think about it, it is more a logic question than a syntax one. However I am a newby to C# (8 years php) and I am currently building my third windows forms app so there may be a method built to do just what I want.
What I am doing is reading a date format given by the user as a single string and breaking it into parts to be assigned to an Array, or from what I have seen in my searches for Associative Arrays, maybe a SortedList or Dictionary.
e.g.
SortedList<string, int> resultArray = new SortedList<string, int>();
string dateFormat = "yyyyMMdd" // Just and example
int yearPos = dateFormat.IndexOf("yyyy");
int monthPos = dateFormat.IndexOf("MM");
int dayPos = dateFormat.IndexOf("dd");
resultArray.Add("yearPos", yearPos);
resultArray.Add("monthPos", monthPos);
resultArray.Add("dayPos", dayPos);
// So, resultArray expressed as an array looks like:
// resultArray["yearPos"] = 0
// resultArray["monthPos"] = 4
// resultArray["dayPos"] = 6
// Sort List and reassign keys (or values) based on their position value (which is unique)
// ???????
return resultArray;
Ideally, the finished result that I am after for this collection/array is to have the members ranked by the value of their position in the string. Like this:
// resultArray["yearPos"] = 1
// resultArray["monthPos"] = 2
// resultArray["dayPos"] = 3
The reason I am trying to do this, is because the same date format is used to pull out a real date from a file using Regex.Match. And I want to use these new values to know which group element of the match to use for each portion of the date.
Any help getting my head around this would be greatly appreciated.
I tried this and it works:
DateTime dt;
if (DateTime.TryParseExact("20110223", "yyyyMMdd", null, 0, out dt))
Console.WriteLine(dt);
Just use DateTime.TryParse. You can pass it a formatting string and it will do all the work for you.

Categories