3

I have an Observable Collection of about 5,000 objects returning from a SQL query to my WCF service. I have been asked to sort the collection by 3 fields in the object. For brevity, lets say this is my c# model:

public class TestObject{
     public string type1{get; set;}
     public string type2{get; set;}
     public string type3(get; set;}
}

And here is some test data that I have to try to sort:

   public ObservableCollection<TestObject> TestObjects = new ObservableCollection<TestObject>();
   TestObjects.Add(new TestObject('708','4','(A)'));
   TestObjects.Add(new TestObject('7','41B','1(A)'));
   TestObjects.Add(new TestObject('69','2','45'));
   TestObjects.Add(new TestObject('708','4','(B)'));
   TestObjects.Add(new TestObject('69','2','5'));
   TestObjects.Add(new TestObject('7','41','1(B)'));
   TestObjects.Add(new TestObject('7','41',''));

Is there a way to dynamically sort these so the Observable collection comes back sorted like this?

  {(7,41,''),(7,41,1(B)),(7,41B,1(A)),(69,2,5),(69,2,45),(708,4,(A)),(708,4,(B))}

So far I have been able to sort them by the type1, but then trying to sort by the type2 screws the order up from the type1 sort. Here is the method I used to try to sort, followed by how I call it:

   protected virtual ObservableCollection<TestObject> SortTestObjects(ObservableCollection<TestObjects> unsortedObjects, string level){
        var SortedObjects = new ObservableCollection<TestObject>();
        Comparison<TestObject> numericComp;
        Comparison<TestObject> comparison;
        Comparison<TestObject> AlphaNumericComp;
        bool sortNumeric = false;
        switch (level)
        {
            case "type1":
                numericComp = (a, b) =>
                {
                    var aKey = Convert.ToDouble(a.type1);
                    var bKey = Convert.ToDouble(b.type1);
                    return aKey.CompareTo(bKey);
                };
                AlphaNumericComp = (a, b) =>
                {
                    return string.CompareOrdinal(a.type1, b.type1);
                };
                sortNumeric = unsortedObjects.ToList().TrueForAll(i => i.type1.IsNumeric());

                break;
            case "type2":
                 numericComp = (a, b) =>
                {
                    var aKey = Convert.ToDouble(a.type2);
                    var bKey = Convert.ToDouble(b.type2);
                    return aKey.CompareTo(bKey);
                };
                 AlphaNumericComp = (a, b) =>
                {
                    return string.CompareOrdinal(a.type2, b.type2);
                };
                sortNumeric = unsortedObjects.ToList().TrueForAll(i => i.type2.IsNumeric());

                break;
            case "type3":
                 numericComp = (a, b) =>
                {
                    var aKey = Convert.ToDouble(a.type3);
                    var bKey = Convert.ToDouble(b.type3);
                    return aKey.CompareTo(bKey);
                };
                 AlphaNumericComp = (a, b) =>
                {
                    return string.CompareOrdinal(a.type3, b.type3);
                };
                sortNumeric = unsortedObjects.ToList().TrueForAll(i => i.type3.IsNumeric());

                break;
            default:
                numericComp = (a, b) =>
                {
                    var aKey = Convert.ToDouble(a.type1);
                    var bKey = Convert.ToDouble(b.type1);
                    return aKey.CompareTo(bKey);
                };
                AlphaNumericComp = (a, b) =>
                {
                    return string.CompareOrdinal(a.type1, b.type1);
                };
                sortNumeric = unsortedObjects.ToList().TrueForAll(i => i.type1.IsNumeric());

                break;
        }
        comparison = sortNumeric ? numericComp : AlphaNumericComp;
        unsortedObjects.ToList().Sort(comparison);
        foreach(var obj in unsortedOjects){
          SortedObjects.Add(obj)
         }
        return SortedObjects;
     }

    Public ObservableCollection<TestObject> SortAllTestObjects(){
         var sort1 = SortTestObjects(TestObjects, "type1");
         var sort2 = SortTestObjects(sort1, "type2");
         var sort3 = SortTestObjects(sort2, "type3");
         return sort3;
    }

**Edit: Here is a SQL query that I would be using in this example. I am totally good with changing the query as long as I get the order correct in the client **

Select type1, type2, type3 from dbo.testObject tO where tO.del_date is null

Please let me know if you need any more info. Any help or constructive feedback would be awesome!

