【问题标题】:Does Enumerable.OrderBy Method preserve insertion order for items with same sorting - including conversion type?Enumerable.OrderBy 方法是否保留具有相同排序的项目的插入顺序 - 包括转换类型?
【发布时间】:2017-02-12 23:32:04
【问题描述】:

我找到了this documentation (Enumerable.OrderBy(Of TSource, TKey) Method (IEnumerable(Of TSource), Func(Of TSource, TKey))),上面写着:

此方法执行稳定排序;也就是说,如果两个元素的键相等,则元素的顺序被保留。相反,不稳定的排序不会保留具有相同键的元素的顺序。

但是,我对 stableunstable sort (?) 之间的区别一无所知。

假设我有一个公共属性,一个数组,称为 fields (as clsField()),其中 parser 放置了所有对象...

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

与上述代码相关的两个疑惑:

  1. 对于具有相同order 的元素,是否会保留Parser 将元素插入数组fields 的顺序?
  2. printFields_Dic_Sort() 的顺序是否与printFields_Sort_Dic() 不同? (第一个转换然后排序;第二个排序然后转换为Dictionary

无法加入这方面的权威信息... 谢谢

已编辑

为返回的Dictionary添加了类型

好的,这就是问题所在:

  1. 如果我想要KeyValuePair (idx, obj) 不能使用array错误:有可能;挑战在于如何在数组之间转换)
  2. 我无法将排序后的array 存储在Dictionary 中,因为不能保证遍历它会得到有序的values正确:不保证通用字典,见下文) .
  3. 可以选择使用SortedDictionary。但是,没有从arraySortedDictionary 的直接转换(除非您进行迭代)...(**对*?*:array/listSortedDictionary 之间没有简单的转换)
  4. 然后,对于一个简单快速的解决方案,它只保留使用list。但是,也有问题,因为它应该是 KeyValuePair 的列表,我不能通过使用 toList 直接从 array 获得(错误:有可能获取List (Of KeyValuePair (Of ...)),但是,它需要之前的转换)。

所以根本没有优雅(简短而简单)的解决方案...(正确:没有 cmets)

任何帮助将不胜感激(仍在使用它) - 已解决...

编辑二

好的,这就是我最终所做的:通过使用 array.ConvertAll (msdn) 并定义我自己的 Converter 函数来解决第 4 点的问题...转换 array 然后,得到一个ListToList,来自已将类型转换为 (Of KeyValuePair (Of String, clsFld)) 的中间 array。希望它可以帮助别人:

'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
上述测试的

结果

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

【问题讨论】:

  • 字典没有真正的排序(除了可能在迭代 KeyValuePairs 时),所以 order by 并没有真正做任何事情。另外,您不使用 blnAlternative 参数。另请参阅..NET 命名约定
  • 它在哪里说:_ 相反,不稳定的排序不会保留具有相同键的元素的顺序。_ -> 这只是对stable sort 含义的“澄清”解释吗?或者,这是否意味着OrderBy 可以做unstable sort
  • 您的getFields(boolean) 方法在技术上也是错误的。您返回DictionaryIOrderedEnumerable,但方法签名是Dictionary。 VB 在这方面显然是宽容的,但我会对实际行为保持警惕。我的猜测是你会得到一个InvalidCastException,这取决于它的使用方式
  • @pinkfloydx33 谢谢...(顺便说一句,这只是一个有疑问的例子 related to the question)...所以,你的意思是唯一的处理方式它总是在迭代 KeyValuePairs?...但是即使OrderBy 不会保留插入顺序...最后,如果“字典没有真正的排序”,为什么 msdn 文档会说“如果两个元素的键相等,元素的顺序被保留”?谢谢
  • 查看链接到字典的 msdn 文档的这个答案。迭代时字典中项目的顺序是不确定的(即您可以在后续运行中获得不同的顺序。实际上您可能不会,但不能保证)stackoverflow.com/a/4007787/491907。因此,重要的是,您调用 order by 并不重要,因为您之后将其转换为字典,然后所有排序都将丢失。之后对其进行排序的另一个功能不再是字典,并且不能被使用/访问。它是一个表示内容迭代的可枚举

标签: vb.net sorting type-conversion preserve


【解决方案1】:
  1. 稳定排序保留它们具有相同值的元素的顺序,所以是的,它们将按添加它们的相同顺序部分返回。但这并不重要,因为您稍后会将它们推送到字典中。见下文。

  2. Dictionary&lt;TKey, TValue&gt; 没有定义排序并且可能不会保留元素 l 的顺序。因此printFields_Sort_Dic() 可能会以不同的顺序返回它们。

【讨论】:

  • 好的,谢谢你的回答,MarcinJuraszek .. 我将在这里继续使用@pinkfloydx33 的线程...所以从link(字典中值的非确定性顺序)开始,它说SortedDictionary 更适合这个目的......但是,我不知道如何解决第二点,因为ToDictionary(Of String, clsField) 确实 not convertSortedDictionary(Of String, clsField)
  • 关于您的第二点,printFields_Dic_Sort() 是否也可以按不同的顺序返回它们? (或者只有printFields_Sort_Dic()?)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-14
  • 1970-01-01
  • 2011-09-20
  • 1970-01-01
  • 1970-01-01
  • 2014-12-17
  • 1970-01-01
相关资源
最近更新 更多