Counting the number of certain characters in an array in VBA - vba

Hello all so I am very new to VBA but have take some beginner classes in Java and HTML so I Almost know what I am doing.
So I have a cell in which I want to split based on a comma (,)
and I want to count the number of occurrences of a certain Character and then display that result in another cell. Doing this while going down a column of these rows in that single column.
ex: y,y,y,n,y = in cell D6 : 4
Below is the bit of code I have generated with the little bit of information I have learned about VBA online.
I have also seen this bit of code but it get very complex very fast (link) so if someone could explain that one instead I would be very grateful.
(I tried to comment on the answer but dont have enough rep to do so)
And please when giving the answer try to explain the methods, I dont have an understanding of what each does, being both new to programming and VBA so in regaurds to your replies see if I am able to figure it out with a bit of a push.
Private Sub CommandButton15_Click()
Dim Number As String
Dim Yoccur As Integer
Dim Noccur As Integer
Dim Notapp As Integer
Dim length As Integer
Dim current As String
Dim i As Integer
Dim Row As Integer
Do While Row < 84
For i = 1 To length
'parse data into a array here
'tempArr = Split(X(lngRow, 2), ",")
' would that work if I tried to split based on the comma?
If current = "y" Then Yoccur = Yoccur + 1
If current = "n" Then Noccur = Noccur + 1
If current = "n/a" Then Notapp = Notapp + 1
Next i
Wend
Range("d45").Value = Yoccur
Range("d46").Value = Noccur
Range("d47").Value = Notapp
End Sub

The code below
Sets a range from D6 to the last used cell in column D
It joins all the values in this range together into a single string (strV) using Join
It dumps the number of y values in A1 by subtracting the length of the string with all y characters removed from the length of the unaltered string (more detailed explanation below)
[a1] = Len(strV) - Len(Replace(strV, "y", vbNullString)) puts the number of y values in A1
[a3] = (Len(strV) - Len(Replace(strV, "n/a", vbNullString))) / 3 puts the number of n/a values in A2 (the /3 is needed to count removing the 3 character length n/a as one replacement)
[a2].Value = Len(strV) - Len(Replace(strV, "n", vbNullString)) - [a3].Value] counts the number of n values - the number of n/a values in A3 (as n/a contains a n)
code
Sub QuickDump()
Dim rng1 As Range
Dim strV As String
Set rng1 = Range([d6], Cells(Rows.Count, "d").End(xlUp))
strV = Join(Application.Transpose(rng1.Value), ",")
d = Filter(Application.Transpose(rng1.Value), "y", True, vbTextCompare)
[a1] = Len(strV) - Len(Replace(strV, "y", vbNullString))
[a3] = (Len(strV) - Len(Replace(strV, "n/a", vbNullString))) / 3
[a2].Value = Len(strV) - Len(Replace(strV, "n", vbNullString)) - [a3].Value
End Sub

