Count in numerical order only in blank (empty) cells - loops

I'm trying to use VBA to auto fill (1, 2, 3, ....) in column "A" while skipping rows that are not blank/empty. For example, if there is text/data in "A3" and "A5", the code would count as follows:
"A1" = 1 "B1" = text/data
"A2" = 2 "B2" = text/data
"A3" = text/data "B3" = text/data
"A4" = 3 "B4" = text/data
"A5" = text/data "B5" = text/data
"A6" = 4 "B6" = text/data
"A7" = 5 "B7" = text/data
...and so on
So far I've only been able to skip the rows with previously entered text but that count continues as if it wasn't skipping any cells.
Please Note: I'm using .End(xlDown).Count in column "B" to define how far down the counter should go.
This is what I have so far
Sub Counter()
Dim NoF As Long
Dim Test As Long
NoF = Range("B1", Range("B1").End(xlDown)).Count
For i = 1 To NoF
If Cells(i, 1) = "" Then
ActiveSheet.Cells(i, 1).Value = i
ElseIf Cells(i, 1) <> "" Then
ActiveSheet.Cells(i, 1).Offset(i + 1, 1).Select
End If
Next i
End Sub

You could use a second variable which counts the rows with text. Initialize it outside of the for loop to zero and add 1 if there was some text. Next, you just need to subtract it from i.
j = 0
For i = 1 To NoF
If Cells(i, 1) = "" Then
ActiveSheet.Cells(i, 1).Value = i - j
ElseIf Cells(i, 1) <> "" Then
ActiveSheet.Cells(i, 1).Offset(i + 1, 1).Select
j = j + 1
End If
Next i
You do not need to Offset, this works as well
Option Explicit
Sub Counter()
Dim NoF As Long
Dim j As Long
Dim i As Long
NoF = Range("B1", Range("B1").End(xlDown)).Count
j = 0
For i = 1 To NoF
If Cells(i, 1) = "" Then
Cells(i, 1).Value = i - j
Else
j = j + 1
End If
Next i
End Sub

Related

VBA: Subscript Out of Range - Size of array index is larger than array size

I created an array index (tickerIndex).
When I run the code, I get the error
subscript out of range
When I run the code, for some reason, the tickerIndex variable counts up to 13 which is 1 more than the size of the array.
The size of the tickers array is 12.
The tickerIndex variable is used to loop the tickers, tickerVolumes, tickerStartingPrices, and tickerEndingPrices arrays.
Dim tickers(12) As String
tickers(0) = "AY"
tickers(1) = "CSIQ"
tickers(2) = "DQ"
tickers(3) = "ENPH"
tickers(4) = "FSLR"
tickers(5) = "HASI"
tickers(6) = "JKS"
tickers(7) = "RUN"
tickers(8) = "SEDG"
tickers(9) = "SPWR"
tickers(10) = "TERP"
tickers(11) = "VSLR"
'Activate data worksheet
Worksheets(yearValue).Activate
'Get the number of rows to loop over
RowCount = Cells(Rows.Count, "A").End(xlUp).Row
Dim tickerIndex As Integer
tickerIndex = 0
Dim tickerVolumes(12) As Long
Dim tickerStartingPrices(12) As Single
Dim tickerEndingPrices(12) As Single
For tickerIndex = 0 To 11
ticker = tickers(tickerIndex)
tickerVolumes(tickerIndex) = 0
Worksheets(yearValue).Activate
For i = 2 To RowCount
'Increase volume for current ticker [error on this line]
***If Cells(i, 1).Value = tickers(tickerIndex) Then***
tickerVolumes(tickerIndex) = tickerVolumes(tickerIndex) + Cells(i, 8).Value
End If
' Check if the current row is the first row with the selected tickerIndex.
If Cells(i - 1, 1).Value <> tickers(tickerIndex) And Cells(i, 1).Value = tickers(tickerIndex) Then
tickerStartingPrices(tickerIndex) = Cells(i, 6).Value
End If
'check if the current row is the last row with the selected ticker
'If the next row's ticker doesn't match, increase the tickerIndex.
If Cells(i + 1, 1).Value <> tickers(tickerIndex) And Cells(i, 1).Value = tickers(tickerIndex) Then
tickerEndingPrices(tickerIndex) = Cells(i, 6).Value
End If
'Increase the tickerIndex if the next row’s ticker doesn’t match the previous row’s ticker.
If Cells(i + 1, 1).Value <> Cells(i - 1, 1).Value Then
tickerIndex = tickerIndex + 1
End If
Next i
Next tickerIndex
'Loop through arrays to output the Ticker, Total Daily Volume, and Return.
For i = 0 To 11
Worksheets("AllStocksAnalysis").Activate
Cells(4 + i, 1).Value = tickers(tickerIndex)
Cells(4 + i, 2).Value = tickerVolumes(tickerIndex)
Cells(4 + i, 3).Value = (tickerEndingPrices(tickerIndex) / tickerStartingPrices(tickerIndex)) - 1
Next i
Don't hard code your array bounds.
Do this
For tickerIndex = LBound(tickers) To UBound(tickers)
ticker = tickers(tickerIndex)
...
or better yet this
For Each ticker In tickers
...
instead of this
For tickerIndex = 0 To 11
ticker = tickers(tickerIndex)
...

