Reputation: 11
Example of code presented below is working fine, it's doing what is supposed to do, but I'm not satisfied. Looking for much smarter solution in VB.NET. The presentation of results (I mean counts for each subgroup) is quite awkward. The content of data and list of records etc. are not important. Also counts should be sorted in order Less{0}, From{1}To{2}, MoreThan{3}...Thanks in advance.
Dim Age1 As Integer = 5
Dim Age2 As Integer = 9
Dim myList As New List(Of Integer) = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10, 2, 4, 5, 7, 8, 9, 6, 7, 9, 11}
Dim Lambda = myList.GroupBy(Function(x) New With {Key .Age1 = (x) < Age1,Key .Age2 = (x) > Age1 - 1 And (x) <= Age2,Key .Age3 = (x) > Age2}).ToList()
Dim group1, group2, group3 As Integer
myList = myList.OrderBy(Function(x) x).ToList()
Console.WriteLine(String.Join(",", myList.Select(Function(s) s.ToString).ToArray))
For Each group In Lambda
If group.Key.Age1 Then group1 = group.Count()
If group.Key.Age2 Then group2 = group.Count()
If group.Key.Age3 Then group3 = group.Count()
Next
' Obviously If Stop Then Error condition
If group1 + group2 + group3 <> myList.Count Then Stop
Console.WriteLine(String.Format("Groups: Less{0},From{1}To{2},MoreThan{3}", Age1, Age1, Age2 - 1, Age2))
Console.WriteLine(String.Format(" Age: {0,4},{1,8},{2,8}", group1, group2, group3))
'1,2,2,3,4,4,5,5,6,6,7,7,7,8,8,9,9,9,10,11
'Groups: Less5,From5To8,MoreThan9
'Age: 6, 12, 2
Upvotes: 0
Views: 204
Reputation: 11
Here is 2'nd fastest and I think quite 'clean' solution based on @Enigmativity concept posted a couple of hours ago...Takes a care of his n-bands approach
Function simpleCSVsplit(ageForBins() As Integer, myList As List(Of Integer)) As List(Of Integer)
Dim Bands As New List(Of Integer)
For indx As Integer = 0 To ageForBins.Count - 1
Bands.Add(myList.Where(Function(x) x < ageForBins(indx)).Count())
myList = myList.Skip(Bands(indx)).ToList()
Next
Bands.Add(myList.Count)
Return Bands
End Function
Upvotes: 0
Reputation: 117064
It seems to me that this is the simplest way to do it:
Dim Age1 As Integer = 5
Dim Age2 As Integer = 9
Dim myList As New List(Of Integer) From {1, 3, 5, 7, 9, 2, 4, 6, 8, 10, 2, 4, 5, 7, 8, 9, 6, 7, 9, 11}
Dim group1 As Integer = myList.Where(Function (x) x < Age1).Count()
Dim group2 As Integer = myList.Where(Function (x) x > Age1 - 1 And x <= Age2).Count()
Dim group3 As Integer = myList.Where(Function (x) x > Age2).Count()
If group1 + group2 + group3 <> myList.Count Then Stop
Console.WriteLine(String.Format("Groups: Less{0},From{1}To{2},MoreThan{3}", Age1, Age1, Age2 - 1, Age2))
Console.WriteLine(String.Format(" Age: {0,4},{1,8},{2,8}", group1, group2, group3))
If you want a funky LINQ-based method then try this:
Dim bands() As Func(Of Integer, Boolean) = _
{ _
Function (x) x < Age1, _
Function (x) x <= Age2, _
Function (x) True _
}
Dim counts = _
myList _
.GroupBy(Function (x) Enumerable.Range(0, bands.Count).Where(Function (n) bands(n)(x)).First()) _
.Select(Function (x) x.Count()) _
.ToArray()
Dim group1 As Integer = counts(0)
Dim group2 As Integer = counts(1)
Dim group3 As Integer = counts(2)
Upvotes: 1
Reputation: 26917
Here's how I would try to improve it:
Dim Age1 As Integer = 5
Dim Age2 As Integer = 9
Dim myList As New List(Of Integer) From {1, 3, 5, 7, 9, 2, 4, 6, 8, 10, 2, 4, 5, 7, 8, 9, 6, 7, 9, 11}
Console.WriteLine(String.Join(", ", myList.OrderBy(Function(x) x)))
console.WriteLine
Dim ageBins = myList.GroupBy(Function(age) If(age < Age1, 1, If(age >= Age1 And age <= Age2, 2, 3))) _
.Select(Function(agebin) New With { agebin.Key, .AgeCount = agebin.Count() }) _
.OrderBy(Function(agebin) agebin.Key)
For Each bin In ageBins
Dim msg As String
Select bin.Key
Case 1
msg = $"Less {Age1}"
Case 2
msg = $"From {Age1} To {Age2}"
Case Else
msg = $"MoreThan {Age2}"
End Select
Console.WriteLine($"{msg,12}: {bin.AgeCount}")
Next
You could also change the code to handle any number of bins:
Dim agesForBins = {5, 10}
Dim ageBins = myList.GroupBy(Function(age) Enumerable.Range(0, agesForBins.Length).Where(Function(n) age < agesForBins(n)).DefaultIfEmpty(agesForBins.Length).First) _
.Select(Function(agebin) New With { agebin.Key, .AgeCount = agebin.Count() }) _
.OrderBy(Function(agebin) agebin.Key)
For Each bin In ageBins
Dim msg As String
If bin.Key = 0 Then
msg = $"Less {agesForBins(0)}"
ElseIf bin.Key = agesForBins.Length Then
msg = $"MoreThan {agesForBins(bin.Key-1)-1}"
Else
msg = $"From {agesForBins(bin.Key-1)} To {agesForBins(bin.Key)-1}"
End If
Console.WriteLine($"{msg,12}: {bin.AgeCount}")
Next
Upvotes: 1