【发布时间】:2017-02-12 23:32:04
【问题描述】:
我找到了this documentation (Enumerable.OrderBy(Of TSource, TKey) Method (IEnumerable(Of TSource), Func(Of TSource, TKey))),上面写着:
此方法执行稳定排序;也就是说,如果两个元素的键相等,则元素的顺序被保留。相反,不稳定的排序不会保留具有相同键的元素的顺序。
但是,我对 stable 和 unstable 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
与上述代码相关的两个疑惑:
- 对于具有相同
order的元素,是否会保留Parser将元素插入数组fields的顺序? -
printFields_Dic_Sort()的顺序是否与printFields_Sort_Dic()不同? (第一个转换然后排序;第二个排序然后转换为Dictionary)
无法加入这方面的权威信息... 谢谢
已编辑
为返回的Dictionary添加了类型
好的,这就是问题所在:
- 如果我想要
KeyValuePair(idx, obj) 不能使用array(错误:有可能;挑战在于如何在数组之间转换) - 我无法将排序后的
array存储在Dictionary中,因为不能保证遍历它会得到有序的values(正确:不保证通用字典,见下文) . - 可以选择使用
SortedDictionary。但是,没有从array到SortedDictionary的直接转换(除非您进行迭代)...(**对*?*:array/list和SortedDictionary之间没有简单的转换) - 然后,对于一个简单快速的解决方案,它只保留使用
list。但是,也有问题,因为它应该是KeyValuePair的列表,我不能通过使用toList直接从array获得(错误:有可能获取List (Of KeyValuePair (Of ...)),但是,它需要之前的转换)。
所以根本没有优雅(简短而简单)的解决方案...(正确:没有 cmets)
任何帮助将不胜感激(仍在使用它) - 已解决...
编辑二
好的,这就是我最终所做的:通过使用 array.ConvertAll (msdn) 并定义我自己的 Converter 函数来解决第 4 点的问题...转换 array 然后,得到一个List,ToList,来自已将类型转换为 (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)方法在技术上也是错误的。您返回Dictionary或IOrderedEnumerable,但方法签名是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