简体   繁体   中英

vb.net sort list of objects with custom order

I have a list of objects and I'm trying to sort by two properties with one property being sorted by a custom order. The list has properties of ReqType and PartNumber. ReqType will be "M", "B", "S", or null and I would like to sort the list in that order. Then sort by PartNumber.

Input list:
PartNumber    ReqType
124           B
125           M
123           B
121           S
120           M
115            

Expected Sort:
PartNumber    ReqType
120           M
125           M
123           B
124           B
121           S
115            

I started with the code below but that only sorts ReqType alphabetically.

Return EBom.OrderBy(Function(f) f.ReqType).ThenBy(Function(f) f.PartNumber).ToList 

I then found a method to create a custom sort order using the code below. Though using Ebom.Sort() doesn't seem to allow me to tack on the second sort order for PartNumber. I realize I could probably add the PartNumber sorting to the custom function but that seems like a lot of work.

EBom.Sort()
Return EBom.ToList


Implements IComparable(Of EBomList)
Public Function SortReq(other As EBomList) As Integer Implements IComparable(Of EBomList).CompareTo
    If (Me.ReqType = other.ReqType) Then
        Return 0
    ElseIf (Me.ReqType = "M") Then
        Return -1
    ElseIf (Me.ReqType = "B") Then
        If (other.ReqType = "M") Then
            Return 1
        Else
            Return -1
        End If
    ElseIf (Me.ReqType = "S") Then
        If (other.ReqType = "M" Or other.ReqType = "B") Then
            Return 1
        Else
            Return -1
        End If
    Else
        Return 1
    End If
End Function

Is there an easier way to sort by a custom order or at least combine the custom sort function with a .thenby(.....) to get the order I'd like?

Cleaner code version to do this is to use a Function in the sort method like the following.

    d.Sort(Function(X As EBomList, Y As EBomList)
               Dim Tx As Integer = InStr("MBS ", X.ReqType.PadLeft(1, " "c))
               Dim Ty As Integer = InStr("MBS ", Y.ReqType.PadLeft(1, " "c))
               Select Case Tx
                   Case Is < Ty : Return -1
                   Case Is > Ty : Return 1
                   Case Else : Return X.PartNumber.CompareTo(Y.PartNumber)
               End Select
           End Function)

Note it only needs to check the partnumber when the type code is the same.

I am assuming your Partnumber is in fact a number. If it is a string you will need to pad it as appropriate. For example.

Return X.PartNumber.PadLeft(6," "c).CompareTo(Y.PartNumber.PadLeft(6," "c))

ALTERNATIVE AND FASTER APPROACH

If you have A LOT of data you may want to consider augmenting the EBomLit to create a sort key rather than doing that string search though...

As in...

Private _ReqType As String
Private _TypeKey As Integer 

Public Property ReqType As String
    Get
        Return _ReqType
    End Get
    Set(value As String)
        _ReqType = value
        _TypeKey = InStr("MBS ", value.PadLeft(1, " "c))
    End Set
End Property

Public ReadOnly Property TypeKey As Integer
    Get
        Return _TypeKey
    End Get
End Property

Then the sort becomes...

    d.Sort(Function(X As EBomList, Y As EBomList)
               Select Case X.TypeKey
                   Case Is < Y.TypeKey : Return -1
                   Case Is > Y.TypeKey : Return 1
                   Case Else : Return X.PartNumber.CompareTo(Y.PartNumber)
               End Select
           End Function)

FASTER STILL

You could even extend that further by creating a full sort key out of the "TypeKey" with the padded "PartNumber" and use those as a key to save the whole shebang in a SortedDictionary instead of a List.

Just create a list of ReqTypes and lookup the index:

Dim sortOrder = "MBS "

Dim sortedList = List.OrderBy(Function(x) sortOrder.IndexOf(If(x.ReqType Is Nothing, " ", x.ReqType))).ThenBy(Function(x) x.PartNumber).ToList()

Note: If sorting by something other than chars then you will need to create a propper array/list of the object to compare by.

A bit easier with Enum and Tuple if those are an option:

Enum PartNumber As Byte : M : B : S : __ : End Enum

Dim list = New List(Of Tuple(Of PartNumber, Integer)) From {
    Tuple.Create(PartNumber.B, 124),
    Tuple.Create(PartNumber.M, 125),
    Tuple.Create(PartNumber.B, 123),
    Tuple.Create(PartNumber.S, 121),
    Tuple.Create(PartNumber.M, 120),
    Tuple.Create(PartNumber.__, 115)}

list.Sort()

For Each item In list
    Debug.Print(item.Item2 & vbTab & item.Item1.ToString.Replace("_", ""))
Next

Output:

120 M
125 M
123 B
124 B
121 S
115 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM