Convert string to MM/yyyy in c# to sort - c#

I have seen previous questions which are related my query but couldn't figure out on how to resolve my issue.
I have a list "Sites" with one of the items as "Year". It is defined as string and is in the format "MM/yyyy". When I try to sort the list based on the year, I'm facing a small problem.
Data for "Year" is
01/2012
04/2012
01/2013
06/2012
When I sort the list by using orderby, the output I'm getting is
01/2012
01/2013
04/2012
06/2012
which is incorrect.
Cannot convert the string using Convert.ToDateTime as the string format doesn't contain day value. How should I go forward with this? How to implement DateTime.TryParseExact without changing the format of the string?
Note : The format should be the same and the list should be sorted.

you could try something like this without having to change the input this will give you the order that you like also look at the OrderByDescending property if you need it in a different sort order
var dateList = new List<string> { "01/2012", "04/2012", "01/2013", "06/2012" };
var orderedList = dateList.OrderBy(x => DateTime.Parse(x)).ToList();

You can still convert the string to a date within a LINQ statement, and the items will stay as strings.
var strings = new[]
{
"01/2012",
"04/2012",
"01/2013",
"06/2012"
};
var ordered = strings.OrderBy(s =>
{
var split = s.Split('/');
return new DateTime(int.Parse(split[1]), int.Parse(split[0]), 1);
});
Your last item will then be "01/2013".
As MethodMan showed in his answer, DateTime.Parse() will be able to parse a MM/yyyy formatted dated. However, if you need to perform anything that takes more than one line, this would be how you can do that. NB: This will not work in any query against a DbContext!

Implement System.IComparable interface:
public int CompareTo(object obj)
{
// Check null
if (obj == null)
return 1;
// Check types
if (this.GetType() != obj.GetType())
throw new ArgumentException("Cannot compare to different type.", "obj");
// Extract year and month
var year = int.Parse(this.Year.SubString(3, 4));
var month = int.Parse(this.Year.SubString(0, 2));
// Extract year and month to compare
var site = (Sites)obj;
var objyear = int.Parse(site.Year.SubString(3, 4));
var objmonth = int.Parse(site.Year.SubString(0, 2));
// Compare years first
if (year != objyear)
return year - objyear;
// Same year
// Compare months
return month - objmonth;
}

you also can create a new list with Dates converted to DateTime format and sort it after. It's a lot of lines but good for learning.
class Sites
{
public string Year { get; set; }
}
class MainClass
{
static void Main()
{
List<Sites> ListOfSites = new List<Sites>();
ListOfSites.Add(new Sites { Year = "01/2012" });
ListOfSites.Add(new Sites { Year = "04/2012" });
ListOfSites.Add(new Sites { Year = "01/2013" });
ListOfSites.Add(new Sites { Year = "06/2012" });
DateTime SiteYear;
List<DateTime> listWithDates = new List<DateTime>();
foreach (var item in ListOfSites)
{
if(DateTime.TryParse(item.Year, out SiteYear))
{
listWithDates.Add(SiteYear);
}
}
Display(SortAscending(listWithDates), "Sort Ascending");
}
static List<DateTime> SortAscending(List<DateTime> list)
{
list.Sort((a, b) => a.CompareTo(b));
return list;
}
static void Display(List<DateTime> list, string message)
{
Console.WriteLine(message);
foreach (var datetime in list)
{
Console.WriteLine(datetime);
}
Console.WriteLine();
}
}

Related

MongoDB .NET Driver - Convert string to DateTime and for Filter Builder

var builder = Builders<ModelClass>.Filter;
var filter = builder.Where(x => x.Active);
if (fromDate.HasValue)
{
var date = fromDate.Value;
var subfilter = builder.Where(x => DateTime.Parse(x.EnrollmentDate) >= date);
filter &= subfilter;
}
Enrollment Date is saved as a string:
public string EnrollmentDate { get; set; }
, I need to filter docs within a set of date range, but how do I compare this? I need to filter like this.
I get
System.InvalidOperationException: Parse({document}{EnrollmentDate}) is not supported.
Error in subfilter line.
I think you need to achieve with MongoDB query as below:
{
"$expr": {
"$gte": [
{ "$toDate": "$EnrollmentDate" },
date
]
}
}
While I think it is not achievable with MongoDB .Net Driver LINQ syntax, you convert the query as BsonDocument:
var subfilter = new BsonDocument("$expr",
new BsonDocument("$gte",
new BsonArray {
new BsonDocument("$toDate", "$EnrollmentDate"),
date
}
)
);
filter &= subfilter;
You have problem here when you want to do DateTime.Parse()
Can you post format of your string EnrollmentDate? And your variable date , is it only Date or DateTime?
This one maybe can help you here
Also, try to use
var subfilter = builder.Gte(x=>x.Y, Z)