Removing duplicates in a for loop crashes Excel

I have a for next loop that runs through a couple hundred thousand lines making changes on most. Could an array to make this code run faster?
The example of my for loop. Sometimes it gets overloaded and crashes Excel. (FYI my loop has to run bottom to top for it to do its intended purpose.)
Sub RemoveDuplicates()
Dim shWorkBook As Worksheet
Dim wkb As Workbook
Dim FullYearData As Worksheet
Set wkb = ThisWorkbook
With wkb
Set shWorkBook = .Sheets("Workbook")
Set shFullYearData = .Sheets("FullYearData")
End With
Dim i As Long
Dim LastRowW As Long
On Error Resume Next
Call TurnOffCalc
FirstRowW = shWorkBook.Cells(1, 1).Row
FirstColW = shWorkBook.Cells(1, 1).Column
LastRowW = shWorkBook.Cells(Rows.Count, 1).End(xlUp).Row
LastColW = shWorkBook.Cells(2, Columns.Count).End(xlToLeft).Column
i = LastRowW
Sum = 0
shWorkBook.Activate
For i = LastRowW To 1 Step -1
If shWorkBook.Cells(i, 7) = shWorkBook.Cells(i - 1, 7) Then
shWorkBook.Cells(i, 26) = vbNullString
End If
If shWorkBook.Cells(i, 26).Value <> "" Then
shWorkBook.Cells(i, 27) = Sum + Cells(i, 25).Value
Sum = 0
ElseIf shWorkBook.Cells(i, 26).Value = "" Then
Sum = shWorkBook.Cells(i, 25).Value + Sum
End If
Next
p = FirstRowW + 1
For p = FirstRowW + 1 To LastRowW Step 1
shWorkBook.Cells(p, 28) = Application.WeekNum(shWorkBook.Cells(p, 3))
Next
shWorkBook.Cells(1, 28).Value = "Week Number"
Call TurnOnCalc
End Sub
Try something like this:
Sub RemoveDuplicates()
Dim shWorkBook As Worksheet
Dim wkb As Workbook
Dim FullYearData As Worksheet
Dim i As Long, Sum
Dim LastRowW As Long, LastColW As Long, tbl As Range, data
Set wkb = ThisWorkbook
With wkb
Set shWorkBook = .Sheets("Workbook")
'Set shFullYearData = .Sheets("FullYearData")
End With
LastRowW = shWorkBook.Cells(Rows.Count, 1).End(xlUp).Row
LastColW = shWorkBook.Cells(2, Columns.Count).End(xlToLeft).Column
Set tbl = shWorkBook.Range("A1").Resize(LastRowW, 28) 'include "Week number" (?)
data = tbl.Value 'get the range value as an array
data(1, 28) = "Week Number"
Sum = 0
For i = LastRowW To 1 Step -1
If data(i, 7) = data(i - 1, 7) Then data(i, 26) = vbNullString
If data(i, 26).Value <> "" Then
data(i, 27) = Sum + data(i, 25).Value
Sum = 0
Else
Sum = data(i, 25).Value + Sum
End If
If i > 1 Then data(i, 28) = CLng(Format(data(i, 3), "ww"))
Next
tbl.Value = data 'return the data
End Sub

How to use an array variable instead of a range in a formula in VBA

