0

Currently, I have a List(of String) which contains data similar to:

"207.5,1"
"373,2"
"278.5,3"
"134,4"
"277,5"
"674,7"
"58.5,9"

To this list, I apply the two commands 'list.Sort' then 'list.Reverse' which both do exactly as expected, my List then contains:

"674,7"
"58.5,9"
"373,2"
"278.5,3"
"277,5"
"207.5,1"
"134,4"

As you can see, for all intents and purposes, this has worked perfectly, BUT, the sharp-eyed will notice that the entry "58.5,9" is out of place and should be at the bottom of the list.

I appreciate I am sorting strings here, so I'm bound to fail. What I need to discover please, is how can I copy the contents of the strings per line into another sortable container, which stores my numbers and 'indexes' as integers and/or singles? Ideally, I'll end up with an array or whatever of data like this:

674.0,7
373.0,2
278.5,3
277.0,5
207.5,1
134.0,4
58.5,9

I have tried as many iterations as I can think of (fairly new to this so probably missing the very obvious!). Please help if you can! Thanks.

1
  • 1
    String.Split(',') will given you the two parts, then all you need is to double.Parse and int.Parse to parse the numbers. Shove them in a struct and sort the structs based on value. Or, be slick, and implement IComparable (or whatever the correct sortable interface is used on structs). Commented Nov 10, 2016 at 17:44

4 Answers 4

1

You can use LINQ to do what you want. If you just want the strings sorted in the correct order you could use:

Dim input = {
    "207.5,1",
    "373,2",
    "278.5,3",
    "134,4",
    "277,5",
    "674,7",
    "58.5,9"
}

Dim sorted =
    From item In input
    Let n = CDec(item.Split(","c)(0))
    Order By n Descending
    Select item

This just converts the first number to decimal to use for sorting. If you want objects with the two numbers extracted, you could use:

Dim sorted2 =
    From item In input
    Let parts = item.Split(","c)
    Select result = New With { .n = CDec(parts(0)), .idx = CInt(parts(1)) }
    Order By result.n Descending

This gives you a IEnumerable of anonymous types with n and idx properties - you could create a class and create instances of that instead if you don't want anonymous types (e.g. you need to return it from a function).

Sign up to request clarification or add additional context in comments.

Comments

1

Using LINQ and Lambda expressions

Dim input = {
    "207.5,1",
    "373,2",
    "278.5,3",
    "134,4",
    "277,5",
    "674,7",
    "58.5,9"
}

' orderedResult is a IOrderedEnumerable(Of String)
Dim orderedResult = input.
    OrderByDescending(Function(item) CDec(item.Split(","c)(0)))

' dictResult is a Dictionary(Of Integer, Decimal) 
' based on the sorted result
Dim dictResult = orderedResult.ToDictionary(
    Function(item) CInt(item.Split(","c)(1)),
    Function(item) CDec(item.Split(","c)(0)))

Comments

1

The simplest way, at least in terms of lines of code, is to use the Sort overload which allows a Comparer. However, if you will be frequently using the numeric value(s) for these things, you should consider a class or structure for the data.

can I copy the contents of the strings per line into another sortable container

The problem is not the container, but the data. Strings of numerals do not sort by the numeric value.

Private Function ThingsCompare(x As String, y As String) As Int32
    Dim xVal As Double = Convert.ToDouble(x.Split(","c)(0))
    Dim yVal As Double = Convert.ToDouble(y.Split(","c)(0))

    If xVal < yVal Then Return -1
    If yVal < xVal Then Return 1

    ' equal, so compare segment 2
    Dim xVal2 As Double = Convert.ToDouble(x.Split(","c)(1))
    Dim yVal2 As Double = Convert.ToDouble(y.Split(","c)(1))

    If xVal2 < yVal2 Then Return -1
    If yVal2 < xVal2 Then Return 1

    Return 0
End Function

Usage:

things.Sort(AddressOf ThingsCompare)

Given the following data:

{"207.5,1", "373,2", "278.5,3", "9.1,1",
"9.1,9", "134,4", "277,5", "674,7", "58.5,9"}

(I added the "9" elements because as characters/numerals they will sort higher than all the others). Results:

9.1,1
9.1,9
58.5,9
134,4
207.5,1
277,5
278.5,3
373,2
674,7

Comments

0

If you're going to be using the data more than once then it makes sense to have a class which represents it. That way you can give the parts meaningful names, have an easy way to create a new item, easily manipulate the data, and have your own way of converting it to a string.

It may look like a load of fiddly code, but you only have to write it once and then your life is much simpler when you want to use the data:

Option Infer On
Option Strict On

Module Module1

    Public Class Datum
        Property Number As Decimal
        Property Index As Integer

        Sub New()
            ' default constructor
        End Sub

        Sub New(NumberIndex As String)
            Dim parts = NumberIndex.Split(","c)
            ' a simple parameter check
            If parts.Length <> 2 Then
                Throw New ArgumentException("No comma found in " & NameOf(NumberIndex))
            End If

            Number = CDec(parts(0))
            Index = CInt(parts(1))

        End Sub

        Public Overrides Function ToString() As String
            Return $"{Number},{Index}"

        End Function

    End Class

    Sub Main()
        Dim myList As New List(Of String) From {"207.5,1", "373,2", "278.5,3", "134,4", "277,5", "674,7", "58.5,9"}
        Dim myData = (myList.Select(Function(d) New Datum(d))).ToList()

        Dim dataDescending = myData.OrderByDescending(Function(d) d.Number).ToList()

        Console.WriteLine(String.Join(vbCrLf, dataDescending))

        Console.ReadLine()

    End Sub

End Module

Outputs:

674,7
373,2
278.5,3
277,5
207.5,1
134,4
58.5,9

2 Comments

Thanks Andrew - I will make preference to your response because it is the one that makes most sense to me (as I said, I'm pretty new to this), and it works perfectly within my existing code. Thanks also to the others who took time to try to help - it's much appreciated! :)
@MalcolmHutcheon Thanks for the feedback. Incidentally, if you actually want to show the ".0" for the numbers, you could change the line Return $"{Number},{Index}" to Return $"{Number:N1},{Index}".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.