How to sort values of textboxes in vb? - vb.net

I have two groups of textboxes, A and B and these are created dynamically.
My program should work like this:
1. A textboxes have corresponding B textboxes.
2. Then, B textboxes should be sorted by their values in ascending order.
3. Based on that order, the A textboxes' values will be sorted also.
EX:
A B
5 1
2 0
3 4
1 5
Output is: 2 5 3 1
Please help me out. Thanks!

Create arrays of the textbox controls you have and then write a simple bubble sort. Bubble sort is slow, but more than fast enough for small amounts of data.
Dim arrA() As Textbox = {a1, a2, a3, a4, a5}
Dim arrB() As Textbox = {b1, b2, b3, b4, b5}
Dim Changed as Boolean
Do
Changed = False
For i = 0 to arrB.Count - 2 'Stop at the second to last array item because we check forward in the array
If CInt(arrB(i).Text) > CInt(arrB(i + 1).Text) Then 'Next value is smaller than previous --> Switch values, also switch in arrA
Dim Temp as String = arrB(i + 1).Text
arrB(i + 1).Text = arrB(i).Text
arrB(i).Text = Temp
Temp = arrA(i + 1).Text
arrA(i + 1).Text = arrA(i).Text
arrA(i).Text = Temp
Changed = True
End If
Next
Loop Until Changed = False 'Cancle the loop when everything is sorted
Now the textbox values are sorted and you can display the results whereever you want.
To display the values in the labels, say called l1-l5:
Dim arrL() as Label = {l1, l2, l3, l4, l5}
For i = 0 to 4
arrL(i).Text = arrA(i).Text
Next

Related

Select Second Pivot Table Row Data After First Row Input VBA Userforms & ComboBox

I have a Userform in which I submit some data, from the data my box populates with more data, and after I select the data from that box I need to select data from for that item from a third box. The data gathered is from a PivotTable.
Box1 is just a supplied combobox while 2 and 3 are directly from a PivotTable. I have the functionality for Box2 working based off Box1 but that's because they are separated by sheets.
The goal is that Box1 can = A,B,C then if Box1=A then Box2 can = 1,2,3 then if Box2 = 1 then Box3= x,y,z.
The problem being in Box3 it returns the information for Box2= 1,2 AND 3 rather than just 1.
My current code is:
lCommentCount = Sheets(pt).PivotTables("Pivottable1").TableRange2.Rows.Count
For i = 1 To lCommentCount
If Me.ptDatabox.Value = Sheets(pt).PivotTables("PivotTable1").PivotFields("ptData").LabelRange.Offset(i, 0).List Then
OEEDataEntry.commentbox.AddItem Sheets(pt).PivotTables("PivotTable1").PivotFields("ptData").LabelRange.Offset(i, 1)
End If
Next i
For i = Me.commentbox.ListCount - 1 To 0 Step -1
If Me.commentbox.List(i) = "" Or Me.commentbox.List(i) = "Grand Total" Or Me.commentbox.List(i) = ("(blank)") Then
Me.commentbox.RemoveItem (i)
End If
Next i
I have been toying around with this all day and found that you have to make the second row visible. This code works well. Note showing the detail BEFORE counting.
Sheets(pt).PivotTables("PivotTable1").PivotFields("ptData").ShowDetail = _
True
For i = 1 To lCommentCount
pti = Sheets(pt).PivotTables("PivotTable1").PivotFields("ptData").LabelRange.Offset(i, 0).Value
If Me.ptDatabox.Text = pti Then
OEEDataEntry.commentbox.AddItem Sheets(pt).PivotTables("PivotTable1").PivotFields("ptData").LabelRange.Offset(i, 1)
End If
Next i
For i = Me.commentbox.ListCount - 1 To 0 Step -1
If Me.commentbox.List(i) = "" Or Me.commentbox.List(i) = "Grand Total" Or Me.commentbox.List(i) = ("(blank)") Then
Me.commentbox.RemoveItem (i)
End If
Next i
If for whatever reason you want to change the information given in the first box then you would need to make it so that on reexit of that first box that you collapse all the bullets so Sheets(pt).PivotTables("PivotTable1").PivotFields("ptData").ShowDetail = False
but that needs to be before you start counting items in the list.

Randomly select an item from a list based on a class, repeat number of times based on different numbers