So, I want to use 1D and 2D arrays in place of the ranges in formulas, except that whatever I have tried has not worked. If anyone can, can they please help?
Also, when transposing my data, I don't understand how to do this by referencing other sheets.
Sub Testrun()
Cells(5, 6).Value = "=Sum([myArr])"
Set mcco = Workbooks("Book1.xlsb").Worksheets("Sheet1")
Set mcfc = Workbooks("Book1.xlsb").Worksheets("Sheet2")
Set mcfb = Workbooks("Book1.xlsb").Worksheets("Sheet3")
TR = Application.CountA(Range("A:A"))
FTNRowStart = 1
MainRowStart = 2
CVTRRowEnd = mcco.Range("A2", mcco.Range("A2").End(xlDown)).Rows.Count + 1
FCRowEnd = mcfc.Range("A2", mcfc.Range("A2").End(xlDown)).Rows.Count + 1
MNCol = 2
FNCol = 4
FTNCol = 8
CVTRmyArr = Application.Transpose(Range(Cells(MainRowStart, MNCol), Cells(CVTRRowEnd, MNCol)))
FCmyArr = Application.Transpose(Range(Cells(MainRowStart, MNCol), Cells(FCRowEnd, MNCol)))
FNmyArr = Application.Transpose(Range(Cells(MainRowStart, FNCol), Cells(FCRowEnd, FNCol)))
mcfc.Activate
For i = 2 To TR
s = 0
TCJ = Cells(1, Columns.Count).End(xlToLeft).Column + 7
For j = 8 To TCJ
TCK = mcfb.Cells(i, Columns.Count).End(xlToLeft).Column + 1
For k = 2 To TCK
XD = "=COUNTIFS(CVTRmyArr,'Sheet3'!R" & i & "C" & k & ",'Sheet3'!R[0]C" & k & ",FNmyArr,""*"" & SUBSTITUTE(MID(FNmyArr,FIND(""*"",SUBSTITUTE(FNmyArr,""("",""*"",LEN(FNmyArr) - LEN(SUBSTITUTE(FNmyArr,""("","""")))) +1,LEN(FNmyArr)),"")"",""""))"
mcfc.Cells(i, j).Value = XD
s = s + Cells(i, j).Value
Cells(i, j).Value = s
Next k
Next j
Next i
End Sub

Why my If condition is returning wrong result?

I'm a student currently studying VBA in one of my classes, where the current assignment is to pull data from a .txt file and display it, as well as total it, and then grade the total. using arrays I've been successful in the first two parts using arrays, but when trying to factor in the total for a grade the array only takes the starting numbers into account. Any thoughts? Code below
Sub Categories()
Dim Locale As String, State(1 To 50) As Variant
Dim Serial(1 To 50) As Single, i As Single
Dim path As String, j As Single
Dim Score(1 To 50, 1 To 7) As Single
Dim IndexGrade(1 To 50) As Single
Dim Total(1 To 50) As Single
Locale = ActiveWorkbook.path
path = Locale & "US_States.txt"
Open path For Input As #1
For i = 1 To 50 Step 1
Input #1, Serial(i), State(i)
Sheet1.Cells(1 + i, 1).Value = Serial(i)
Sheet1.Cells(1 + i, 2).Value = State(i)
For j = 1 To 7 Step 1
Input #1, Score(i, j)
Total(i) = Total(i) + Score(i, j)
Sheet1.Cells(1 + i, 3).Value = Total(i)
Next j
Total(i) = Sheet1.Cells(1 + i, 3).Value
If 0 <= Total(i) < 100 Then
Sheet1.Cells(1 + i, 4).Value = "A"
ElseIf 100 <= Total(i) < 200 Then
Sheet1.Cells(1 + i, 4).Value = "B"
ElseIf 200 <= Total(i) < 300 Then
Sheet1.Cells(1 + i, 4).Value = "C"
ElseIf 300 <= Total(i) Then
Sheet1.Cells(1 + i, 4).Value = "D"
End If
Next i
Close #1
End Sub
Problem is with your If condition. In VBA 1 < 2 < 1 evaluates to true. That's why even if your total(i) is more than 100, it always evaluates to true and your elseif is not coming into play.
In VBA/VB6, type conversion is simply evil.
You nee to rewrite your If and elseif conditions
Example:
Sub test()
Dim x As Long
Dim y As Long
x = 101
y = 99
'/ What you are doing
If 0 <= x < 1 Then
MsgBox "This is not python."
End If
'/ How you should do it.
If y >= 0 And y < 100 Then
MsgBox "This is how you do it in VBA."
End If
End Sub

is it possbile to create an collection of arrays in vba?

