1

I have found this documentation (Enumerable.OrderBy(Of TSource, TKey) Method (IEnumerable(Of TSource), Func(Of TSource, TKey))), where it says:

This method performs a stable sort; that is, if the keys of two elements are equal, the order of the elements is preserved. In contrast, an unstable sort does not preserve the order of elements that have the same key.

However, I do not have any idea about the difference between stable and unstable sort (?).

Let's say that I have a public property, an array, called fields (as clsField()) where a parser has placed all the objects...

Public Class clsField
    Public idx As String
    Public name As String
    Public order As Long
End Class

Public Class Container
    Public fields As clsField()

    Public Function getFields() As Dictionary(Of String, clsField)
        If (fields Is Nothing) OrElse (fields.Count < 1) Then Return New Dictionary(Of String, clsField)
        Return fields.OrderBy(Function(fld) fld.order).ToDictionary(Of String, clsField)(Function(fld) fld.idx, Function(fld) fld)
    End Function

    Public Function getFields(blnAlternative As Boolean) As Dictionary(Of String, clsField)
        If (fields Is Nothing) OrElse (fields.Count < 1) Then Return New Dictionary(Of String, clsField)
        Return fields.ToDictionary(Of String, clsField)(Function(fld) fld.idx, Function(fld) fld).OrderBy(Function(pair) pair.Value.order)
    End Function
End Class

Public Class Consumer
    Public cont As Container
    Public parse As Parser

    Public Sub New(strFormat)
        cont = New Container
        cont.fields = parse.deserializeOject(Of clsField())(strFormat)
    End Sub

    Public Sub printFields_Sort_Dic()
        For Each fld As clsField In cont.getFields().Values
            Console.WriteLine(fld.ToString)
        Next
    End Sub

    Public Sub printFields_Dic_Sort()
        For Each fld As clsField In cont.getFields(True).Values
            Console.WriteLine(fld.ToString)
        Next
    End Sub

    Public Sub Main()
        printFields_Dic_Sort()
        printFields_Sort_Dic()
    End Sub
End Class

Two doubts related to the above code:

  1. Will the order in which the Parser has inserted the elements into the array fields be preserved for elements with same order?
  2. Could printFields_Dic_Sort() have different order than printFields_Sort_Dic()? (the first converts and then sorts; second sorts and then converts into Dictionary)

Could not join definitive information on this... Thanks

EDITED

Added types for returned Dictionary

Ok, this is the problem:

  1. If I want to have KeyValuePair (idx, obj) cannot use an array (wrong: it is possible; the challenge is how to convert between arrays)
  2. I cannot store the sorted array in a Dictionary because there is not guarantee that iterating through it will give ordered values (right: no guarantee with generic Dictionary, see below) .
  3. There is the option to use SortedDictionary. However, there is no direct conversion from array to SortedDictionary (unless you do the iteration)... (**right*?*: no trivial conversion between array/list and SortedDictionary)
  4. Then, for an easy and quick solution it only remains the use of list. However, there there are problems as well, because it should be a list of KeyValuePair that I cannot get directly from the array by the use of toList (wrong: it is possible to get the List (Of KeyValuePair (Of ...)), however, it requires a previous conversion).

So no elegant (short and simple) solution at all... (right: no comments)

Any help will be highly appreciated (still working with it) - Solved below...

EDIT II

Ok, this what I finally have done: solve the problem at point 4 by using array.ConvertAll (msdn) and defining my own Converter function... to convert the array and then, get a List, by ToList, from that intermediate array that already has the type converted to (Of KeyValuePair (Of String, clsFld)). Hope it helps someone else:

'Imports System.Linq.Enumerable
'Imports System.Runtime.CompilerServices ' for extensions

Public Class clsField
    Public idx As String
    Public name As String
    Public weight As Long

    Public Sub New(Optional i As String = vbNullString, Optional n As String = vbNullString, Optional w As Long = vbNullString)
        idx = i : name = n : weight = w
    End Sub
End Class

Public Class Container
    Public fields As clsField()

    ' returns a list sorted by clsField.weight preserving order for elements with same 'weight' value
    Public Function getFields() As List(Of KeyValuePair(Of String, clsField))
        Dim auxList As List(Of KeyValuePair(Of String, clsField))

        If (fields Is Nothing) OrElse (fields.Count < 1) Then Return New List(Of KeyValuePair(Of String, clsField))
        ' .ToList to transform IEnumerable to the return type
        auxList = Array.ConvertAll(fields, New Converter(Of clsField, KeyValuePair(Of String, clsField))(AddressOf FieldToPair)).ToList
        Return auxList.OrderBy(Function(x) x.Value.weight).ToList()
    End Function

    Public Shared Function FieldToPair(fld As clsField) As KeyValuePair(Of String, clsField)
        Return New KeyValuePair(Of String, clsField)(fld.idx, fld)
    End Function
End Class

