Reputation: 3
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.
Upvotes: 0
Views: 74
Reputation: 38875
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
Upvotes: 1
Reputation: 15774
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)))
Upvotes: 1
Reputation: 25013
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
Upvotes: 0
Reputation: 8160
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).
Upvotes: 1