Parallel for loops in VBA - vba

this is my first time using VBA. My goal is to create a column of values that are the result of evaluating a function. Here is a simplified version of how I'm structuring my code.
Sub find_alpha()
Dim StartNumber As Integer
Dim EndNumber As Integer
Dim i As Integer
Dim alpha As Integer
EndNumber = 39
For alpha = 0 To 10
For StartNumber = 1 To EndNumber
For i = 0 To 38
Cells(StartNumber, "A").Value = Cells.Item(1, "B") * i * (1 - alpha)
Next i
Next StartNumber
Next alpha
End Sub
This doesn't work because it loops from 0 to 38 in only one cell, while I need it to actually move through and add the value to each cell. So the value at i=0, should go to A1, i=2 should go to A2, i=3 should go to A3, etc.
Is there a way to move through the loop in parallel to accomplish this?
Thank you!!

your question/aim isn't very clear so here are some possible blind shots:
place from cell "A1" down to a predefined rows number (EndNumber) the result of multiplying cell "B1" value by a number from 0 to (currentRowIndex- 1) * (1 - alpha), where currentRowIndex is the row index of the current cell in column "A" being written and alpha varies from 0 to 10 (...)
then you'd go:
Option Explicit
Sub find_alpha1()
Dim StartNumber As Long
Dim EndNumber As Long
Dim i As Long
Dim alpha As Long
StartNumber = 1
EndNumber = 39
For alpha = 0 To 10
For i = StartNumber To EndNumber
Cells(i, "A").value = Cells.item(1, "B") * (i - 1) * (1 - alpha)
Next i
Next alpha
End Sub
the same code could be rewritten as follows:
Option Explicit
Sub find_alpha2()
Dim EndNumber As Long
Dim alpha As Long
EndNumber = 39
For alpha = 0 To 10
With Range("A1").Resize(EndNumber)
.FormulaR1C1 = "=R1C2*(ROW()-1)*(1-" & alpha & ")"
.Value=.Value '<--| get rid of the formula and leave values only
End With
Next alpha
End Sub
but there would be the issue that those column "A" cells would be rewritten at every For alpha = 0 To 10 loop ... (see below)
place in columns starting from "A" rightwards from row 1 to a predefined rows number (EndNumber) the results of multiplying cell "B1" value by a number from 0 to (currentRowIndex- 1) * (1 - alpha), where currentRowIndex is the row index of the current cell in current column being written and with columns shifting rightwards at every alpha loop from 0 to 10
for such a task you actually need to write cells from row 2 downwards in order not to overwrite cell "B1"
then you'd go:
Option Explicit
Sub find_alpha3()
Dim EndNumber As Long
Dim alpha As Long
EndNumber = 39
For alpha = 0 To 10
With Range("A2").Offset(, alpha).Resize(EndNumber)
.FormulaR1C1 = "=R1C2*(ROW()-2)*(1-" & alpha & ")"
.value = .value '<--| get rid of the formula and leave values only
End With
Next alpha
End Sub
If not 1 or 2 then what?

Related

VBA - Breaking a cell string into individual cells based on length

I have a cell with a string of different lengths. I want split them into individual cells with a length of, say, 3 characters.
A cell with ABCCBA should end up ABC CBA in 2 different cells.
While a cell with ABCDABCDAB should end up ABC DAB CDA B in 4 different cells.
Is there any convenient way to do this?
I was looking at
' Finding number of cells
Segments = WorksheetFunction.RoundUp(Len(Range("A1").Value) / 3, 0)
' Split base on character length
For n = 1 to Segments
Cells(2, n) = Range("A1").Characters(n, 3)
Next n
But it doesn't seem to work.
A simple macro to split the string in to 3 lettered strings and write into columns next to the data range
Sub Split()
Dim Checkcol As Integer
Dim currentRowValue As String
Dim rowCount As Integer
Dim splitval As Integer
Dim i As Integer, j As Integer
Checkcol = 1 'Denotes A column
rowCount = Cells(Rows.Count, Checkcol).End(xlUp).Row
For currentRow = 1 To rowCount
currentRowValue = Cells(currentRow, Checkcol).Value
splitval = Int(Len(currentRowValue) / 3) + 1 'Find the number of 3 letter strings
j = 0
For i = 1 To splitval 'Loop through each value and write in next columns
j = (i - 1) * 3 + 1
Cells(currentRow, Checkcol + i).Value = Mid(currentRowValue, j, 3)
Next
Next
End Sub
If you are comfortable with formulas, assuming your data is in Cell A2, and you want to implement the formula in Cell B2 and rightwards.
Formula in B2:
=MID($A2,(COLUMNS($B$2:B2)-1)*3+1,3)
Copy it down and across as much you need.

unable to make the formula dynamic based on column value using loops in macro

This question is related to automating a formula for dynamic range using Macro in excel.
I have 2 columns "A" and "B".
Column A with Application IDs. Application ID in column "A" change dynamically after some iterations i.e. have 18 count, then 43, then 15 and so on...
Column B has corresponding 0s and 1s.
I'm calculating a binomial distribution formula based on the count values using 18 B values, then 43 B values, then 15 B values and so on.
If the Application ID in two rows doesn't match then a formula should be calculated. The formula contains 2-19 row values, than 20-62 row values, than 63-77 row values and so on...
I want it to calculate for 109972 cells. Looking for a macro which can do this.
First formula:
=IF(A19<>A20,BINOM.DIST(COUNTIF($B$2:B19,0),COUNT($B$2:B19),COUNTIF($B$2:B19,0)/COUNT($B$2:B19),FALSE),"")
Second Formula:
=IF(A62<>A63,BINOM.DIST(COUNTIF($B$20:B62,0),COUNT($B$20:B62),COUNTIF($B$20:B62,0)/COUNT($B$20:B62),FALSE),"")
Third Formula (and so on has to calculated)
=IF(A77<>A78,BINOM.DIST(COUNTIF($B$63:B77,0),COUNT($B$63:B77),COUNTIF($B$63:B77,0)/COUNT($B$63:B77),FALSE),"")
If your data is in a sheet named Data, add a command button and then following code. You should check the binomial parameters, cause I'm not used to them.
Private Sub CommandButton1_Click()
Dim lTrialNumber As Long
Dim lFailNumber As Long
Dim lLastRow As Long
Dim i As Long
lLastRow = Worksheets("Data").Cells(1, 1).End(xlDown).Row
lTrialNumber = 0
lFailNumber = 0
For i = 2 To lLastRow 'if data start in row 2. Row 1 for Titles
If Worksheets("Data").Cells(i + 1, 1) <> Worksheets("Data").Cells(i, 1) Then
lTrialNumber = lTrialNumber + 1
If Worksheets("Data").Cells(i, 2) = 0 Then
lFailNumber = lFailNumber + 1
End If
Worksheets("Data").Cells(i, 4) = WorksheetFunction.BinomDist(lFailNumber, lTrialNumber, lFailNumber / lTrialNumber, False)
lTrialNumber = 0
lFailNumber = 0
Else
lTrialNumber = lTrialNumber + 1
If Worksheets("Data").Cells(i, 2) = 0 Then
lFailNumber = lFailNumber + 1
End If
End If
Next
End Sub

Go through Cells and Round to Closest 5 VBA

I have a spreadsheet with 50K values on it.
I want it a code to go through every value and check to see if it ends in a 5 or 0 and if it doesn't not to round to the nearest of the two.
I tried this as my code
Sub Round_flow()
Dim nxtRow As Long, found As Boolean, i As Long, minus As Long, plus As Long, equal As Long, cell As Boolean, f As Integer
nxtRow = 2
found = False
i = Sheet1.Cells(nxtRow, 2)
minus = -2
equal = 0
While Not found 'finds last used row
If (Cells(nxtRow, 2) = "") Then
found = True
Else
nxtRow = nxtRow + 1
End If
Wend
For f = 2 To i
For minus = -2 To 168 Step 5
If ActiveCell.Value <> equal Then
While Not cell
plus = minus + 4
equal = minus + 2
If minus <= ActiveCell.Value <= plus Then
Sheet1.Cells(i, 2).Value = equal
cell = True
End If
Wend
End If
Next minus
Next f
Essentially what I was trying to do is say here is the last row, i want to check every value from i to last filled row to see if it falls between any plus and minus value (+-2 of the nearest 5 or 0) then have whatever activecell.value be replaced by the 0 or 5 ending digit 'equal' which changes with each iteration.
Ok, that seems way too complicated. To round to 5, you just multiply by 2, round, then divide by 2. Something like this will do the trick:
Dim NumberToBeRounded as Integer
Round(NumberToBeRounded *2/10,0)/2*10
*2 and /2 to get it to be rounded to 5, and /10 *10 to make the round function round for less than 0 decimals.
(I have to admit, I don't really understand what your code is trying to do, I hope I didn't completely misunderstand your needs.)
This should do the trick:
Sub Round_flow()
For f = 2 To Cells(1, 2).End(xlDown).Row
Cells(f, 2).Value = Round(Cells(f, 2).Value * 2 / 10) / 2 * 10
Next
End Sub
Cells(1, 2).End(xlDown).Row finds the last used cell, unless you have no data; if that can happen, add some code to check if you have at least 2 rows. Or you can use the Usedrange and SpecialCells(xlLastCell) combo to find the last used row of your table...
Another way:
Sub RoundEm()
Dim wks As Worksheet
Dim r As Range
Dim cell As Range
Set wks = ActiveSheet ' or any other sheet
On Error Resume Next
Set r = wks.Cells.SpecialCells(xlCellTypeConstants, xlNumbers)
On Error GoTo 0
If Not r Is Nothing Then
For Each cell In r
cell.Value2 = Round(cell.Value2 / 5, 0) * 5
Next cell
End If
End Sub

Excel VBA Loop on columns

when we are going to do a loop in the rows, we can use code like the following:
i = 1
Do
Range("E" & i & ":D" & i).Select
i = i + 1
Loop Until i > 10
but what if we want to do a loop on a column?
Can we use the same method as above?
while the columns in Excel is a complex such as A, B, C, ..., Y, Z, AA, AB, AC, ..., etc.
problems will arise between loop from the "Z" to the "AA".
how we do looping alphabet column from "A" to "Z" and then continued into "AA", "AB" and so on
is there anything that can help?
Yes, let's use Select as an example
sample code: Columns("A").select
How to loop through Columns:
Method 1: (You can use index to replace the Excel Address)
For i = 1 to 100
Columns(i).Select
next i
Method 2: (Using the address)
For i = 1 To 100
Columns(Columns(i).Address).Select
Next i
EDIT:
Strip the Column for OP
columnString = Replace(Split(Columns(27).Address, ":")(0), "$", "")
e.g. you want to get the 27th Column --> AA, you can get it this way
Another method to try out.
Also select could be replaced when you set the initial column into a Range object. Performance wise it helps.
Dim rng as Range
Set rng = WorkSheets(1).Range("A1") '-- you may change the sheet name according to yours.
'-- here is your loop
i = 1
Do
'-- do something: e.g. show the address of the column that you are currently in
Msgbox rng.offset(0,i).Address
i = i + 1
Loop Until i > 10
** Two methods to get the column name using column number**
Split()
code
colName = Split(Range.Offset(0,i).Address, "$")(1)
String manipulation:
code
Function myColName(colNum as Long) as String
myColName = Left(Range(0, colNum).Address(False, False), _
1 - (colNum > 10))
End Function
If you want to stick with the same sort of loop then this will work:
Option Explicit
Sub selectColumns()
Dim topSelection As Integer
Dim endSelection As Integer
topSelection = 2
endSelection = 10
Dim columnSelected As Integer
columnSelected = 1
Do
With Excel.ThisWorkbook.ActiveSheet
.Range(.Cells(columnSelected, columnSelected), .Cells(endSelection, columnSelected)).Select
End With
columnSelected = columnSelected + 1
Loop Until columnSelected > 10
End Sub
EDIT
If in reality you just want to loop through every cell in an area of the spreadsheet then use something like this:
Sub loopThroughCells()
'=============
'this is the starting point
Dim rwMin As Integer
Dim colMin As Integer
rwMin = 2
colMin = 2
'=============
'=============
'this is the ending point
Dim rwMax As Integer
Dim colMax As Integer
rwMax = 10
colMax = 5
'=============
'=============
'iterator
Dim rwIndex As Integer
Dim colIndex As Integer
'=============
For rwIndex = rwMin To rwMax
For colIndex = colMin To colMax
Cells(rwIndex, colIndex).Select
Next colIndex
Next rwIndex
End Sub
Just use the Cells function and loop thru columns.
Cells(Row,Column)