first of all, i'd like to say, i've sarched thorugh the net, but i haven't run into such a thing. i've seen collection of collections, or array of arrays, but not a collection of array.
what i want to do is, to collect ID's in collections for each District. Finally, i will join the values in the collections with Join function and ";" as delimiter, and then print them in a range of 4 column as a lookup list, for each class. For example;
Class2(0) will include 54020 and 30734, class2(1) will include 58618, class1(4) will include none, class3(7) will include 35516,34781 and 56874, and so on.
i want to loop through column C and put a select case statment to check the class and then assign the values to collections
Sub dict_coll()
Dim class1() As New Collection
Dim class2() As New Collection
Dim class3() As New Collection
Dim class4() As New Collection
Dim dict As New Scripting.Dictionary
Set dRange = range(range("a2"), range("a2").End(xlDown))
i = 0
For Each d In dRange
If Not dict.Exists(d.Value) Then
dict.Add key:=d.Value, item:=i
i = i + 1
End If
Next d
Set cRange = range(range("c2"), range("c2").End(xlDown))
For Each c In cRange
Select Case c.Value
Case "class1"
class1(dict(c.offset(0,-2).value)).Add c.Offset(0, 1).Value 'fails here
Case "class2"
class2(dict(c.offset(0,-2).value)).Add c.Offset(0, 1).Value 'fails here
Case "class3"
class3(dict(c.offset(0,-2).value)).Add c.Offset(0, 1).Value 'fails here
Case Else
class4(dict(c.offset(0,-2).value)).Add c.Offset(0, 1).Value 'fails here
End Select
Next c
End Sub
and what i want to see is as foloowing:
is there any easier and proper way of what i wanna do? any help wil be appreciated.
thanks
I didnt see that sb variable defined in your code.
Anyway, for me I see a case of straightforward arrays: There is fixed dimension of classes so it good enough for me. Furthermore, you can print back to worksheet so easily.
Public Sub test()
Const strPrefix = "class"
Dim districtRange As Range, outputRange As Range, r As Range
Dim arr() As String
Dim i As Long, j As Long, x As Long, y As Long
Dim district As String, str As String, idVal As String
Dim arr2 As Variant
Application.ScreenUpdating = False
ReDim arr(1 To 5, 1 To 1)
arr(1, 1) = "District"
arr(2, 1) = "Class 1"
arr(3, 1) = "Class 2"
arr(4, 1) = "Class 3"
arr(5, 1) = "Class 4"
Set districtRange = Range(Range("A2"), Range("C2").End(xlDown))
arr2 = districtRange.Value
For x = LBound(arr2, 1) To UBound(arr2, 1)
district = arr2(x, 1)
i = Val(Mid(arr2(x, 3), Len(strPrefix) + 1))
idVal = arr2(x, 2)
j = inArray(arr, district, 1) 'returns -1 if not found
If j >= 0 Then
arr(i + 1, j) = IIf(arr(i + 1, j) = "", idVal, arr(i + 1, j) & ";" & idVal)
Else
ReDim Preserve arr(1 To 5, 1 To UBound(arr, 2) + 1)
arr(1, UBound(arr, 2)) = district
arr(i + 1, UBound(arr, 2)) = idVal
End If
Next x
Set outputRange = Range("E1")
outputRange.Resize(UBound(arr, 2), UBound(arr, 1)).Value = Application.Transpose(arr)
outputRange.Sort Key1:=Range("E1"), Header:=xlYes, Order1:=xlAscending
Application.ScreenUpdating = True
End Sub
Public Function inArray(arr As Variant, k As String, Optional rowNum As Long, Optional colNum As Long) As Long
Dim i As Long, j As Long
inArray = -1
If rowNum Then
For i = LBound(arr, 2) To UBound(arr, 2)
If arr(rowNum, i) = k Then
inArray = i
Exit Function
End If
Next i
Else
For i = LBound(arr, 1) To UBound(arr, 1)
If arr(i, colNum) = k Then
inArray = i
Exit Function
End If
Next i
End If
End Function
by the way, i've found another solution, usinf both dictionary and 3-dimension array.
Sub test()
Dim Blg As New Scripting.Dictionary
Dim Sgm As New Scripting.Dictionary
Dim Siciller() As String
ReDim Siciller(0 To 23, 0 To 3, 0 To 5)
Set alanBolge = range(range("a2"), range("a2").End(xlDown))
Set alanSegment = range(range("c2"), range("c2").End(xlDown))
i = 0
For Each d In alanBolge
If Not Blg.Exists(d.Value) Then
Blg.Add Key:=d.Value, item:=i
i = i + 1
End If
Next d
k = 0
For Each d In alanSegment
If Not Sgm.Exists(d.Value) Then
Sgm.Add Key:=d.Value, item:=k
k = k + 1
End If
Next d
'data reading
For Each d In alanBolge
Siciller(Blg(d.Value), Sgm(d.Offset(0, 2).Value), dolusay(Siciller, Blg(d.Value), Sgm(d.Offset(0, 2).Value)) + 1) = d.Offset(0, 1).Value
Next d
'output
For x = 1 To 4
For y = 1 To 24
Set h = Cells(1 + y, 5 + x)
h.Select
h.Value = sonucgetir(Siciller, Blg(h.Offset(0, -x).Value), Sgm(h.Offset(-y, 0).Value))
Next y
Next x
End Sub
Public Function dolusay(ByVal data As Variant, ByVal i1 As Integer, ByVal i2 As Integer) As Integer
Dim count As Integer
count = 0
For j = 0 To UBound(data, 3) - 1
If Len(data(i1, i2, j)) > 0 Then
count = count + 1
End If
Next
dolusay = count
End Function
Public Function sonucgetir(ByVal data As Variant, ByVal i1 As Integer, ByVal i2 As Integer) As String
sonucgetir = ""
For i = 0 To UBound(data, 3)
If Len(data(i1, i2, i)) > 0 Then
x = data(i1, i2, i) & ";" & x
sonucgetir = Left(x, Len(x) - 1)
End If
Next i
End Function

Resources