The following code should help. Note the assumptions:
Your input is located in column A of the sheet, across multiple rows with no spaces between them and starts at row 1.
The output column for "y" is D, for "n" is E and for "n/a" is F.
Private Sub CommandButton15_Click()
Dim lastRow As Long
Dim row As Long
Dim c As Range
Dim arr() As String
Dim s As Variant
Dim sumY As Long
Dim sumN As Long
Dim sumNA As Long
' This is the last row of your input in column A.
lastRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, 1).End(xlUp).row
For row = 1 To lastRow ' Loop all used rows
sumY = 0 ' Reset for every loop.
sumN = 0
sumNA = 0
Set c = Cells(row, 1)
arr = Split(c, ",") ' Split the value in the cell (row, 1) on comma.
' For each string in the split array, do lower-case comparisons
' for "y", "n" and "n/a" and increment appropriate counters.
For Each s In arr
If LCase(s) = "y" Then
sumY = sumY + 1
ElseIf LCase(s) = "n" Then
sumN = sumN + 1
ElseIf LCase(s) = "n/a" Then
sumNA = sumNA + 1
Else
MsgBox "Unknown value!" ' Sanity test if something is wrong with the input.
End If
Next
' Assign the sums of "y", "n" and "n/a" to columns 4, 5 and 6 (D, E and F).
Cells(row, 4).Value = sumY
Cells(row, 5).Value = sumN
Cells(row, 6).Value = sumNA
Next row
End Sub
I'm sure it's not "perfect" (it's very late -- or early -- here!) but it should work and it should at least give you some ideas on how to proceed from here.
Let me know if you need me to explain anything that's not clear.
UPDATE
Given the same assumptions as above (you can change column A to column C by replacing 1 to 3 in the column indexing of the code) and your comment about linebreaks if I understood it correctly, the following code will now not only calculate the values for each row but will instead count the occurrences of "y", "n" and "n/a" across all rows and will put the results in cell D1 for "y", E1 for "n" and F1 for "n/a".
Private Sub CommandButton15_Click()
Dim lastRow As Long
Dim row As Long
Dim c As Range
Dim arr() As String
Dim s As Variant
Dim sumY As Long
Dim sumN As Long
Dim sumNA As Long
' This is the last row of your input in column A.
lastRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, 1).End(xlUp).row
For row = 1 To lastRow ' Loop all used rows
Set c = Cells(row, 1)
arr = Split(c, ",") ' Split the value in the cell (row, 1) on comma.
' For each string in the split array, do lower-case comparisons
' for "y", "n" and "n/a" and increment appropriate counters.
For Each s In arr
If LCase(s) = "y" Then
sumY = sumY + 1
ElseIf LCase(s) = "n" Then
sumN = sumN + 1
ElseIf LCase(s) = "n/a" Then
sumNA = sumNA + 1
Else
MsgBox "Unknown value!" ' Sanity test if something is wrong with the input.
End If
Next
Next row
' Assign the sums of "y", "n" and "n/a" to columns 4, 5 and 6 (D, E and F).
Cells(1, 4).Value = sumY
Cells(1, 5).Value = sumN
Cells(1, 6).Value = sumNA
End Sub

Related

How do I pass an argument from a subroutine to a function in VBA?

I'm trying to look for values to create a final ticket number for a ticket reconciliation process. This is what should happen:
subroutine looks for a value in cell "Gx"
if it finds a value
pass value to function to strip out letters, convert to a number, pass back to subroutine to place in
cell "Ax"
if there is no value
pass value of "Cx" to function etc.
This loops through the number cells I have in my worksheet based on the number of rows filled in a separate column.
The function works fine by itself in the worksheet, but when I pass it a value from the subroutine column A fills up with the number of the row ie. A37=37, A8=8. I don't think I'm passing the argument correctly to the function, but I'm not certain. Here's the code for the subroutine and the function:
Sub final_ticket_number()
Dim rw As Integer
Dim i As Integer
'header label
Range("A1").Value = "Final Ticket #"
'set number of rows for loop
With Worksheets(1)
rw = .Range("B2").End(xlDown).Row
End With
'check col G for empty, use col C as backup
For i = 2 To rw
If Not IsEmpty(Cells(i, "G")) Then
'strip out letters in col G, place in col A
Cells(i, "A").Value = getNumeric("G" & i)
Else
'strip out letters in col C, place in col A
Cells(i, "A").Value = getNumeric("C" & i)
End If
Next i
End Sub
Function getNumeric(cellRef As String) As Long 'remove letters from ticket numbers
Dim stringLength As Integer
Dim i As Byte
Dim Result As String
stringLength = Len(cellRef)
'loops through each character in a cell to evaluate if number or not
For i = 1 To stringLength
If IsNumeric(Mid(cellRef, i, 1)) Then
Result = Result & Mid(cellRef, i, 1)
End If
Next i
'convert remaining characters to number
getNumeric = CLng(Result)
End Function
What am I missing?
As I understand it, the only thing that is wrong is your Len (cellRef), here you are only passing the range and not his value. See how I did it, I had to specify the spreadsheet, do the same that will work.
Use debug.print to see the outputs of the variables. Write in the code "debug.print XvariableX" and in the immediate check (Ctrl + G) you see the value assigned to the variable. good luck.
Sub final_ticket_number()
Dim rw As Integer
Dim i As Integer
Range("A1").Value = "Final Ticket #"
With Worksheets(1)
rw = .Range("B2").End(xlDown).Row
End With
For i = 2 To rw
If Not IsEmpty(Cells(i, "G")) Then
Cells(i, "A").Value = getNumeric("G" & i)
Else
Cells(i, "A").Value = getNumeric("C" & i)
End If
Next i
End Sub
Function getNumeric(cellRef As String) As Long 'remove letters from ticket numbers
Dim stringLength As Integer
Dim i As Byte
Dim Result As String
Dim Wrs As String
Wrk = ActiveWorkbook.Name
Workbooks(Wrk).Activate
Wrs = ActiveSheet.Name
stringLength = Len(Workbooks(Wrk).Worksheets(Wrs).Range(cellRef))
For i = 1 To stringLength
If IsNumeric(Mid(Workbooks(Wrk).Worksheets(Wrs).Range(cellRef), i, 1)) Then
Result = Result & Mid(Workbooks(Wrk).Worksheets(Wrs).Range(cellRef), i, 1)
End If
Next i
getNumeric = CLng(Result)
End Function

Auto scheduling

I am trying to make an auto scheduling program with an excel.
For example, each number is certain job assigned to the person given day.
1/2 1/3 1/4 1/5
Tom 1 2 2 ?
Justin 2 3 1 ?
Mary 3 3 ?
Sam 1 ?
Check O O X ? ## check is like =if(b2=c2,"O","X")
The things I want to make sure is every person is given a different job from yesterday.
My idea
while
randomly distribute jobs for 1/5
wend CheckCell = "O"
But I found that checking cell in the vba script doesn't work - the cell is not updated in each while loop.
Could you give me a little pointer for these kinds of program? Because I am new to vbaScript, any kinds of help would be appreciated.
Using VBA, I'm sure there are better ways to do this, but this will check the values from the penultimate column against values from last column and if they match it will write "O" to under the last column, else it will write "X":
Sub foo()
Dim ws As Worksheet: Set ws = Sheets("Sheet1")
'declare and set your worksheet, amend as required
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
'get the last row with data on Column A
LastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
counter = 0 'set counter
For i = 2 To LastRow 'loop through penultimate column and add values to array
If ws.Cells(i, LastCol - 1).Value <> "" Then
Values = Values & ws.Cells(i, LastCol - 1) & ","
End If
Next i
Values = Left(Values, Len(Values) - 1)
Values = Split(Values, ",") 'split values into array
For i = 2 To LastRow 'loop through last column and add values to array
If ws.Cells(i, LastCol).Value <> "" Then
ValuesCheck = ValuesCheck & ws.Cells(i, LastCol) & ","
End If
Next i
ValuesCheck = Left(ValuesCheck, Len(ValuesCheck) - 1)
ValuesCheck = Split(ValuesCheck, ",")
For y = LBound(Values) To UBound(Values) 'loop through both arrays to find all values match
For x = LBound(ValuesCheck) To UBound(ValuesCheck)
If Values(y) = ValuesCheck(x) Then counter = counter + 1
Next x
Next y
If counter = UBound(Values) + 1 Then 'if values match
ws.Cells(LastRow + 1, LastCol).Value = "O"
Else 'else write X
ws.Cells(LastRow + 1, LastCol).Value = "X"
End If
End Sub
just to clarify are you looking to implement the random number in the vba or the check.
To do the check the best way would be to set the area as a range and then check each using the cells(r,c) code, like below
Sub checker()
Dim rng As Range
Dim r As Integer, c As Integer
Set rng = Selection
For r = 1 To rng.Rows.Count
For c = 1 To rng.Columns.Count
If rng.Cells(r, c) = rng.Cells(r, c + 1) Then
rng.Cells(r, c).Interior.Color = RGB(255, 0, 0)
End If
Next c
Next r
End Sub
this macro with check the text you have selected for the issue and change the cell red if it matches the value to the right.
To make it work for you change set rng = selection to your range and change the rng.Cells(r, c).Interior.Color = RGB(255, 0, 0) to the action you want
A sligthly different approach than the other answers.
Add this function:
Function PickJob(AvailableJobs As String, AvoidJob As String)
Dim MaxTries As Integer
Dim RandomJob As String
Dim Jobs() As String
Jobs = Split(AvailableJobs, ",")
MaxTries = 100
Do
MaxTries = MaxTries - 1
If MaxTries = 0 Then
MsgBox "Could find fitting job"
End
End If
RandomJob = Jobs(Int((1 + UBound(Jobs)) * Rnd()))
Loop Until RandomJob <> AvoidJob
PickJob = RandomJob
End Function
And put this formula in your sheet
=PickJob("1,2,3",D2)
where D2 points to is the previous job

