This question already has answers here:
Alphanumeric sorting using LINQ
(12 answers)
Closed 6 years ago.
I have a list containing data with string and number. I have to order it in ascending and descending order.
id data
------------------
1 data1#%
2 data10
3 data
4 #$data
5 data2
I fetch the record and store it in list "List". Then I order it in ascending, but "data10" is coming in b/w "data1" and "data2". Below is my code
var o/p = List.OrderBy(x => x.data);
expected output - data1, data2 and data10
You are currently sorting it based on the string values, which will sort it by dictionary value. In a dictionary, "10" will appear between "1" and "2" because that is alphabetical order - it does not recognize that it is sorting numbers.
True alphanumeric sorting can get pretty complex, but based on your data you might be able to simplify it. Assuming your string "data1", "data2", and "data10" is a consistent pattern, you can do something like this:
var op = List.OrderBy(x => int.Parse(x.data.substring(4)));
Alternatively, if the value before the number isn't a constant length, you can use Regex to pull the number value out:
var op = List.OrderBy(x => int.Parse(Regex.Match(x.data, "\\d+").Value));
To get what you want, you need to pad the numeric portion in your order by clause, something like:
var o/p = List.OrderBy(x => PadNumbers(x.Data));
where PadNumbers could be defined as StackOverflow user Nathan has written here:
public static string PadNumbers(string input)
{
return Regex.Replace(input, "[0-9]+", match => match.Value.PadLeft(10, '0'));
}
This pads zeros for any number (or numbers) that appear in the input string so that OrderBy sees:
data0000000001
data0000000010
data0000000002
The padding only happens on the key used for comparison. The original strings (without padding) are preserved in the result.
Note : This approach assumes a maximum number of digits for numbers in the input.
Related
I am new in c#.So please help.
The first line of input has a unique integer N that indicates the number of sets of strings, each set may contain between 1 and 50 inclusive elements, and each of the strings of the set may contain between 1 and 50 inclusive characters.
How i take input from user above this condition??
example:
enter integer number:3
My dream is big
I want to go school
You are so sweet
Here's what you need to do.
Split the string into array.
Sort the array using an IComparer that compares the length of each string in the array
Join the string array into a single string
See this function below:
public static string SortStringByLength(string input, Order order) {
string[] words = input.Split(' ');
if (order == Order.ASC)
Array.Sort(words, (x, y) => x.Length.CompareTo(y.Length));
else
Array.Sort(words, (x, y) => y.Length.CompareTo(x.Length));
return string.Join(" ", words);
}
And here's a demo to see it in action.
I have an array of string and I want to get back from it a filtered array that contains only those strings that match the searched string.
string[] myValues = {"School.Report1", "School.Report2", "School.Report3", "House.Report1", "House.Report2"};
string myFilter = "School";
string[] filteredValues = myValues.Filter(myFilter); // or something similar
filteredValues must contains only: "School.Report1", "School.Report2", "School.Report3".
-- EDIT --
I prefer a non-LINQ approach if possibile. Otherwise I know that this question can be answered with the solution proposed here: filter an array in C#.
If you can't use LINQ you can still use Array.FindAll:
string[] filteredValues = Array.FindAll(myValues, s => s.Contains(myFilter));
or maybe you want to keep only all strings which first token(separated by dot) is School:
string[] filteredValues = Array.FindAll(myValues, s => s.Split('.')[0] == myFilter);
One possible answer is to make an IComparer that sorts the array by the matching value (if it contains filter return 1 else return 0) then find the first item outside the filter and make another array with the values up the one before that point.
How to sort this?
I have List of string with values like this
11-03-2013
11-03-2013 -Count=2
11-03-2013 -count=1
11-04-2013 -Count=1
11-04-2013 -Count=2
11-04-2013
Output should be, The one without the count should be on the last and the top most should be 1 followed by 1 and dates should be ordered by ascending.
11-03-2013 -Count=2
11-03-2013 -count=1
11-03-2013
11-04-2013 -Count=2
11-04-2013 -Count=1
11-04-2013
I tried this code but this is sorting this by descending
var edates= edates.OrderBy(e => e.Replace("-count=1", string.Empty).Replace("-count=2", string.Empty)).ToList();
I know that a simple class with properties can do the trick but doing that so would need to change other methods which would require a lot of work.
Regards
it is because you compare strings not dates. Create two functions: first substrings date part and parses it and second substrings the count part and returns parsed count(or 0 if length < 11) and then yourList.OrderBy(s=> f1(s)).ThenByDescending(s=> f2(s))
Here is #Guru Stron's solution in code
private static void sortList()
{
var dates = getDates();
var sorted = dates.OrderBy(f1).ThenByDescending(f2);
}
private static DateTime f1(string parse)
{
return DateTime.Parse(parse.Substring(0, 10));
}
private static int f2(string parse)
{
int sort;
if (parse.Length > 10) int.TryParse(parse.Substring(18), out sort);
else sort = 0;
return sort;
}
You first need to order by the date, then by the rest of the string, descending;
edates =
edates.OrderBy(x =>
DateTime.ParseExact(x.Substring(0, 10), "MM-dd-yyyy",
CultureInfo.InvariantCulture))
.ThenByDescending(x => x.Substring(10))
.ToList();
You ought to use a real class to simplify things and have a proper representation. That said, you can use the LINQ query syntax and take advantage of the let clause to store the result of splitting the text on a space and the equal symbol. If the split result has more than one element we can assume the count exists. Next, we order by the date (after parsing it) and then by parsing the count (descending).
Try this approach out:
string[] inputs =
{
"11-03-2013",
"11-03-2013 -Count=2",
"11-03-2013 -Count=1",
"11-04-2013 -Count=1",
"11-04-2013 -Count=2",
"11-04-2013"
};
var query = from input in inputs
let split = input.Split(' ', '=')
let count = split.Length > 1 ? int.Parse(split[2]) : 0
orderby DateTime.Parse(split[0]), count descending
select input;
foreach (var item in query)
{
Console.WriteLine(item);
}
The following should order as desired across different years and counts >= 10.
This won't be the "fastest" method (and it will most certainly not work with L2S/EF LINQ providers), but it should sort the given format correctly and fail-fast on invalid values. I would likely write the code like this (or use an IComparer with an equivalent setup) - mainly because it "reads simple" to me.
DateTime OrderableItem (string e) {
var date = e.Substring(0, 10);
return DateTime.ParseExact(date, "MM-dd-yyyy", CultureInfo.InvariantCulture)
}
int OrderableCount (string e) {
var m = Regex.Match(e, #"-count=(\d+)$", RegexOptions.IgnoreCase);
return m.Success
? int.Parse(m.Groups[1].Value)
: 0;
}
var res = seq.OrderBy(OrderableDate)
.ThenBy(OrderableCount);
I much prefer to take data, instead of excluding data.
I would like to know how to write a Linq (using lambda Expression in standard dot notation) query.
I have an array of some names, and I would like to retrevie a new array of names based on one statement. This is: Order the array of names, and return a new list from the name which starts on some specific letter (lets say letter M) on.
This is my current array:
string[] arrNames = { "Mike", "Zach", "Ella", "Allan", "Jo", "Roger", "Tito" };
I would like to return names like this: Mike, Roger, Tito, Zach - these 4;
Other 3 names (Allan, Ella and Jo are names which start with a letter that are in the alphabetica order bellow letter "M".
This is not the same as using Operator "StartsWith". This one only selects the names started on the specific letter. I would like to get all the names which are in alphabetical order from this letter and on (so names started from M to Z).
So retun list with names starts with letter "M" or above looking on the alphabetical order.
Mitja
var result = arrNames.Where(i => String.Compare("M", i) <= 0)
.OrderBy(i => i);
Looks like you need this:
arrNames.Where(n => string.Compare(n, "M") >= 0)
which returns all the names alphabetically greater (or equal) "M", in the default order ({ Mike, Zach, Roger, Tito } in your case).
If you want to sort it additionally, use
arrNames.Where(n => string.Compare(n, "M") >= 0).OrderBy(n => n)
This gives { Mike, Roger, Tito, Zach }.
arrNames.Where(s => string.Compare(s,"M",StringComparison.InvariantCultureIgnoreCase) >= 0).OrderBy(s => s);
if you want case-insensitive comparisons. Or use StringComparison.InvariantCulture for case-sensitive. It is usually a good idea to specify the culture for string comparisons (e.g. you can use the current culture, or the invariant culture).
If your whole point of sorting is just to get at the items beyond "M", then you may omit the OrderBy.
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.