How to find the postFix of a string - c#

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

Related

Loop through list and compare previous line to current line. I have a list of account numbers mostly duplicates

My result two is a list of account numbers and dates that I have added from another list by using LINQ to sort. The list of result two contains many duplicate account numbers but I would like to filter the list to remove duplicates but not just selecting the first instance.
I am looking to select the account number that has the oldest date. I'm getting some output from the stream reader but its not behaving as expected. I am still getting every line and no removals.
I'm trying to compare if the account number matches the account number from the previous line and the current date is older to remove the instance of the account number with the newer date.
Any ideas?
foreach (var valueTwo in resultTwo)
{
var valueString = valueTwo.Name.ToString();
var currentAccount = valueString.Substring(0, valueString.IndexOf('\t'));
var currentDateTwo = (valueString.Substring((valueString.IndexOf('\t') + 1), 10).Replace("/", ""));
int currentDate = Convert.ToInt32(currentDateTwo);
var prevAccount = "";
int prevDate = 0;
if (currentAccount != prevAccount)
{
forecastTestList.Add(currentAccount + '\t' + currentDate);
}
if (currentAccount == prevAccount)
{
if (currentDate > prevDate)
{
forecastTestList.Remove(prevAccount + '\t' + prevDate);
forecastTestList.Add(currentAccount + '\t' + currentDate);
}
}
prevAccount = currentAccount;
prevDate = currentDate;
//srcheckForecast.WriteLine(valueTwo.Name);
}
foreach (var valueThree in forecastTestList)
{
srcheckForecast.WriteLine(valueThree);
}
}
An alternative approach would be to use Dictionary data structure to store the maximum day for each account:
var dictionary = new Dictionary<string, int>();
foreach (var valueTwo in resultTwo)
{
var valueString = valueTwo.Name.ToString();
var currentAccount = valueString.Substring(0, valueString.IndexOf('\t'));
var currentDateTwo = (valueString.Substring((valueString.IndexOf('\t') + 1), 10).Replace("/", ""));
int currentDate = Convert.ToInt32(currentDateTwo);
if (!dictionary.TryGetValue(currentAccount, out int value) || currentDate > value)
{
dictionary[currentAccount] = currentDate;
}
}
// transorm dictionary to List<string> of your form
var forecastTestList = dictionary.Select(x => $"{x.Key + '\t' + x.Value}").ToList();

Using DateTime and split in Array