Sum into blank cell while summing all above until next blank cell, in Excel VBA

I've got this spreadsheet in which I need to Sum up worked hours.
In Column 'I' I've got all worked hours which I sorted through weeknumbers in row 'E' with the following loop I found somewhere on Google (can't remember who wrote it but it works).
Dim i, itotalrows As Integer
Dim strRange As String
itotalrows = ActiveSheet.Range("E20000").End(xlUp).Offset(1, 0).Row
Do While i <= itotalrows
i = i + 1
strRange = "E" & i
strRange2 = "E" & i + 1
If Range(strRange).Text <> Range(strRange2).Text Then
Rows(i + 1).Insert
itotalrows = ActiveSheet.Range("E20000").End(xlUp).Offset(1, 0).Row
i = i + 1
End If
Loop
In the picture you can see one of the cells marked with "Total value of cells up
"
there's a blank every few rows with a cell on 'I' where the total value should go.
Sheet Picture:
Perhaps to sum the groups in column I, based on where the blanks are in column G
Sub x()
Dim r As Range
For Each r In Range("G:G").SpecialCells(xlCellTypeConstants).Areas
r.Cells(r.Count + 1).Offset(, 2).Value = WorksheetFunction.Sum(r.Offset(, 2))
Next r
End Sub
If you were to replace your code with the following, I believe it should do what you expect:
Sub foo()
Dim i, itotalrows As Integer
Dim strRange As String
itotalrows = ActiveSheet.Range("E20000").End(xlUp).Offset(1, 0).Row
Do While i <= itotalrows
i = i + 1
strRange = "E" & i
strRange2 = "E" & i + 1
If Range(strRange).Text <> Range(strRange2).Text Then
Rows(i + 1).Insert
Cells(i + 1, "I").FormulaR1C1 = "=SUMIF(C[-4],R[-1]C[-4],C)"
'when inserting a new row, simply add this formula to add up the values on column I
itotalrows = ActiveSheet.Range("E20000").End(xlUp).Offset(1, 0).Row
i = i + 1
End If
Loop
End Sub
Seeing as your code already does what you wanted (ie. add a new row when values on Column E differ) then adding the formula into that row will add up anything on Column I where the value of Column E is the same.
This is a general approach how to sum the cells in the blank cell.
If this is the input then the right ppicture should be the output:
.
Using this code:
Sub TestMe()
Dim myCell As Range
Dim currentSum As Double
For Each myCell In Worksheets(1).Range("A1:A14")
If myCell = vbNullString Then
myCell = currentSum
myCell.Interior.Color = vbRed
currentSum = 0
Else
currentSum = currentSum + myCell
End If
Next myCell
End Sub
The idea is simply to use a variable for the currentSum and to write it every time when the cell is empty. If it is not empty, increment it with the cell value

VBA, Parse by "|" and Transpose, Next row