I am not familiar with using macro's, but I think that what I would like excel to perform is best handled with a macro. So I can use all the input you may have!
I have these headers;
ID Tag Pen Sex Weight Class Inside range
With 450 rows of data. Based on the distribution of the weight data, I have in two other columns (class and number) the number of rows I want to select within each class. The selected rows must have the value "Yes" in the column "Inside range".
I want to randomly select the rows, based on the number needed for each class, and copy these rows to a new sheet. It sums up to 30 rows in the new sheet.
I hope you have a suggestion how to complete this action!
can you try the following, you will need to add a reference to Microsoft Scripting Runtime library:
Const rowCount = 450
Public Sub copyRows()
Dim i As Integer
Dim j As Integer
Dim classes As Scripting.Dictionary
Dim source As Worksheet
Dim colNumber As Integer
Dim colClassName as Integer
Dim colInsideRange As Integer
Dim allSelected As Boolean
Dim randomRow as Integer
Dim sumRemaining as Integer
allSelected = False
Set source = Worksheets("YourWorksheetName")
colClassName = 6 'this is the column number where class names are entered. I am assuming 6
colNumber = 7 'this is the column number where number of rows to be selected are entered. I am assuming 7
colInsideRange = 8 'this is the column number where "Inside Range" values are entered. I am assuming 9
For i = 2 to rowCount + 1 'assuming you have a header row
classes(CStr(source.Cells(i, colClassName))) = CInt(source.cells(i, colNumber)
Next i
Do until allSelected
Randomize
randomRow = Int ((Rnd * 450) + 2) 'assuming you have a header row, + 1 if you don't
If classes(CStr(source.Cells(randomRow, colClassName))) = 0 Then
With classes
sumRemaining = 0
For j = 1 to .Count - 1
sumRemaining = sumRemaining + .Items(j)
If sumRemaining > 0 Then Exit For
Next j
allSelected = (sumRemaining = 0)
End With
Else
source.Cells(randomRow, colInsideRange) = "Yes"
classes(CStr(source.Cells(randomRow, colClassName))) = classes(CStr(source.Cells(randomRow, colClassName))) - 1
End If
Loop
'Enter your code to copy rows with "Inside Range" = "Yes"
End Sub
Sorry if there are some errors or typos, I wrote from my mobile phone.

Show certain rows but keep others hidden

I am working with an excel workbook and on one of the worksheets are rows that hide or show depending on options selected in another worksheet. The structure looks something like this
A
1
2
3
4
B
1
2
3
4
C
1
2
3
4
Where they have the option to hide all of A and B, A and C, A, B, or C. The user has the option to hide A and B or C (they must select between B or C).They also have the option to hide the individual rows under each letter. Rows 1, 2, and 3. If the option to hide 2 is checked, all "2" rows under each letter is hidden. If they unchecked this option, all 2 rows appear once more. The problem is that the "2" row of an already hidden letter will display.
I have run into a mental block, but this is what I've done. Psuedocode for readability because right now my code is messy and I hate the way vba looks. This is a logic problem more than a syntax problem anyway.
Property hiddenA As Bool get let
Property hiddenB As Bool get let
Property hiddenC As Bool get let
OptionButton1.Click()
hiddenA = true
Hide A row and all rows associated with it
OptionButton2.Click()
HiddenA = false
Show A row and all rows associated with it
OptionButton3.Click()
HiddenB = false
HiddenC = true
Show B row and all rows associated with it
Hide C row and all rows associated with it
OptionButton4.Click()
HiddenB = true
HiddenC = false
Hide B row and all rows associated with it
Show A row and all rows associated with it
CheckBox1.Click()
if CheckBox1.value = false Then
Hide all "1" rows
Else
Show all "1" rows, but keep the "1"s under already hidden letters, hidden.
This is the problem.
And so on. There are checkboxes for showing/hidden all 2, 3, and 4 rows as well.
Rough outline - UNTESTED CODE... something like this should unhide everything, check each checkbox for status, add checked boxes to range, and hide that entire range at end.
'CheckBox1 is Row 1 in group
'CheckBox2 is Row 2 in group
'CheckBox3 is Row 3 in group
'CheckBox4 is Row 4 in group
'CheckBox5 is Row 5 in group
'CheckBox6 is Group A
'CheckBox7 is Group B
'CheckBox8 is Group C
'CheckBox9 is Group D
'CheckBox10 is Group E
Sub CheckBoxClick() 'Assign this to all checkboxes
Application.ScreenUpdating = False 'Turn off screen updating
ActiveSheet.Cells.EntireRow.Hidden = False 'Unhide all
Dim RngCnt As Range
Dim LastRow As Long
Dim CurRow As Long
Dim ChkBx As OLEObject
LastRow = Range("A" & Rows.Count).End(xlUp).Row
For Each ChkBx In ActiveSheet.OLEObjects
If TypeName(ChkBx.Object) = "CheckBox" Then
Select Case ChkBx.Name
Case "CheckBox1"
If ChkBx.Value = True Then
RngCnt = Union(RngCnt, Range(Rows this chk box effects))
End If
Case "CheckBox2"
If ChkBx.Value = True Then
RngCnt = Union(RngCnt, Range(Rows this chk box effects))
End If
Case ETC, ETC, ETC to "CheckBox10"
...
End If
Next ChkBx
RngCnt.EntireRow.Hidden = True
Application.ScreenUpdating = True
End Sub

List occupied cells across multiple cells

I have 4 variables and wish to list which (maximum of 3) variables are being used.
I have used VBA functions before but I am stumped as to the reason this isn't working..
The four variables are percentages so for example:
if (20%,empty,20%,60%) I want the three cells to be (A,C,D)
if (50%,50%,empty,empty) => (A,B,empty)
Hello,
if (empty,empty,100%,empty) => (C,empty,empty)
The code I have at the moment isn't working (for the first cell):
Function whichtoa(w As Integer, x As Integer, y As Integer, z As Integer) As String
If w <> 0 Then
whichtoa = "A"
ElseIf x <> 0 Then
whichtoa = "B"
ElseIf y <> 0 Then
whichtoa = "C"
ElseIf z <> 0 Then
whichtoa = "D"
End If
End Function
Could it be to do with the empty cells being general and the others being a percentage? I can't really change this as the data is coming from another program.
Could I use a null check or something similar?
Thanks in advance!
Lucas
Consider the following data. the last column has the formula for whichtoA
A B C D E
60% 40% 30% 30% ABC
30% 60% 30% 90% ABC
10% 20% 50% ABC
30% 50% BC
30% C
50% 60% CD
If you are using percentages you need to use something other than integer in your function since you're dealing with decimals.
Function whichtoa(w As Double, x As Double, y As Double, z As Double) As String
Dim emptyCount As Integer
Dim results As String
' Assume zero
valueCount = 0
' Assume empty string
results = ""
If w <> 0 Then
results = results & "A"
valueCount = valueCount + 1
End If
If x <> 0 Then
results = results & "B"
valueCount = valueCount + 1
End If
If y <> 0 Then
results = results & "C"
valueCount = valueCount + 1
End If
' This is the only time you need to check the condition of valueCount. If you want 3 maximum
If (z <> 0) And (valueCount < 3) Then
results = results & "D"
End If
whichtoa = results
End Function
Each condition is checked individually. The If block you have will only process the first match and then stop evaluating the block. Then, counting the number of positive values, or hits if you will, with valueCount we can stop processing if we get 3 hits. This only needs to be checked with z parameter in the event we have 3 hits already at that point. Build the results as a string and return it.
Your conditional statement is chained: each ElseIf is only evaluated if the preceding If evaluates to True, so the function will only return a single string value (either A, B, C, or D but not a combination of multiple possible values, which would require stroing them all in a collection/dictionary/array/etc., and removing the ones that are empty values.
Compounded by implied type conversion (presumably you're passing range objects to this function, on a worksheet, which evaluate to their .Value which is "0" if the range is empty.
Another problem you may not have hit yet (if you're still working through the above) is that if the cell values contain percentages, by casting them as Integer in the function declaration, any values which round down to 0 will be evaluated as zero.
I suggest declaring the variables as Range objects, and then specifically check their .Value property. Store ALL cells and a key value ("A", "B", etc.) in a dictionary. Iterate the dictioanry and check the value for emptiness:
I also use this to return an error value if the dictionary contains 4 items, since you want a maximum of 3.
Function whichtoa(a As Range, b As Range, c As Range, d As Range)
Dim dict As Object
Dim itm As Variant
Set dict = CreateObject("Scripting.Dictionary")
'Add each cell value to a dictionary, using unique letter as Key
dict("A") = a.Value
dict("B") = b.Value
dict("C") = c.Value
dict("D") = d.Value
'iterate the dictionary keys, removing empty values
For Each itm In dict.Keys()
If IsEmpty(dict(itm)) Then dict.Remove (itm)
Next
If Not dict.Count = 4 Then
whichtoa = Join(dict.Keys(), ",")
Else:
whichtoa = CVerr(2023)
End If
End Function
I'm not sure exactly what the return value is that you want (your example are inconsistent), but the following may point you in the right direction:
Public Function whichtoa(r as Range)
Dim arr, i
arr = Array("A", "B", "C", "D")
For i = 0 to 3
If IsEmpty(r.Cells(1,i+1) Then
arr(i) = "empty"
End If
Next
whichtoa = arr(0) & "," & arr(1) & "," & arr(2) & "," & arr(3)
End Function

return single values for multiple records

Is there a way to merge multiple records then display only the highest value for each column? Example: A2:A25=names, B2=Grade1, C2=Grade2...etc.
First I removed duplicates in case there are exact duplicates. Then I sort on Name.
Can something be added to this code, based on column A-names, to display each name once with the highest value from each column?
=IF(B2="","Empty",IF(B2="High","High",IF(B2="Med","Med",IF(B2="Low","Low",""))))
Data Example
A1:name B1:Grade1 C1:Grade2...etc
A2:Joe B2:High C3:Low
A3:Joe B3:Med C3:High
A4:Dan B4:Low C4:Med
A5:Dan B5:Low C5:Low
__Results: Joe Grade1=high Grade2=high, Dan: Grade1=Low Grade2=Med
Record an Excel macro. Select first column. Click advanced filter.Choose copy to location and select a new column say X. Enable unique filter. Now click Ok. Now look at vba source to get the code to get unique elements in a column. Now assign Low as 0, Med as 1, High as 2 . loop through the rows and find the maximum grade1 , maximum grade2 etc corresponding to each element in column X and populate columns Y,Z etc. As and when you find a new maximum replace the existing. Now you will have the required data in columns X,Y,Z. Loop through them again and display in the format what you needed.
Decided to try VBA code for this one. It's a bit bruitish, but gets the job done.
Took a shortcut and made columns b and c numbers rather than strings. You could do a lookup function on the spreadsheet to make that conversion, or add an extra check in the code.
Sub find_high_values()
' subroutine to find max values of columns b and c against names
' assumes for simplicity that there are no more than 10 rows
' assumes values being checked to be numbers, if they are strings, additional loops would need to be done
Dim sName(10) As String, lBval(10) As Long, lCval(10) As Long 'arrays for original list
Dim iCountN As Integer, iUnique As Integer, iUniqueCount As Integer 'counters
Dim bUnique As Boolean
Dim rStart As Range, rOutput As Range 'ranges on worksheet
Dim lBmax(10) As Long, lCmax(10) As Long, sUniqueName(10) As String 'output arrays
Set rStart = ActiveSheet.Range("d6") 'Cell immediately above the first name in list
Set rOutput = ActiveSheet.Range("j6") 'cell reference for max value list
iUniqueCount = 1
For iCountN = 1 To 10 'set max counters to a min value
lBmax(iCountN) = 0
lCmax(iCountN) = 0
Next
For iCountN = 1 To 10 'step through each original row
sName(iCountN) = rStart.Offset(iCountN, 0).Value
lBval(iCountN) = rStart.Offset(iCountN, 1).Value
lCval(iCountN) = rStart.Offset(iCountN, 2).Value
bUnique = True 'Starter value, assume the name to be unique, changes to false if already in list
For iUnique = 1 To iCountN 'loop to check if it is a new name
If sUniqueName(iUnique) = sName(iCountN) Then bUnique = False
Next
If bUnique Then 'if new name, add to list of names
sUniqueName(iUniqueCount) = sName(iCountN)
iUniqueCount = iUniqueCount + 1
End If
Next
iUniqueCount = iUniqueCount - 1 'make the count back to total number of names found
For iUnique = 1 To iUniqueCount 'loop through names
For iCountN = 1 To 10 'loop through all values
If sName(iCountN) = sUniqueName(iUnique) Then
If lBval(iCountN) > lBmax(iUnique) Then lBmax(iUnique) = lBval(iCountN)
If lCval(iCountN) > lCmax(iUnique) Then lCmax(iUnique) = lCval(iCountN)
End If
Next
Next
'output section
rStart.Resize(1, 3).Select
Selection.Copy
rOutput.PasteSpecial xlPasteValues
For iUnique = 1 To iUniqueCount
rOutput.Offset(iUnique, 0).Value = sUniqueName(iUnique)
rOutput.Offset(iUnique, 1).Value = lBmax(iUnique)
rOutput.Offset(iUnique, 2).Value = lCmax(iUnique)
Next
End Sub