none
Sort order of a list RRS feed

  • Question

  • I have the following code based on MasterList(Of Thing) that I know is ordered (decreasingly) by a DateTime property of Thing:

    lstTemporary = New List(Of Thing)
    lstTemporary = MasterList.FindAll(<a lambda expression>)
    Dim ThingCount as Integer = lstTemporary.Count
    aryTest = Array.CreateInstance(GetType(DateTime), ThingCount)
    For I as Integer = ThingCount -1 To 0 Step -1
          aryTest.SetValue(lstTemporary.Item(I).ADateProperty, I)
    Next

    My question: Can I depend on aryTest being sorted in increasing order?  I have run some tests and it has been but those tests were on a small subset of the data.  Is it ALWAYS sorted properly?

    By the way, I have to go through this crazy dance because the array data gets fed to a routine that I do not have control over that only takes arrays.

    Friday, August 24, 2018 1:36 AM

Answers

  • In this case, the lambda expression you use in the FindAll method can include a sort parameter.

    It can?  Not according to the documentation of FindAll.  Please show me how to do this.

    Note that I employ the looping because it reverses the sort order of the input data, MasterList.  According to the documentation ToArray cannot do that.  I have assumed that a ToArray operation followed by a sort of the resulting array would be slower than just looping once through lstTemporary.  Do you think that is a good assumption?

    Sorry, you're right that the FindAll method isn't right for sorting.  So don't use it.  Just write a LINQ expression instead.  It will be easer and will reduce the code.  For example:

    Dim tempArray = (From entry In MasterList Where entry.Name Like "New Foo%" Order By entry.DateStamp Descending).ToArray

    This gives you the array you need to work with in one shot.  The "where" clause portion of the statement would contain whatever criteria you are using in your current lambda expression and then the Order by clause would ensure the sort order in the direction you want it.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    • Marked as answer by SezMe Saturday, August 25, 2018 4:44 PM
    Saturday, August 25, 2018 3:03 PM
    Moderator