I have the following data in a cells A1
|stack|over|flow|
and cells A2..
|today|is|friday
How can I delimit this and transpose it into a vertical/column based view view?
Delimiting will give me data row based, which is good but that I have to transpose this manually each time. I plan to do this for many rows. I realized this could be tricky as the next row will need to be pushed back down for each time.
Result A1:A6:
Stack
Over
flow
today
is
friday
Edit
For unlimited rows and unlimited columns:
Sub splt()
Dim str As String
Dim col As Long, rw As Long, colcnt As Long, rwcnt As Long
With Sheets("Sheet1")
colcnt = .Cells(1, .Columns.Count).End(xlToLeft).Column 'total no of columns
For col = 1 To colcnt
rwcnt = .Cells(.Rows.Count, col).End(xlUp).Row 'total no of rows for specific column
For rw = 1 To rwcnt
str = str & .Cells(rw, col)
Next rw
rw = 1
For Each Item In Split(str, "|") 'split string and display output
If Item <> "" Then
.Cells(rw, col) = Item
rw = rw + 1
End If
Next
str = ""
Next
End With
End Sub
Edit:
You can use an array for this, but the following method is less complicated to easy to write and read:
Sub splt()
Dim rw As Long, i As Long, rwcnt As Long
i = 1
With Sheets("Sheet1")
rwcnt = .Cells(.Rows.Count, 2).End(xlUp).Row 'last non-empty row number
For rw = 1 To rwcnt 'from row 1 till last non-empty row
For Each Item In Split(.Cells(rw, 2), "|") 'split the string in column 2 from "|"
If Item <> "" Then ' 'if the splitted part of the string is not empty
.Cells(i, 4) = .Cells(rw, 1) 'populate column 4 with column 1
.Cells(i, 5) = Item 'populate column 5 with splitted part of the string
.Cells(i, 6) = .Cells(rw, 3) 'populate column 6 with column 3
i = i + 1 ' increase i variable by one to be able to write the next empty row for the next loop
End If
Next 'loop to next splitted string
Next rw 'loop to next row
.Columns("A:C").EntireColumn.Delete 'when all data is extracted to Columns D-E-F, delete Columns A-B-C and your results will be in Column A-B-C now
End With
End Sub
This one manages an unlimited number of rows on column A
Sub go()
Dim strFoo As String
Dim LastRow As Long
Dim LastPosition As Long
Dim MySheet As Worksheet
Dim arr() As String
Dim i As Long
Dim j As Long
Set MySheet = ActiveWorkbook.ActiveSheet
MySheet.Range("A1").EntireColumn.Insert
LastRow = MySheet.Cells(MySheet.Rows.Count, "B").End(xlUp).Row
LastPosition = 1
For i = 1 To LastRow
strFoo = MySheet.Range("B" & i)
If strFoo <> "" Then
arr = Split(strFoo, "|")
For j = 0 To UBound(arr)
If arr(j) <> "" Then
MySheet.Range("A" & LastPosition) = arr(j)
LastPosition = LastPosition + 1
End If
Next j
End If
Next i
End Sub
You can do this with Power Query or Get & Transform
Data --> Get & Transform Data --> From Table/Range
Then in the Query Editor
Split Column by Delimiter
Use a Custom Delimiter: the Pipe |
Split at left most (to get rid of that first pipe
Remove Column 1 (the blank column)
Split Column by delimiter
Use the Advanced Option and select to split into rows
Save and you are done.

How to read a string starts with "PR" in a cell ( multiple strings in a cell) and write them to the next column

Option Explicit
Const strText As String = "PR"
Sub InStrTakeNext10()
Dim MainString As String 'String1
Dim SubString As String 'String2
Dim TempString As String 'String3
Dim lastrow As Long, lCount As Long
Dim i As Integer,j As Integer
'---INPUT---
SubString = "SR"
'Also adjust the MainString line in the For Loop
'-----------
lastrow = ActiveSheet.Range("J3").End(xlUp).Row
For i = 3 To lastrow
MainString = Range("J" & i)
If InStr(MainString, SubString) <> 0 Then
'HOW CAN I WRITE THE ALREADY FOUND STRING INTO THE NEXT COLUMN?????
'MainString contains the SubString
TempString = Mid(MainString, "SR" + 0, 10)
For j = i + 1 To i + 10
'Copy the next 10 lines to Column H
lCount = lCount + 1
Range("I" & lCount).Value = TempString
'------ MID(C22,FIND("SR",C22)+0,10)
Next j
i = i + 10 'skip the next 10 cells
End If
Next i
End Sub
Please click here to see the Example -The PR numbers in the column c, and i need them to get added to the Column B after rows are added
1.I Initially I have ID and Description Columns with data.
2. The Description section contains multiple strings starts with "PR". This PR is followed by 15 numbers ( maximum)
3. Need to read number of PR strings in the C cell and add that number of raws below.
4. Then write that PR number on B columns for rows ( what is added)