tags:

views:

159

answers:

5

Lets say i have the strings abc_ and abc2_. Now, normally when sorting i C# abc2_ would come after abc_, leaving 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.

+3  A: 

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.

LukeH
Better to try and use the built-in StringComparer class, see my answer.
luksan
+1  A: 

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);
Khnle
I think that answer is pretty close. If it's not, the OP might just need to write the proper unit tests and modify the implementation as needed.
Greg
This wont work. a.Replace(..) doesnt change a, it returns a new string. So it would need to be a.Replace(...).CompareTo(b.Replace(...))
Jamiec
@Jamiec - I agree
Khnle
A: 

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.

Sorax
A: 

Using CompareOptions.Ordinal did the trick.

Erik
A: 

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!

luksan