I'm trying to to take part of a string and set it as a date time property for a class. The date part of string is part of a textfile and formatted as dd/mm/yyyy.
My code works fine for other properties, string int etc but I'm experiencing an error when trying to use datetime. I cant pinpoint where the problem is.
An unhandled exception of type 'System.IndexOutOfRangeException'
occurred in Hospital.exe
Additional information: Index was outside the bounds of the array.''
//read files from file
string[] linesx = File.ReadAllLines("patients.txt");//creates array of each line in text file
string[] patientInfo = new string[4];
string[] dateInfo = new string[3];
string patientLong, name;
Patient newPatient;
int age,blood,x;
DateTime date;
int year, month, day;
//sort through text and get data.
for (int i = 0; i < linesx.Length; i++)
{
patientLong = linesx[i];//gets each lines details eg.ward,56
patientInfo = patientLong.Split(',');//separates info eg,ward ,56
name = patientInfo[0];//gets name from index 0
age = Convert.ToInt32(patientInfo[1]); //gets age from index1
blood = Convert.ToInt32(patientInfo[2]);
x = Convert.ToInt32(patientInfo[3]);
dateInfo = patientInfo[2].Split('/');
day = Convert.ToInt32(dateInfo[0]);
month = Convert.ToInt32(dateInfo[1]);
year = Convert.ToInt32(dateInfo[2]);
date = new DateTime(year, month, day);
newPatient = new Patient(name, age,blood,x,date);
patients.Add(newPatient);
}
I can see that
dateInfo = patientInfo[2].Split('/');
should be
dateInfo = patientInfo[4].Split('/');
You've messed int and DateTime formats:
Either patientInfo[2] is integer, e.g. 42, so you can put
blood = Convert.ToInt32(patientInfo[2]);
but can't
dateInfo = patientInfo[2].Split('/');
month = Convert.ToInt32(dateInfo[1]);
Or patientInfo[2] is DateTime, e.g. 10/11/1976, so you can't put
blood = Convert.ToInt32(patientInfo[2]);
Since you have 'System.IndexOutOfRangeExceptionexception,patientInfo[2]is of typeintand the most probable cause of the misbehaviour is a *typo*: it'spatientInfo[4]that storesdate, notpatientInfo[2]`.
As for implementation, it's one of the problem Linq has been designed for:
using System.Linq;
....
List<Patient> patients = File
.ReadLines("patients.txt")
.Select(line => line.Split(','))
.Select(items => new Patient(
items[0],
int.Parse(items[1]),
int.Parse(items[2]),
int.Parse(items[3]),
DateTime.ParseExact(items[4], "d/M/yyyy", CultureInfo.InvariantCulture)
))
.ToList();

Parallel.For with date time

OK this code is a bit meta but it roughly explains how i have it now and what i want to achieve.
specialObject{
DateTime date;
int number;
}
var startDate = Lowest date in the list;
var endDate = Hightest date int the list;
List<SpecialObject> objs = (list from database where date > startDate and date < endDate)
//A list with alot of dates and numbers, most of the dates are the same. List contains roughly 1000 items, but can be more and less.
for(var date = startDate; date < endDate; date = date.AddDay(1){
listItem = objs.Where(x => x.Day = date).Sum(x => x.alotOfNUmbers);
}
Now since i don't care what day i calculate first, i thought i could do this.
Parallel.For(startDate, endDate, day => {
listItem = objs.Where(x => x.Day = date).Sum(x => x.alotOfNUmbers);
}
But how do i make it step dates ?
You can make a Range and iterate over it with Parallel.ForEach :
// not tested
var days = Enumerable
.Range(0, (endDate-startDate).Days) // check the rounding
.Select(i => startDate.AddDays(i));
Parallel.ForEach(days, day => ....)
Alternatively, you could use PLinq over the original source, probably faster. Roughly:
// not tested
var sums = objs.AsParallel().GroupBy(x => x.date).Select(g => g.Sum(i => i.number));
All the overloads of Parallel.For take two integer variables for start and end. I also don't see any version which would support something like a step so you can't just use the tick count of a DateTime as the loop variable.
But it should be easy to use a Parallel.ForEach instead, when you create an IEnumerable<DateTime> as the source sequence.
var source = Enumerable.Range(0, (endDate - startDate).Days)
.Select(t => startDate.AddDays(t));
Add +1 to the count parameter if the endDate is inclusive.
Ok after a few days search i figured if i placed all days in an array and "whiled" through it. It gives a pretty good result. With code easy to read
var start = new DateTime(2014, 09, 09);
var end = new DateTime(2014, 10, 01);
var listOfDays = new List<DateTime>();
int i = 0;
for (var day = start; day < end; day = day.AddDays(1))
{
listOfDays.Add(day);
}
Parallel.ForEach(listOfDays.ToArray(), currentDay =>
{
for (var d = new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 0, 0, 0); d < new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, 23, 59, 59); d = d.AddSeconds(5))
{
var str = "Loop: " + i + ", Date: " + d.ToString();
Console.WriteLine(str);
}
i++;
});
Console.Read();

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

Find date ranges from a collection of dates C#

Simple question. I have an ordered collection of dates. They are UK dates btw
01/01/10
01/02/10
01/03/10
01/04/10
02/04/10
03/04/10
04/04/10
And I want to convert this into a collection of date ranges
01/01/10 -> 01/01/10
01/02/10 -> 01/02/10
01/03/10 -> 01/03/10
01/04/10 -> 04/04/10
Just to clarify, I'm trying to convert any consecutive dates into a range. so the first 3 dates are stand alone and the last 4 get converted into a range 1st of April to 4th of April.
Now I can do this using loops but it's not very elegant. Does any one have any solutions out there that are?
Thanks
Given that you want to determine ranges of consecutive date ranges, I think your only option is, as you say a loop. You can do it in a single pass though, and put it in an extension method so it'll operate on any IList<DateTime>, for example:
// purely an example, chances are this will have actual, y'know logic in live
public class DateRange
{
private List<DateTime> dates = new List<DateTime>();
public void Add(DateTime date)
{
this.dates.Add(date);
}
public IEnumerable<DateTime> Dates
{
get { return this.dates; }
}
}
public static IEnumerable<DateRange> GetRanges(this IList<DateTime> dates)
{
List<DateRange> ranges = new List<DateRange>();
DateRange currentRange = null;
// this presumes a list of dates ordered by day, if not then the list will need sorting first
for( int i = 0; i < dates.Count; ++i )
{
var currentDate = dates[i];
if( i == 0 || dates[i - 1] != currentDate.AddDays(-1))
{
// it's either the first date or the current date isn't consecutive to the previous so a new range is needed
currentRange = new DateRange();
ranges.Add(currentRange);
}
currentRange.Add(currentDate);
}
return ranges;
}
You could also make it even more generic by passing in an IEnumerable<DateTime>:
public static IEnumerable<DateRange> GetRanges(this IEnumerable<DateTime> dates)
{
List<DateRange> ranges = new List<DateRange>();
DateRange currentRange = null;
DateTime? previousDate = null;
// this presumes a list of dates ordered by day, if not then the list will need sorting first
foreach( var currentDate in dates )
{
if( previousDate == null || previousDate.Value != currentDate.AddDays(-1) )
{
// it's either the first date or the current date isn't consecutive to the previous so a new range is needed
currentRange = new DateRange();
ranges.Add(currentRange);
}
currentRange.Add(currentDate);
previousDate = currentDate;
}
return ranges;
}
dates.Aggregate(new List<DateRange>(), (acc, dt) =>
{
if (acc.Count > 0 && acc.Last().d2 == dt.AddDays(-1))
acc[acc.Count - 1].d2 = dt;
else
acc.Add(new DateRange(dt, dt));
return acc;
}
);
where DateRange is a class like this:
class DateRange
{
public DateTime d1, d2;
public DateRange(DateTime d1, DateTime d2)
{
this.d1 = d1;
this.d2 = d2;
}
}
var stringDates = new List<string> {"01/09/10", "31/08/10", "01/01/10"};
var dates = stringDates.ConvertAll(DateTime.Parse);
dates.Sort();
var lastDateInSequence = new DateTime();
var firstDateInSequence = new DateTime();
foreach (var range in dates.GroupBy(
d => { if ((d - lastDateInSequence).TotalDays != 1)
firstDateInSequence = d;
lastDateInSequence = d;
return firstDateInSequence;
}))
{
var sb = new StringBuilder();
sb.Append(range.First().ToShortDateString());
sb.Append(" => ");
sb.Append(range.Last().ToShortDateString());
Console.WriteLine(sb.ToString());
}

Categories

Resources