Excel VBA optimisation for hidden rows

I have a macro that iterates through some rows, to update the colouring of data points in a related chart. The rows can be hidden by the user, so it checks the hidden value, i.e.
Do While wsGraph.Cells(RowCounter, 1) <> ""
If wsGraph.Rows(RowCounter).Hidden = False Then
'code here
End If
RowCounter = RowCounter + 1
Loop
This code takes 69 seconds to run. If I take the test for the hidden row out, it takes 1 second to run.
Is there a better way to do this test, otherwise I will have to tell the users they can't use the hide function (or deal with a 69 second delay).
Thanks
Here's the full code, as requested.
The graph is a bar graph, and I colour the points based on the values being in certain ranges, eg: over 75% = green, over 50% = yellow, over 25% = orange, else red. There's a button on the form to recolour the graph, that executes this code.
If someone filters the data table, what's happening is this: say the first 20 rows were over 75%, and were initially coloured green. After filtering the table, say only the first 5 are over 75%. The graph still shows the first 20 as green. So this button with the macro recolours the bars.
' --- set the colour of the items
Dim iPoint As Long
Dim RowCounter As Integer, iPointCounter As Integer
Dim wsGraph As Excel.Worksheet
Set wsGraph = ThisWorkbook.Worksheets(cGraph5)
wsGraph.ChartObjects("Chart 1").Activate
' for each point in the series...
For iPoint = 1 To UBound(wsGraph.ChartObjects("Chart 1").Chart.SeriesCollection(1).Values)
RowCounter = 26
iPointCounter = 0
' loop through the rows in the table
Do While wsGraph.Cells(RowCounter, 1) <> ""
' if it's a visible row, add it to the counter, if it's the same counter as in the series, exit do
If wsGraph.Rows(RowCounter).Hidden = False Then
iPointCounter = iPointCounter + 1
If iPointCounter = iPoint Then Exit Do
End If
RowCounter = RowCounter + 1
Loop
' colour the point from the matched row in the data table
Dim ColorIndex As Integer
If wsGraph.Cells(RowCounter, 5) >= 0.75 Then
ColorIndex = ScoreGreen
ElseIf wsGraph.Cells(RowCounter, 5) >= 0.5 Then
ColorIndex = ScoreYellow
ElseIf wsGraph.Cells(RowCounter, 5) >= 0.25 Then
ColorIndex = ScoreOrange
ElseIf wsGraph.Cells(RowCounter, 5) >= 0 Then
ColorIndex = ScoreRed
Else
ColorIndex = 1
End If
ActiveChart.SeriesCollection(1).Points(iPoint).Interior.ColorIndex = ColorIndex
Next
Try Special Cells
Sub LoopOverVisibleCells()
Dim r As Range
Dim a As Range
dim cl As Range
Set r = ActiveSheet.UsedRange.Columns(1).SpecialCells(xlCellTypeVisible)
For Each a In r.Areas
For Each cl In a
' code here
Next
Next
End Sub
This is what I've done, using Chris's suggestion. It doesn't answer why the hidden check is so slow, but it's a more efficient way of doing the recolouring:
Dim myrange As range
Set myrange = wsGraph.range("E26:E304").SpecialCells(xlCellTypeVisible)
Dim i As Integer
For i = 1 To myrange.Rows.Count
If myrange.Cells(i, 1) >= 0.75 Then
ColorIndex = ScoreGreen
ElseIf myrange.Cells(i, 1) >= 0.5 Then
ColorIndex = ScoreYellow
ElseIf myrange.Cells(i, 1) >= 0.25 Then
ColorIndex = ScoreOrange
ElseIf myrange.Cells(i, 1) >= 0 Then
ColorIndex = ScoreRed
Else
ColorIndex = 1
End If
ActiveChart.SeriesCollection(1).Points(i).Interior.ColorIndex = ColorIndex
Next i