tCoe
  • 401
  • 1
  • 5
  • 24
  • Could you define the sorting rules? – Nazar Merza Nov 26 '19 at 15:20
  • Sorting numerically then alphabetically, by type1, then type2, then type3, as shown above where i give an example of how i would want 'TestObjects' returned. – tCoe Nov 26 '19 at 15:48
  • It is still not fully clear what your rules are. Also, is it a programming problem OR it is that your rules are logically inconsistent. Because if you sort alphabetically and then numerically, it will change the first sort, and it is not something that you can by programming. – Nazar Merza Nov 26 '19 at 20:26
  • this is why I mentioned that it is sorted numerically first, then alphabetically. if you look below, at the accepted answer, I have solved my problem – tCoe Nov 26 '19 at 21:53

2 Answers2

1

So here is what I ended up doing to solve the problem. In the object, I concatenated the types

public class TestObject{
 public string type1{get; set;}
 public string type2{get; set;}
 public string type3{get; set;}
 public string types{get;set;}
}

and in the method where the TestObjects are being populated, I added this in:

public ObservableCollection<TestObject> TestObjects = new ObservableCollection<TestObject>();
 TestObjects.Add(new TestObject('708','4','(A)','708.4.(A)'));
 TestObjects.Add(new TestObject('7','41B','1(A)','7.41B.1(A)'));
 TestObjects.Add(new TestObject('69','2','45', '69.2.45'));
 TestObjects.Add(new TestObject('708','4','(B)', '708.4.(B)'));
 TestObjects.Add(new TestObject('69','2','5', '69.2.5'));
 TestObjects.Add(new TestObject('7','41','1(B)', '7.41.1(B)'));
 TestObjects.Add(new TestObject('7','41','', '7.41.'));
  TestObjects= SortObjects(TestObjects);

Next, I created a custom comparison method in the ViewModel, using the "Shlwapi.dll':

   [DllImport("Shlwapi.dll", Charset = Charset.Unicode)]
   private static extern int StrCmpLogicalW(string x, string y); 

   public ObservableCollection<TestObject> SortObjects(ObservableCollection<TestObject> unsortedObjects){
   var sortedCollection = new ObservableCollection<TestObject>();
   var objectList = new List<TestObject>();
   foreach(var obj in unsortedObjects){
      objectList.Add(obj);
     }
   Comparison<TestObject> typesComp = (a,b) =>{
      var aKey = a.types;
      var bKey = b.types;
      return StrCmpLogicalW(aKey,bKey);
   }
   objectList.Sort(typesComp);
   foreach(var obj in objectList){
     sortedCollection.Add(obj);
   }
   return sortedCollection;
   }

This method allows me to return the TestObjects collection in ascending order. Still working on descending order switch, and will update this when I get it.

UPDATE: Here is the SortObjects method with the reversal ability

   public ObservableCollection<TestObject> SortObjects(ObservableCollection<TestObject> unsortedObjects, bool IsAscend){
      var sortedCollection = new ObservableCollection<TestObject>();
      var objectList = new List<TestObject>();
      foreach(var obj in unsortedObjects){
         objectList.Add(obj);
      }
      Comparison<TestObject> typesComp = (a,b) =>{
         var aKey = a.types;
         var bKey = b.types;
         return StrCmpLogicalW(aKey,bKey);
      }
      objectList.Sort(typesComp);
      if(!isAscend){
         objectsList.Reverse();
      }
      foreach(var obj in objectList){
        sortedCollection.Add(obj);
      }
      return sortedCollection;
    } 

And then it is called passing in a true / false parameter like this for ascending:

 TestObjects = SortObjects(TestObjects, true);

and like this for descending order:

 TestObjects = SortObjects(TestObjects, false);
tCoe
  • 401
  • 1
  • 5
  • 24
-1

You can use OrderBy() and ThenBy().

Example

public class SomeClass
{
    public string PropA { get; }
    public string PropB { get; }
}

protected ObservableCollection<SomeClass> SomeFunc(ObservableCollection<SomeClass> collection)
{

    var sorted=collection.OrderBy(s => s.PropA).ThenBy(s => s.PropB);
    ObservableCollection<SomeClass> sortedObservable=new ObservableCollection<SomeClass>();
    foreach(var item in sorted)
    {
        sortedObservable.Add(item);
    }
    return sortedObservable;
}

To account for numeric values in your string you can use a custom comparer, The answer to this is a good example

Link to microsoft notes

  • Thank you for the answer, but this doesn't order the type1 or type2 properly. It looks to be only treating the values as a string, and returning the following from the dataset in my example: {(69,2,45),(69,2,5),(7,41,''),(7,41,1(B)),(7,41B,1(A)),(708,4,(A)),(708,4,(B))} – tCoe Nov 20 '19 at 20:13
  • 1
    My bad, you need a custom comparer, answer is updated with an example of this. – Andrew Murphy Nov 20 '19 at 21:58
  • it looks like the 'StrCmpLogicalComparer' that is mentioned could work if I can get it to accept a named field from an object instead of a List – tCoe Nov 22 '19 at 20:25