Public Class Consumer
    Public cont As Container

    Public Sub New()
        cont = New Container
        cont.fields.Add(New clsField("ffq", "foo30004", 33))
        cont.fields.Add(New clsField("ffc", "foo9997", 55))
        cont.fields.Add(New clsField("ffp", "foo9908", 55))
        cont.fields.Add(New clsField("ffo", "foo100001", 22))
        cont.fields.Add(New clsField("ffx", "foo8885", 33))
        cont.fields.Add(New clsField("ffz", "foo70002", 22))
        cont.fields.Add(New clsField("ffy", "foo8806", 33))
        cont.fields.Add(New clsField("ffa", "foo9009", 55))
        cont.fields.Add(New clsField("ffb", "foo8000", 55))
        cont.fields.Add(New clsField("ffn", "foo7003", 22))
    End Sub

    Public Sub printSortedFields()
        Dim aux As List(Of KeyValuePair(Of String, clsField))

        aux = cont.getFields()

        For Each pair As KeyValuePair(Of String, clsField) In aux
            Console.WriteLine(pair.Value.name)
            With pair.Value
                Debug.Print("name: " & .name & " || idx: " & .idx & " || weight: " & .weight)
            End With
        Next
    End Sub

    Public Sub Main()
        printSortedFields()
    End Sub
End Class

Module ArrayExtension ' custom method for array
    <Extension()>
    Public Sub Add(Of T)(ByRef arr As T(), item As T)
        If arr IsNot Nothing Then
            Array.Resize(arr, arr.Length + 1)
            arr(arr.Length - 1) = item
        Else
            ReDim arr(0)
            arr(0) = item
        End If

    End Sub
End Module

Result for the test above:

name: foo100001 || idx: ffo || weight: 22
name: foo70002 || idx: ffz || weight: 22
name: foo7003 || idx: ffn || weight: 22
name: foo30004 || idx: ffq || weight: 33
name: foo8885 || idx: ffx || weight: 33
name: foo8806 || idx: ffy || weight: 33
name: foo9997 || idx: ffc || weight: 55
name: foo9908 || idx: ffp || weight: 55
name: foo9009 || idx: ffa || weight: 55
name: foo8000 || idx: ffb || weight: 55
rellampec
  • 698
  • 6
  • 22
  • Dictionaries have no real ordering (except perhaps when iterating the KeyValuePairs), so order by isn't really doing anything. Also sideline that you don't use your `blnAlternative` parameter. Also see..NET naming conventions – pinkfloydx33 Feb 12 '17 at 23:53
  • Where it says: _ In contrast, an unstable sort does not preserve the order of elements that have the same key._ -> Is this just a "clarifying" explanation about what `stable sort` means? Or, does it rather mean that `OrderBy` could do an `unstable sort`? – rellampec Feb 12 '17 at 23:54
  • Also your `getFields(boolean)` method is technically wrong. You return either a `Dictionary` or an `IOrderedEnumerable` but the method signature is `Dictionary`. VB is apparently forgiving in this regard but I'd be wary about the actual behavior. My guess is that you'd get an `InvalidCastException` depending on how it gets used – pinkfloydx33 Feb 12 '17 at 23:59
  • @pinkfloydx33 Thanks... (sideline that this is just an example with doubts **only** `related to the question`)... So, do you mean that the only way to deal with it is always iterating the KeyValuePairs?... but that even though `OrderBy` will not preserve the insertion order... And finally, if "dictionaries have no real ordering", why msdn documentation says "if the keys of two elements are equal, the order of the elements is preserved"? Thank you – rellampec Feb 13 '17 at 00:01
  • 1
    See this answer which links to the msdn doc for Dictionary. The ordering of items in a dictionary is indeterminate when iterating (ie you could get a different order on subsequent runs. In practice you probably won't, but there is no guarantee) http://stackoverflow.com/a/4007787/491907. So point being that it doesn't matter that you called order by since you convert it to a dictionary afterwards then all ordering is lost. The other function that orders it afterwards isn't a dictionary anymore and cannot be used/accessed by one. It's an enumerable representing an iteration of the contents – pinkfloydx33 Feb 13 '17 at 00:03

1 Answers1

1
  1. Stable sort preserves the order of elements of they have the same valu, so yes, they will be returned in the same order part added them. But that doesn't really matter because you're pushing them to a dictionary later. See below.

  2. Dictionary<TKey, TValue> does not define ordering and may not preserve the order of elements l. Because of that printFields_Sort_Dic() might return them in different order.

MarcinJuraszek
  • 124,003
  • 15
  • 196
  • 263
  • Ok, Thank you for your answer, MarcinJuraszek .. I will continue the thread with @pinkfloydx33 here... so from that [link](http://stackoverflow.com/questions/4007782/the-order-of-elements-in-dictionary/4007787#4007787) (non-deterministic order of values in a Dictionary), it says that `SortedDictionary` suits more to this purpose... However, I do not see how I can solve the second point, as `ToDictionary(Of String, clsField)` does **not convert** to `SortedDictionary(Of String, clsField)` – rellampec Feb 13 '17 at 00:19
  • Regarding your second point, might `printFields_Dic_Sort()` return them in different order too? (or only `printFields_Sort_Dic()`?) – rellampec Feb 13 '17 at 00:32