Is there a way to create this report by Looping over the list of dates?

This is for a .net core 3.1 application, using Blazor for front end.
I have a list of dates that I need to take, and then build an "analysis" report using those dates. The analysis basically needs to look at the dates and tell me how many dates fall on each day of the week, and also how many dates are in each month of the year. This fis for an Human Resources application where they are tracking employee absences.
I have a List that I've built, and I'm passing it into a method that then performs an analysis.
public class DateHourResponseModel
{
public DateTime Date { get; set; }
public int Hours { get; set; }
}
I figured I could then take that list, and create a list of "Responses", each for a different day of the week, and month of the year. I figured for "Type" I could just use the DateTime.DayOfWeek, DateTime.Month and get an into (0-6 for day of week, 1-12 for month) which is why I have the "IsMonth" bool on there.
public class AbsenceAnalysisResponse
{
public int Type { get; set; }
public bool IsMonth { get; set; }
public int Count { get; set; }
public int Hours { get; set; }
}
And This class, which is just a list of the above:
public class AbsenceAnalysis
{
public List<AbsenceAnalysisResponse> Responses { get; set; }
}
My question is: Is there a way to build this analysis report by doing a foreach on the list of dates that I start with? I haven't figured out a way to create this list without doing something like this:
var analysisResponses = new List<AbsenceAnalysisResponse>
{
new AbsenceAnalysisResponse
{
Type = 0,
IsMonth = false,
Count = dateHourModel.Count(x => (int)x.Date.DayOfWeek == 0),
Hours = dateHourModel.Where(x => (int)x.Date.DayOfWeek == 0).Sum(x => x.Hours)
},
I feel like an idiot because I know there has got to be a more elegant way of doing this, and maybe the problem is how I'm approaching it. I have the analysis working and displaying on the front end but I absolutely hate how I'm creating the list of "responses". I'm still pretty green behind the ears with this, and I don't know if I just haven't been searching the right questions online or what, but I haven't found anything where someone is doing something similar. Thanks for any help, and please let me know if there is any information I need to provide.
Something like this should work:
var models = new List<DateHourResponseModel>(); //Should be a filled list, not empty like this
var modelMap = new Dictionary<(int, bool), AbsenceAnalysisResponse>();
foreach (DateHourResponseModel dateHour in models)
{
Add(type: (int)dateHour.Date.DayOfWeek, isMonth: false);
Add(type: dateHour.Date.Month, isMonth: true);
void Add(int type, bool isMonth)
{
(int, bool) key = (type, isMonth);
//Try to get existing responses
if(!modelMap.TryGetValue(key, out AbsenceAnalysisResponse response))
{
//Create if first time adding to it
modelMap[key] = response = new AbsenceAnalysisResponse
{
Type = type,
IsMonth = isMonth,
Count = 0,
Hours = 0
};
}
response.Count += 1;
response.Hours += dateHour.Hours;
}
}
//convert to list, can order if needed with .OrderBy()
List<AbsenceAnalysisResponse> analysisResponses = modelMap.Values.ToList();
The idea would be to loop through each model and add/modify to the list. Instead of searching through the list, a dictionary can be used with a unique key. In this case type + isMonth, in tuple form, but it could also be a string "{type}-{isMonth}" or something. If the key is not found then it will be created, otherwise just modified. At the end, turn the Dictionary into a list of its values. There are other ways of doing this, but this should be a good approach
Just wanted to post another solution I found while tinkering with this. I really liked Gekctek's answer, and used that initially. I think this one might be slightly more readable? Happy to receive any feedback.
private AbsenceAnalysis GetAbsenceAnalysisFromData(List<DateHourResponseModel> dateHourModel)
{
var analysis = new List<AbsenceAnalysisResponse>();
foreach (var data in dateHourModel)
{
var week = analysis.FirstOrDefault(x =>
x.Type == (int)data.Date.DayOfWeek && x.IsMonth == false);
var month = analysis.FirstOrDefault(x =>
x.Type == (int)data.Date.Month && x.IsMonth == true);
if (week == null)
{
week = new AbsenceAnalysisResponse
{
Count = 0,
Hours = 0,
IsMonth = false,
Type = (int)data.Date.DayOfWeek
};
analysis.Add(week);
}
if (month == null)
{
month = new AbsenceAnalysisResponse
{
Count = 0,
Hours = 0,
IsMonth = true,
Type = data.Date.Month
};
analysis.Add(month);
}
week.Count += 1;
week.Hours += data.Hours;
month.Count += 1;
month.Hours += data.Hours;
}

Sorting a class string field property with numerical value [duplicate]

This question already has answers here:
Sorting of list contained strings having alphabetic/numeric
(2 answers)
Closed 8 years ago.
I have a class with one property "Name" containing names like "1_[AnnualRevenue]","2_[ResellerType]","3_xxx"....
my class is like
class xxx
{
private string fileName;
public string FileName
{
get { return fileName; }
set { fileName = value; }
}
}
And I am assigning the values to the object of the class. like xxx.FileName="1_[AnnualRevenue]";
Now I have a list class. And now sort the list according to this class property.
Now I want to sort the field according to the numeric order, I mean 1 first 2 second and so on.
And then write it to filestream.
Could any body help me with this.
Thanks in advance.
Since the property is a String but you want to sort it numerically, probably the best way would be to implement IComparable on your class and then put your custom sort code in the CompareTo method. Then you don't have to write a more complex Lambda statement each time you want to Sort a list, you can just call the Sort() method on the list.
You can also handle cases where the FileName property does not contain an underscore or is null, rather than getting exceptions in your OrderBy code (which is what would happen with most of the other answers).
I made a couple of other changes also - override the ToString method so you can easily display the value to the console window, and used Automatic property syntax for the FileName property so we can remove the backing field:
class xxx : IComparable<xxx>
{
public string FileName { get; set; }
public int CompareTo(xxx other)
{
// Short circuit if any object is null, if the
// Filenames equal each other, or they're empty
if (other == null) return 1;
if (FileName == null) return (other.FileName == null) ? 0 : -1;
if (other.FileName == null) return 1;
if (FileName.Equals(other.FileName)) return 0;
if (string.IsNullOrWhiteSpace(FileName))
return (string.IsNullOrWhiteSpace(other.FileName)) ? 0 : -1;
if (string.IsNullOrWhiteSpace(other.FileName)) return 1;
// Next, try to get the numeric portion of the string to compare
int thisIndex;
int otherIndex;
var thisSuccess = int.TryParse(FileName.Split('_')[0], out thisIndex);
var otherSuccess = int.TryParse(other.FileName.Split('_')[0], out otherIndex);
// If we couldn't get the numeric portion of the string, use int.MaxValue
if (!thisSuccess)
{
// If neither has a numeric portion, just use default string comparison
if (!otherSuccess) return FileName.CompareTo(other.FileName);
thisIndex = int.MaxValue;
}
if (!otherSuccess) otherIndex = int.MaxValue;
// Return the comparison of the numeric portion of the two filenames
return thisIndex.CompareTo(otherIndex);
}
public override string ToString()
{
return FileName;
}
}
Now, you can just call Sort on your list:
List<xxx> list = new List<xxx>
{
new xxx {FileName = "13_a"},
new xxx {FileName = "8_a"},
new xxx {FileName = null},
new xxx {FileName = "1_a"},
new xxx {FileName = "zinvalid"},
new xxx {FileName = "2_a"},
new xxx {FileName = ""},
new xxx {FileName = "invalid"}
};
list.Sort();
Console.WriteLine(string.Join("\n", list));
// Output (note the first two are the empty string and the null value):
//
//
// 1_a
// 2_a
// 8_a
// 13_a
// invalid
// zinvalid
You can use LINQ to do that for you
List<xxx> orderedList = unOrderedList.OrderBy(o => Convert.ToInt32(o.FileName.Split('_').First())).ToList();
Editted the answer on behalf of the comments - pointing out that indeed we need to convert to integers to order correctly.
You can do like following to sort the list:
List<xxx> list = new List<xxx>
{
new xxx { FileName = "3_a" },
new xxx { FileName = "1_a" },
new xxx { FileName = "2_a" },
new xxx { FileName = "8_a" }
};
var sorted = list.OrderBy(it => Convert.ToInt32(it.FileName.Split('_')[0]));//using System.Linq;
And you can write the list to disk file as below:
using (TextWriter tw = new StreamWriter("C:\\FileNames.txt"))
{
foreach (var item in sorted)
{
tw.WriteLine(item.FileName.ToString());
}
}

Checking foreach loop list results C#

I have this bit of code in a class:
public class TicketSummary
{
//get all the development tickets
public List<IncidentSummary> AllDevelopmentTickets { get; set; }
public List<string> TicketNames()
{
List<string> v = new List<string>();
foreach (var developmentTicket in AllDevelopmentTickets)
{
var ticketIds = developmentTicket.id.ToString(CultureInfo.InvariantCulture);
v.Add(ticketIds);
}
return v;
}
}
}
And I am trying to see if my API connection (plus all the code) did it's job and pulls back the tickets and their info, more specifically the ids.
In my main program I have no clue how to check if it did the job. I tried something but it isn't quite right and doesn't return anything ( I know I need a Console.WriteLine)
static void Main(string[] args)
{
Console.ReadLine();
var tickets = new TicketSummary();
tickets.TicketNames();
while ( tickets != null )
{
Console.WriteLine(tickets);
}
}
Any suggestions, please?
Thank you!
You've dropped the returned result: tickets.TicketNames(); returns List<String> that you have to assign and then itterate:
var tickets = new TicketSummary();
var names = tickets.TicketNames(); // <- names, List<String> according to the code
// printing out all the names
foreach(var name in names)
Console.WriteLine(name);
Do you mean you just want to print all the tickets out?
foreach (var ticket in tickets.TicketNames())
{
Console.WriteLine(ticket);
}
You have several problems in your code, that should keep it from even compiling, but aside from that, It seem's what you're really after is rather transforming the data in AllDevelopmentTickets, rather than moving it somewhere. So you could probably do it with a Select call (from LINQ). So, in your main method:
var tickets = new TicketSummary();
// add some tickets to tickets.AllDevelopmentTickets here...
var ticketNames = tickets.AllDevelopmentTickets.Select(ticket => ticket.id.ToString();
// Yes, you should probably use an UI culture in the ToString call.
// I'm just trying to limit my line width =)
Now, ticketNames should be an IEnumerable<string> holding all the ticket ids. To, for example, print them out, you can iterate over them and write to console output:
foreach (var name in ticketNames) {
Console.WriteLine(name);
}
You need to assign / use the return value of the TicketNames() method. This seems a lot of work just to return the string version of the TicketId. This can be reduced
public List<string> TicketNames()
{
return AllDevelopmentTickets
.Select(t => t.id.ToString(CultureInfo.InvariantCulture))
.ToList();
}
var ticketSummary = new TicketSummary();
var ticketNames = ticketSummary.TicketNames();
foreach(var ticketName in ticketNames)
{
Console.WriteLine(ticketName);
}
or Even just:
foreach(var ticketName in AllDevelopmentTickets
.Select(t => t.id.ToString(CultureInfo.InvariantCulture)))
{
Console.WriteLine(ticketName);
}
You're ignoring the returned value.
static void Main(string[] args)
{
Console.ReadLine();
var tickets = new TicketSummary();
var res = tickets.TicketNames();
while ( for r in res )
{
Console.WriteLine(r);
}
}

Enumerated int List<GradeRange>

I really have no clue about enumerated list, but after some research I found that this list may help solve my problem. So I have a string in my settings called strGrades, and it is a range of strings that I manually update. The range is 0155-0160, 0271-0388, 0455-0503, 0588-687. What I basically want to do is find the values that are not in this grade list (for example 0161,0389, 0504-0587...)
So I came up with a function that will allow me to get each match in the grade range:
public static List<GradeRange> GetValidGrades()
{
MatchCollection matches= Regex.Matches(Settings.Default.productRange,
Settings.Default.srGradeRange);
List<GradeRange> ranges= new List<GradeRange();
if(matches.Count >0)
{
foreach (Match match in matches)
{
ranges.Add(new GradeRange() 23 {
Start= int.Parse(match.Groups["Start"].Value),
Stop= int.Parse(match.Groups["Stop"].Value)
});
}
}
return ranges;
}
here is the grade range class
public class GrandRange
{
public int Start{get; set;)
public int Stop {get; set; )
}
So the function above caputures my Start and End values, can anyone please help me get this into a list where I can find the values that fall outside of the range values, I just need a starting point. Thanks so much!
You could use a custom extension method that creates .Between along with a Where
var myFilteredList = list.Where(x=>!myValue.Between(x.Start, x.Stop, true));
This isnt the most performant answer, but if you need a list of all the numbers that are not between certain ranges, then you could do something like this:
var missingNumbers = new List<int>();
var minStop = list.OrderBy(x=>x.Stop).Min().Stop;
var maxStart = list.OrderBy(x=>x.Start).Max().Start;
Enumerable.Range(minStop, maxStart).ToList()
.ForEach(x=>
{
if(!x.Between(x.Start, x.Stop, true))
missingNumbers.Add(x);
}
);
Here this should get you started
var strings = "0155-0160, 0271-0388, 0455-0503, 0588-687";
var splitStrings = strings.Split(char.Parse(","));
var grads = new List<GrandRange>();
foreach (var item in splitStrings) {
var splitAgain = item.Split(char.Parse("-"));
var grand = new GrandRange
{
Start = int.Parse(splitAgain[0]),
Stop = int.Parse(splitAgain[1])
};
grads.Add(grand);
}
}

Categories