开发者

Change C# sorting behaviour

开发者 https://www.devze.com 2023-01-02 17:24 出处:网络
Lets say i have the strings abc_ and abc2_. Now, normally when sorting i C# abc2_ would come after abc_, le开发者_StackOverflow社区aving the result:

Lets say i have the strings abc_ and abc2_. Now, normally when sorting i C# abc2_ would come after abc_, le开发者_StackOverflow社区aving the result:

  1. abc_
  2. abc2_

I am using this to sort, if it matters:

     var element = from c in elements
     orderby c.elementName ascending
     select c;

How can i change this? I want abc_ to come last. Reversing is not an option because the list is contains more than two elements.


The simplest solution is to use the ordinal string comparer built in to the .NET Framework:

var element = from c in elements
    .OrderBy(c => c.elementName, StringComparer.Ordinal) 
    select c; 

No custom Comparer class needed!


The OrderBy method can potentially take an IComparer<T> argument. (I'm not sure if that overload can be used with query comprehension syntax, or if it's only available when using the fluent extension method syntax.)

Since it's not clear exactly what your sort algorithm should involve, I'll leave implementing the required IComparer<T> as an exercise for the reader.


If you want to underscore to play no part in the comparison, we can just make it so. So:

class CustomComparer : Comparer<string>
    {            
        public override int Compare(string a, string b)
        {   
            //Jamiec fixed the line below.             
            return a.Replace("_", "").CompareTo(b.Replace("_", ""));
        }
    }


var customComparer = new CustomComparer();                                    
var element = elements.OrderBy(c => c.elementName, customComparer);


I'm still unsure of the actual pattern we are trying to sort on but I wrote what I thought would be a solution. I saw "_" as a kind of wildcard where "abc2_" would be a subset of "abc_". But from the OPs comments "bar_" < "barxyz" breaks my understanding. Here's my code and I can modify when I gain more clarity.

static void Main(string[] args)
    {
        List<Element> Elements = new List<Element>();
        Elements.Add(new Element("abc_"));
        Elements.Add(new Element("abc2_"));
        Elements.Add(new Element("aaa"));

        var max = Elements.Max(e => e.Name.Length);
        var result = Elements.OrderBy(e => e.Name, new CustomComparer(max));

        foreach (var item in result)
            Console.WriteLine(item.Name);

        Console.Read();

    }

    class Element
    {
        public string Name { get; private set; }
        public Element(string name)
        {
            this.Name = name;
        }
    }

    class CustomComparer : Comparer<string>
    {
        private const string cWildCard = "_";
        private const char cHeavyChar = 'Z';
        public int Max { get; private set; }
        public CustomComparer(int max)
        {
            this.Max = max;
        }
        public override int Compare(string a, string b)
        {
            string comp1 = string.Empty, comp2 = string.Empty;

            int index = a.IndexOf(cWildCard);
            if (index > 0)
                comp1 = a.Substring(0, index).PadRight(this.Max, cHeavyChar);
            index = b.IndexOf(cWildCard);
            if (index > 0)
                comp2 = b.Substring(0, index).PadRight(this.Max, cHeavyChar);

            int result = comp1.CompareTo(comp2);
            return result;
        }
    }

You can see I'm just weighting a word heavier from where a "_" is found. Let me know if this is on the right track.


Using CompareOptions.Ordinal did the trick.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号