All replies

  • A list is not necessarily sorted.  You can add/remove objects from a list in a non-sorted fashion.

    A list can be sorted but the load or add must adher to the sort order but there is nothing about a list to ensure that it is sorted. 

    Bottom line = if you want the list to be sorted then you will have to sort it.


    Lloyd Sheen

    Friday, August 24, 2018 2:20 PM
  • "Can I depend on aryTest being sorted in increasing order?"

    You can if you ensure that your temporary list is in the correct order.  In this case, the lambda expression you use in the FindAll method can include a sort parameter.  That will ensure that the list is always in the desired sort order when you loop through it and set the array values.  Note that if the temporary list is sorted correct by the lambda expression then you can simply call lstTemporary.ToArray to get an equivalent array - no need to create a separate array and loop through setting values with your own code.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Friday, August 24, 2018 3:22 PM
    Moderator
  • A list is not necessarily sorted.

    I know, but please note my first sentence where I state clearly that MasterList IS sorted.

    You can add/remove objects from a list in a non-sorted fashion.

    How can removing objects from a sorted list change the sort order?  Note that this is the central question in my post, namely, can FindAll (which basically just removes items from a list) change the sort order?

    A list can be sorted but the load or add must adher[sic] to the sort order but there is nothing about a list to ensure that it is sorted.

    Note in this case, I am neither loading or adding to the list so this is not relevant to the questionPlease address the central question noted above.

    Bottom line = if you want the list to be sorted then you will have to sort it.

    Bottom line = your reply did not speak to the question.



    • Edited by SezMe Friday, August 24, 2018 5:06 PM
    Friday, August 24, 2018 4:42 PM
  • In this case, the lambda expression you use in the FindAll method can include a sort parameter.

    It can?  Not according to the documentation of FindAll.  Please show me how to do this.

    Note that I employ the looping because it reverses the sort order of the input data, MasterList.  According to the documentation ToArray cannot do that.  I have assumed that a ToArray operation followed by a sort of the resulting array would be slower than just looping once through lstTemporary.  Do you think that is a good assumption?

    Friday, August 24, 2018 5:03 PM
  •  You could also just tack on an OrderBy Method or OrderByDescending Method to the end of your FindAll method to order the results of the FindAll method by the date in ascending or descending order.


    If you say it can`t be done then i`ll try it

    Friday, August 24, 2018 7:38 PM
  • Excellent point.  Now basically the same question I asked of Reed: Which is faster, an OrderBy method then a ToArray as compared to just the one loop as in my OP.

    Certainly your suggestion is easier to read and thus maintain.

    Friday, August 24, 2018 9:04 PM
  •  Set up a small application that mimics your situation and data and use a StopWatch to track the time it takes for each method to execute on your data.  Keep in mind that most methods usually take longest to execute on the first call to it than the 2nd, 3rd, ..... times.  You may want to time how long it takes to execute the method several times (in a loop) and get an average time if you that is how you call it in your real application.

     


    If you say it can`t be done then i`ll try it

    Friday, August 24, 2018 11:51 PM
  • In this case, the lambda expression you use in the FindAll method can include a sort parameter.

    It can?  Not according to the documentation of FindAll.  Please show me how to do this.

    Note that I employ the looping because it reverses the sort order of the input data, MasterList.  According to the documentation ToArray cannot do that.  I have assumed that a ToArray operation followed by a sort of the resulting array would be slower than just looping once through lstTemporary.  Do you think that is a good assumption?

    Sorry, you're right that the FindAll method isn't right for sorting.  So don't use it.  Just write a LINQ expression instead.  It will be easer and will reduce the code.  For example:

    Dim tempArray = (From entry In MasterList Where entry.Name Like "New Foo%" Order By entry.DateStamp Descending).ToArray

    This gives you the array you need to work with in one shot.  The "where" clause portion of the statement would contain whatever criteria you are using in your current lambda expression and then the Order by clause would ensure the sort order in the direction you want it.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    • Marked as answer by SezMe Saturday, August 25, 2018 4:44 PM
    Saturday, August 25, 2018 3:03 PM
    Moderator
  •  You could also just tack on an OrderBy Method or OrderByDescending Method to the end of your FindAll method to order the results of the FindAll method by the date in ascending or descending order.


    If you say it can`t be done then i`ll try it

    I guess this is what I was thinking in my initial reply - you don't add the sorting to the first lambda, you call another method and then supply the sort lambda.

    I never use the extension methods, so its easy to forget the exact sequence of usage.  I always just write a LINQ statement which I find cleaner and more flexible.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Saturday, August 25, 2018 3:10 PM
    Moderator
  • That's a good approach.  I'll mark that as the answer but it does leave me with a question.  I saw some performance testing of LINQ versus doing the operations in code with the result that LINQ was 3-4 times slower than code.  Thus, I have avoided all LINQ.

    Do you know of performance testing of LINQ that shows differentl results?  Do you use LINQ independent of performance because it is easier and more readable?

    Saturday, August 25, 2018 4:43 PM
  • There may be some overhead in using LINQ, but I would very much like to see the kinds of comparison tests that were being performed.  LINQ is highly optimized for complex query scenarios, but obviously it has to execute its own code to do the work is provides with such simplicity.  My experience in actual usage is usability far outweighs any performance overhead that might be incurred.

    I just wrote a little test and LINQ is much faster than the loops, after its first iteration over the collection (caching occurs after the first iteration so subsequent LINQ statements are faster).

    Here's an example class to work with:

    Public Class SomeThing
        Private Shared random As New Random
        Private Shared createdCount As Integer
    
        Public Shared Function CreateRandom() As SomeThing
            Dim result As New SomeThing
            result.ID = Guid.NewGuid
            Dim count = random.Next(15, 26)
            result.Code = New String((From c In (Iterator Function()
                                                     For x = 0 To count - 1
                                                         Yield Chr(random.Next(33, 127))
                                                     Next
                                                 End Function).Invoke).ToArray)
            result.Created = Date.Parse($"{random.Next(1, 13)}/{random.Next(1, 28)}/{random.Next(2012, 2019)}")
            createdCount += 1
            result.Sequence = createdCount
            result.Value = random.Next(10, 101) * random.Next(1, 100) * 0.01
            Return result
        End Function
    
        Public Property ID As Guid
        Public Property Code As String
        Public Property Created As Date
        Public Property Sequence As Integer
        Public Property Value As Double
    End Class

    And then here's the test code:

    Private Function CompareLINQ() As Dictionary(Of String, Double)
        Dim resultTimes As New Dictionary(Of String, Double)
        Dim timer As New Stopwatch
        Dim things = GetSomeThings()
    
        'get things with value > 50
        timer.Start()
        Dim valueLinq = (From entry In things Where entry.Value > 50)
        timer.Stop()
        resultTimes("Things with Value > 50, LINQ") = timer.Elapsed.TotalMilliseconds
    
        timer.Restart()
        Dim valueLoop As New List(Of SomeThing)
        For i = 0 To things.Count - 1
            If things(i).Value > 50 Then valueLoop.Add(things(i))
        Next
        timer.Stop()
        resultTimes("Things with Value > 50, Loop") = timer.Elapsed.TotalMilliseconds
    
        'get things with code contains "E", ordered by created date
        timer.Restart()
        Dim containsOrderedLinq = (From entry In things Where entry.Code.Contains("E") Order By entry.Created)
        timer.Stop()
        resultTimes("Things Code contains 'E' Ordered by Created, LINQ") = timer.Elapsed.TotalMilliseconds
    
        timer.Restart()
        Dim containsOrderedLoop As New SortedSet(Of SomeThing)(Comparer(Of SomeThing).Create(Function(source As SomeThing, target As SomeThing)
                                                                                                 Return source.Created.CompareTo(target.Created)
                                                                                             End Function))
        For i = 0 To things.Count - 1
            If things(i).Code.Contains("E") Then containsOrderedLoop.Add(things(i))
        Next
        timer.Stop()
        resultTimes("Things Code contains 'E' Ordered by Created, Loop") = timer.Elapsed.TotalMilliseconds
    
        Return resultTimes
    End Function
    
    Private Function GetSomeThings() As IEnumerable(Of SomeThing)
        Dim result As New List(Of SomeThing)
        For i = 1 To 1000
            result.Add(SomeThing.CreateRandom)
        Next
        Return result
    End Function
    

    Executing this a couple of times (on my machine anyway) results in:

    Things with Value > 50, LINQ = 0.1618 ms
    Things with Value > 50, Loop = 0.115 ms
    Things Code contains 'E' Ordered by Created, LINQ = 0.0614 ms
    Things Code contains 'E' Ordered by Created, Loop = 0.43 ms
    Things with Value > 50, LINQ = 0.0007 ms
    Things with Value > 50, Loop = 0.0368 ms
    Things Code contains 'E' Ordered by Created, LINQ = 0.0007 ms
    Things Code contains 'E' Ordered by Created, Loop = 0.1979 ms
    Things with Value > 50, LINQ = 0.0007 ms
    Things with Value > 50, Loop = 0.0382 ms
    Things Code contains 'E' Ordered by Created, LINQ = 0.0007 ms
    Things Code contains 'E' Ordered by Created, Loop = 0.1999 ms
    Things with Value > 50, LINQ = 0.0007 ms
    Things with Value > 50, Loop = 0.0336 ms
    Things Code contains 'E' Ordered by Created, LINQ = 0.0007 ms
    Things Code contains 'E' Ordered by Created, Loop = 0.1967 ms


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Saturday, August 25, 2018 5:51 PM
    Moderator
  • Very, very interesting.  Thanks for the time and effort.  I noodled around the web for a bit but could not find the article I saw.  I'll try your code on my machine just to double check but for now those runs unmistakably point to using LINQ on a performance basis alone, not to mention the other bennies.

    Thanks again.

    Saturday, August 25, 2018 9:04 PM