I'm trying to come up with a clean way of sorting a set of strings based on a "sorting template". I apologize if my wording is confusing, but I can't think of a better way to describe it (maybe someone can come up with a better way to describe it after reading what I'm trying to do?).
Consider the following list of strings (my "sort template", each item in the list a "command"):
[FA, TY, AK, PO, PR, ZZ, QW, BC]
I'd like to use the order of the strings within that list to sort a list of those commands. For example, I'd like the following list:
[TY, PR, PR, ZZ, BC, AK]
to be sorted into the following list based on the "sorting template":
[TY, AK, PR, PR, ZZ, BC]
What would be a good way to acomplish this?
The best idea I have yet is to use an enumeration...
enum Command
{
FA,
TY,
AK,
PO,
PR,
ZZ,
QW,
BC
};
...and do an Enum.Parse() on each command in my list I want sorted, converting that list from a list of strings into a list of Commands, which would then be sorted based on the order of the enumeration.
I don't know. The enumeration seems like it would work, but is there a better way I could go about this?
Here is a very simple way to do it!
List<string> template = new List<string>{ "ZD", "AB", "GR"};
List<string> myList = new List<string>{"AB", "GR", "ZD", "AB", "AB"};
myList.Sort((a, b) => template.IndexOf(a).CompareTo(template.IndexOf(b)));
You could use a Dictionary<string, int> to store and retrieve your sorting template tokens. However, this basically does the same as your enum (only perhaps in a slightly more readable manner), because Enum.Parse here could be confusing.
var ordering = Dictionary<string, int>();
ordering.Add("FA", 0);
ordering.Add("TY", 1); // …
MyList.Sort((a, b) => ordering[a].CompareTo(ordering[b]));
This uses an appropriate overload of the List<T>.Sort method to compare two elements based on their value in the template dictionary.
You could rename your commands like
[1FA, 2TY, 3AK, 4PO, 5PR, 6ZZ, 7QW, 8BC]
and strip out the first character when you were ready to use it. I think that's called a kludge.
I can't help thinking you might get some mileage out of using a SortedList but in effect it propbably will work more or less like your enum
SortedList Commands = new SortedList();
Commands.Add(1,FA);
Commands.Add(2,TY);
//etc
Use the Command pattern ( I think it's called)
write a sort method that sorts the list, but uses an external method to do the comparison between pairs of objects... Then pass it a delegate to the comparison method... Write the comparison method to take two members of the list, and the sorting template as input parameters... In the method, return a -1, a 0 or a + 1 based on whether the first member of the pair or the second member is found first in the template list.
In your sort method use the return value from the compare method to implement the sort, whatever kind of sort you do...
Related
I have a list, each element in the list is a string that contains date and integer in specific format: yyyyMMdd_number.
List<string> listStr = new List<string> { "20170822_10", "20170821_1", "20170823_4", "20170821_10", "20170822_11", "20170822_5",
"20170822_2", "20170821_3", "20170823_6", "20170823_21", "20170823_20", "20170823_2"};
When use method listStr.Sort();
Result as below:
20170821_1
20170821_10
20170821_3
20170822_10
20170822_11
20170822_2
20170822_5
20170823_2
20170823_20
20170823_21
20170823_4
20170823_6
Expected Output:
20170821_1
20170821_3
20170821_10
20170822_2
20170822_5
20170822_10
20170822_11
20170823_2
20170823_4
20170823_6
20170823_20
20170823_21
The way: i think every string(day_number) will split with an underline, then compare and sort by number.
But please suggest me LINQ solution or better way to sort in this case.
Since the dates are in the format that can be ordered lexicographically, you could sort by the date prefix using string ordering, and resolve ties by parsing the integer:
var sorted = listStr
.OrderBy(s => s.Split('_')[0])
.ThenBy(s => int.Parse(s.Split('_')[1]));
Demo.
I imagine any numeric ordering would first require converting the value to a numeric type. So you could split on the underscore, sort by the first value, then by the second value. Something like this:
list.OrderBy(x => x.Split('_')[0]).ThenBy(x => int.Parse(x.Split('_')[1]))
You could improve this, if necessary, by creating a class which takes the string representation on its constructor and provides the numeric representations (and the original string representation) as properties. Then .Select() into a list of that class and sort. That class could internally do type checking, range checking, etc.
The answers above are much easier to follow / understand, but purely as an alternative for academic interest, you could do the following:
var sorted = listStr.OrderBy(x => Convert.ToInt32(x.Split('_')[0])*100 + Convert.ToInt32(x.Split('_')[1]));
It works on the premise that the suffix part after the underscore is going to be less than 100, and turns the two elements of the string into an integer with the relative 'magnitude' preserved, that can then be sorted.
The other two methods are much, much easier to follow, but one thing going for my alternative is that it only needs to sort once, so would be a bit faster (although I doubt it is going to matter for any real-world scenario).
The dificulty I'm facing is as follows:
I need to create a dictionary with something like 10 main definitions in it. What I actually need is to be able to recognize some X amount of strings that should represent one certain string. I want to have like 10 main strings and to be able to add different representative string to each one of them. Example: I have the strings "animal", "fruit" and "object" and I want to assing e.g. the strings "dog", "cat" and "snake" to the string "animal". The point is that everytime I face one of those strings, I'll want replace it with "animal".
I imagine this as some kind of dictionary and I've read the documentary about this class in c#, but I'm not quite sure it's the best method so that's why I'm asking you. My idea was to create a new entry each time I face one of the substrings and to set that substring (e.g. "dog") as a key with value - the main string (in this case "animal"), but I find it quite inappropriate.
Following question - could you suggest a good enough method to store the data from that "dictionary" locally/online, so that I can collect data troughout the time I'm using my code.
Thanks a lot, friendly members of this community! :D
What would be best in your case would be to inverse your logic. You should use a Dictionary with a string a key and List as value and retrieve the value using the key which is a member of your list.
var d = new Dictionary<string,List<string>>();
d.Add("Animal", new List("Dog","Cat");
d.FirstOrDefault(x => x.Value.Contains("Cat")).Key;
If I understand correctly, you can just use a dictionary:
var d = new Dictionary();
d.Add("dog", "animal");
....
d["dog"]; //this gives you animal.
You can do this with each item you want to replace, and the dictionary will give you its replacement value.
I need to sort a list of objects by one of the object's properties, but it's a string that needs to be sorted as if it's an integer. The objects are custom "Property" objects where the property name (property.Name) is a string, however 90% of the property names are actually numbers while the other 10% are names/letters (hence while the variable itself has to be a string and not an integer).
I know I can use
propertyList.OrderBy(x => x.Name)
...but that will sort it looking at it as if it's a string (i.e. 15000 is "greater" than 20).
For sorting purposes, I've already split the list into two separate lists (one that holds all the ones with property names that contain letters and another that contains the ones that can be converted to integers), but I don't know how to sort the "integer" list.
I've tried this and it doesn't work, but is there something like this I can use?
propertyList.OrderBy(x => Convert.ToInt32(x.Name))
You don't need to split the data into two lists; also note that you can perform complex operations inside lambda methods, you just need to use a different syntax:
IEnumerable<TItem> sorted = propertyList.OrderBy( x => {
Int32 asInt;
if( Int32.TryParse( x.Name, NumberStyles.Integer, CultureInfo.InvariantCulture, out asInt ) ) {
return asInt;
}
if( x.Name.Length > 0 ) return (Int32)x.Name[0];
return 0;
});
Note this code is a bit ugly and imperfect as it won't sort two textual names correctly if they start with the same character. I suggest using the more advanced overload of OrderBy instead:
class NameComparer : IComparer<String> {
public Int32 Compare(String x, String y) {
// put Name comparison logic here
}
}
IEnumerable<TItem> sorted = propertyList.OrderBy( x => x.Name, new NameComparer() );
It is not clear what you actually want here. Do you want an ordered view of the original data? Or do you actually want to modify the original data so that it's ordered?
If the former, then the OrderBy() method is what you want. It sounds like you're fine sorting the numeric names separately from the non-numeric ones, but in that case it's not clear what you mean by "it doesn't work" when you tried your second code example. That expression should work fine, and would provide an ordered view of your data. E.g.
foreach (var x in propertyList.OrderBy(x => int.Parse(x.Name))
{
// do something with the ordered elements
}
If you meant that you want to actually order the original data, then you can just use the List<T>.Sort() method:
propertyList.Sort((x, y) => int.Parse(x.Name).CompareTo(int.Parse(y.Name));
Note that in the List<T> example, the conversion from string to int is done repeatedly. This should not be a problem for relatively small collections, but if performance becomes an issue then using the LINQ OrderBy() method (which caches the keys for you) would be preferred. You can use ToList() with OrderBy() to materialize the result back to a List<T>.
Of course that involves overhead with the intermediate data structures, so there's little point in doing it that way unless you have a genuine demonstrated performance issue to address, and you have shown that that alternative fixes the issue.
I've used generic dictionaries in C# a fair bit. Things like:
var example = new Dictionary<int, string> {
{ 0, "Test0" },
{ 1, "Test1" } };
I vaguely remember being told that, before generics came along, you could use a Hashtable(). Basically the same thing, but without a specific type (so value types are going to be boxed, I think).
var example2 = new Hashtable {
{0, "Test0"},
{1, "Test1"} };
And there are questions like this one discussing why we prefer Dictionary over Hashtables (Why is Dictionary preferred over hashtable?).
But what about all the other 'dictionary' types?
SortedDictionary<K,V> - Seems to work like Dictionary but it's .Keys collection is sorted. I'm not sure why you'd care though.
OrderedDictionary is non-generic like a Hashtable, but I can't wrap my head around what's different than a Hashtable. http://msdn.microsoft.com/en-us/library/system.collections.specialized.ordereddictionary.aspx mentions that it's keys are not sorted like a SortedDictionary, so I just plain don't see why or when to use this.
ListDictionary - Smaller/Faster than Hashtable (but is it faster than a generic Dictionary?) when the number of elements is less than 10. Again, I'm at a loss for when you'd use this.
I'm also confused about SortedList<K,V>. When I hear List I don't think key/value pairs (maybe I should?). It implements IDictionary<TKey,TValue>. From this question, I can see that it differs from SortedDictionary in it's performance characteristics (What's the difference between SortedList and SortedDictionary?)
Can Someone Briefly Explain When To Use Which Dictionary Type?
For the sake of simplicity, assume I have access to .Net 4.5 or higher...so maybe there is no situation where Hashtable is useful any more?
Both Dictionary and Hashtable indicate the use of some kind of indexing of the data. Consulting the index takes some time, making it slower at small numbers of elements.
A List does not use an index, and items are typically added at the end. When inserting items, the other items "physically" move to create room for the new element, and when removing items, the other items move to close the gap.
A Dictionary typically does not preserve order, and may contain gaps in memory. When adding items, these gaps may be filled by the new item. Iterating over the Dictionary would then return the items in a different order.
Sorting is a different kind of ordering - it does not preserve the order in which items were added, but follows rules to determine the place of added items.
It's funny that ArrayList became List<T> when the genericalisation happened, and Hashtable became Dictionary<T, U> - both removing the technical aspect from the name, leaving only the name of the abstraction.
Use Dictionary<TKey,TValue>. There's no reason to use the older non-generic hash table.
Ordered Dictionary
If the insertion order of the items in the dictionary matter, then use the OrderedDictionary.
Say I have a mapping of children to their favorite ice cream.
OrderedDictioanry childToIcecream = new OrderedDictionary();
childToIcecream["Jake"] = "Vanilla";
childToIcecream["Kevin"] = "Chocolate";
childToIcecream["Megan"] = "Strawberry";
Each day one child gets an extra scoop in rotation. We could take the day number (Sunday = 0, Monday = 1..) mod it by the number of children, and pull their index from the dictionary to select whose lucky day it is. This of course only works if the dictionary maintains the order. Otherwise I would need a separate List<string> just for maintaining the order. You get key/value pairs and order in one container.
It's unfortunate there's no generic ordered dictionary, but someone posted an implementation here,
Sorted Dictionary
Same for sorted dictionary. If you had a requirement that the key/value pairs needed to be sorted this would save you time to keep it always sorted rather than have to do an expensive sort operating when you needed it to be.
SortedDictionary<char, string> letterToWord = new SortedDictionary<char, string>();
letterToWord['b'] = "bat";
letterToWord['c'] = "cat";
letterToWord['a'] = "apple";
Say you have a dictionary like the above, except the user can build the letter associations at runtime. You always want to display it in alphabetical order, so it makes sense to always keep it sorted as each new item is added.
TLDR; Always use Dictionary<TKey, TValue> unless you have a circumstance that requires it to be ordered or sorted.
I'm working on doing some custom filtering and sorting of a dataset, based on a collection of sort fields sent from the client browser, and am using Dynamic Linq to achieve (most of) the desired effect. Where I'm running into a problem is when I try to sort by a column of type String, which contains both traditional strings and numbers stored as strings. It doesn't appear that I can pass in a StringComparison enum value, or specify an IComparer parameter for the Dynamic Linq orderby function.
My sorting code looks like:
myList.AsQueryable().OrderBy("StringColWithNums ASC")
I end up with:
1
10
100
11
12
2
20
instead of:
1
2
10
11
12
20
100
Anyone have any experience doing something similar?
myList.AsQueryable().Sort((r, s) => int.Parse(r).CompareTo(int.Parse(s)));
will take some tweaking if those are objects, just use int.Parse(r.StringColWithNums), or whatever the field is.
Oops, sorry, didn't read all the OP to see it has letters too and you want the dynamic linq, editing
EDIT
I don't know that you're going to be able to do that using Dynamic linq and passing IComparer. You may be able to do it after getting the results (i.e. as I was originally writing the sort, with modifications). Comment if you want to pursue that line.
This is a fundamental problem with attempting to perform numeric comparisons within a string comparison. A couple of ways I would do this:
When loading the list, prefix numbers with an amount of zeroes that will accompany the max string size, i.e. String.Format("000000", number). This will only work if you care mostly about sorting and less about the appearance of the results - even then, you could convert "000010" back to a numeric and call the ToString() method to display the number again without the leading zeroes.
Write your own implementation (extension method) of OrderBy wherein you pass a function (or anonymous function) as a parameter to re-sort the results calling the method passed in.
You can solve this by writing a new string comparer
class AlphaNumericComparer : IComparer<string>
{
public int Compare(string x, string y)
{
// if both values are integers then do int comparision
int xValue, yValue;
if (int.TryParse(x, out xValue) && int.TryParse(y, out yValue))
return xValue.CompareTo(yValue);
return x.CompareTo(y); // else do string comparison
}
}
Then you can use the comparer in methods like OrderBy and Sort
var sorted = lst.OrderBy(s => s, new AlphaNumericComparer());
lst.Sort(new AlphaNumericComparer());
This will give you the desired result. If not then just tweak the comparer.
It seems that this is not something that can be accomplished out of the box with Dynamic Linq, at least not in .NET 2.0/3.5. I ended up modifying the Dynamic Linq source code in order to accomplish what I needed.