interesting question here. I'm trying to sort a Dictionary of strings by numerical value. It works in a List but not in a Dictionary, what am I doing wrong here?
var v = 0;
Dictionary<string, string> s = new Dictionary<string, string>();
s.Add("a", "a");
s.Add("100", "1");
s.Add("2", "2");
s.Add("10", "10");
List<string> g = new List<string> { "1", "10", "a", "1000" };
var problem = s.OrderBy(x => int.TryParse(x.Key,out v));
// outputs original order
var works = from c in g
orderby int.TryParse(c,out v)
select c;
//outputs sorted order a,1,10,1000
CodePudding user response:
You have two distinct, massive problems, that you need to understand.
- You are not sorting by v. You are sorting by whatever is returned by the comparer passed into the OrderBy call, which in your sample is a bool. So, you are doing this:
var g = new List<string> { "1", "10", "a", "1000" };
var works = from c in g
orderby int.TryParse(c,out v)
select c;
// It doesn't work, because int.TryParse outputs [true, true, false, true]
// which is used as a sorting key, which puts the 'a' as the first element,
// because false is sorted before true.
// try with this input:
var g = new List<string> { "10", "1", "a", "1000" };
// and you'll see that the output is ["a", "10", "1", "1000"]
So, your sorting function should be int.TryParse(c,out v) ? v : -1
.
The -1
could be something different if you need a sorting also on the invalid keys, but that can be addressed by a ThenBy
after the OrderBy
.
BUT, you still have a major bug:
- You are always updating the single variable
v
. If the value is calculated and stored in a shared variable it could easily lead to problems with late evaluation, so you should removevar v
from the external scope and use
// the addition of 'var' in the out declaration enables the usage of a separate, new v for each item in the source,
// thus removing the possibility of collisions.
s.OrderBy(x => int.TryParse(x.Key, out var v) ? v : -1);
CodePudding user response:
You can try something like this:
var problem = s
.OrderBy(pair => int.TryParse(pair.Key, out var parsed)
? parsed // Take Key value as it is
: long.MinValue); // Treat pair as the topmost
Here we try to parse Key
from the key-value pair
and in case we fail, we put the pair on the top (I've used long.MinValue
in order to mix with possible int.MinValue
key). You may want to add .ThenBy
:
var problem = s
.OrderBy(pair => int.TryParse(pair.Key, out var parsed)
? parsed // Take Key value as it is
: long.MinValue) // Treat pair as the topmost
.ThenBy(pair => pair.Key);
if you want to sort non-number keys as well as numbers, e.g.
Dictionary<string, string> demo = new() {
{ "a", "a" },
{ "100", "1" },
{ "2", "2" },
{ "b", "a" },
{ "10", "10" },
{ "c", "a" },
};
var result = demo
.OrderBy(pair => int.TryParse(pair.Key, out var parsed)
? parsed
: long.MinValue)
.ThenBy(pair => pair.Key);
Console.Write($"{string.Join(Environment.NewLine, result)}");
Output:
[a, a]
[b, a]
[c, a]
[2, 2]
[10, 10]
[100, 1]