Enumerable.OrderBy 方法是否保留具有相同排序(包括转换类型)的项目的插入顺序?
Does Enumerable.OrderBy Method preserve insertion order for items with same sorting - including conversion type?
我找到了 this documentation (Enumerable.OrderBy(Of TSource, TKey) Method (IEnumerable(Of TSource), Func(Of TSource, TKey))
),其中写着:
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.
但是,我不知道 stable 和 unstable sort 之间的区别 (?).
假设我有一个 public 属性,一个名为 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
(right:不保证通用词典,见下文)。
- 可以选择使用
SortedDictionary
。但是,没有从 array
到 SortedDictionary
的直接转换(除非你进行迭代)...(**对*?*:array
/[=34 之间没有简单的转换=] 和 SortedDictionary
)
- 那么,为了简单快捷的解决方案,它只保留使用
list
。但是,也有问题,因为它应该是 KeyValuePair
的列表,我不能通过使用 toList
直接从 array
获取( 错误:可以得到List (Of KeyValuePair (Of ...))
,但是需要事先转换)。
所以根本没有优雅(简短)的解决方案...(正确:无评论)
任何帮助将不胜感激(仍在使用它)- 在下面解决...
编辑二
好的,这就是我最终所做的:通过使用 array.ConvertAll
(msdn) 并定义我自己的 Converter
函数来解决第 4 点的问题...将array
然后,通过 ToList
从已经将类型转换为 (Of KeyValuePair (Of String, clsFld))
的中间 array
得到一个 List
。希望对其他人有帮助:
'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
稳定排序保留了它们具有相同值的元素的顺序,所以是的,它们将return按照添加它们的相同顺序进行编辑。但这并不重要,因为您稍后会将它们推送到字典中。见下文。
Dictionary<TKey, TValue>
没有定义顺序,可能不会保留元素 l 的顺序。因此 printFields_Sort_Dic()
可能 return 它们的顺序不同。
我找到了 this documentation (Enumerable.OrderBy(Of TSource, TKey) Method (IEnumerable(Of TSource), Func(Of TSource, TKey))
),其中写着:
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.
但是,我不知道 stable 和 unstable sort 之间的区别 (?).
假设我有一个 public 属性,一个名为 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
(right:不保证通用词典,见下文)。 - 可以选择使用
SortedDictionary
。但是,没有从array
到SortedDictionary
的直接转换(除非你进行迭代)...(**对*?*:array
/[=34 之间没有简单的转换=] 和SortedDictionary
) - 那么,为了简单快捷的解决方案,它只保留使用
list
。但是,也有问题,因为它应该是KeyValuePair
的列表,我不能通过使用toList
直接从array
获取( 错误:可以得到List (Of KeyValuePair (Of ...))
,但是需要事先转换)。
所以根本没有优雅(简短)的解决方案...(正确:无评论)
任何帮助将不胜感激(仍在使用它)- 在下面解决...
编辑二
好的,这就是我最终所做的:通过使用 array.ConvertAll
(msdn) 并定义我自己的 Converter
函数来解决第 4 点的问题...将array
然后,通过 ToList
从已经将类型转换为 (Of KeyValuePair (Of String, clsFld))
的中间 array
得到一个 List
。希望对其他人有帮助:
'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
稳定排序保留了它们具有相同值的元素的顺序,所以是的,它们将return按照添加它们的相同顺序进行编辑。但这并不重要,因为您稍后会将它们推送到字典中。见下文。
Dictionary<TKey, TValue>
没有定义顺序,可能不会保留元素 l 的顺序。因此printFields_Sort_Dic()
可能 return 它们的顺序不同。