I have two worksheets (Variáveis and Indicadores). In Variáveis there are 14 columns (col A-Codes, col B-Description, cols C:N-months from January to December) and in Indicadores there are 17 columns (col A-ID, col B-Description, col C-Formula, col D-Measurement Unit, col E- Info, cols F:Q- months from January to December).
Variáveis contains the int monthly values for each code.
Indicadores has a text formula (column C) composed by the codes stored in Variáveis (e.g. (dAA11b+dAA12b)/dAA13b*100) that is converted (with the code below that was developed with the help of #tigeravatar) into a excel formula by replacing the codes for his correspondent int monthly value (e.g. (742+764)/125*100).
Portion of Sheet Variáveis
Portion of Sheet Indicadores with formula already converted and result in the first month
Note: this is just an example and values vary because I used the =RANDBETWEEN() formula to populate the worksheet with int values
Dim wb As Workbook
Dim wsLookup As Worksheet
Dim wsData As Worksheet
Dim aLookup() As Variant
Dim aData() As Variant
Dim lCodesLookupCol As Long
Dim lCodesConvertCol As Long
Dim i As Long
Set wb = ActiveWorkbook
Set wsData = wb.Worksheets("Indicadores")
Set wsLookup = wb.Worksheets("Variáveis")
'This line loads the codes we need to lookup and their associated Int Value into an array
aLookup = wsLookup.Range("A2:C1309")
lCodesLookupCol = LBound(aLookup, 2)
lCodesConvertCol = UBound(aLookup, 2)
'This is the range containing the Formulas stored as text in data worksheet
With wsData.Range("C2", wsData.Cells(wsData.Rows.Count, "C").End(xlUp))
'Loop through the lookup array and convert all codes into their associated Int Values using the Range.Replace method
For i = 1 To UBound(aLookup, 1)
.Replace aLookup(i, lCodesLookupCol), aLookup(i, lCodesConvertCol), xlPart, , False
Next i
'Now all of the Codes should have been replaced with their associated Int Values, but the formulas are still just text
'This block will load the formulas as text into an array
If .Cells.Count = 1 Then
ReDim aData(1 To 1, 1 To 1)
aData(1, 1) = .Formula
Else
aData = .Formula
End If
'Loop through the aData array to prepend an = sign to convert them into formulas
For i = 1 To UBound(aData, 1)
'Verifies if the cell isn't blank and that it's not already a formula
If Len(aData(i, 1)) > 0 And Left(aData(i, 1), 1) <> "=" Then aData(i, 1) = "=" & aData(i, 1)
wsData.Cells(i + 1, 6) = aData(i, 1)
If Left(aData(i, 1), 1) <> "=" Then
MsgBox "Error # row " & Str(i + 1)
End If
'On Error Resume Next
Next i
End With
End Sub
Right now the code converts the text formulas in Indicadores into excel formulas by replacing the codes with their values (stored in Variáveis) and placing the result in the column desired (in this case it was col F- Jan)
At the moment the macro can only do the job for one month at a time. What I'm trying to do now is to do the same thing but for all months simultaneously. All help is appreciated, thank you.
It's a little obfuscated and over-engineered, but I think this is the line that actually writes your values to the output sheet:
wsData.Cells(i + 1, 6) = aData(i, 1)
In which case the number "6" is the column being written to, which makes sense since that is the "January" column in your output sheet.
What I would do is enclose the entire sub in a For Loop that runs 12 times (once for each month), and have it increment that number on each loop. Something like this:
Sub Looper()
For x = 6 To 18
LookupMachine x
Next x
End Sub
Sub LookupMachine(MonthNum As Integer)
Dim wb As Workbook
Dim wsLookup As Worksheet
Dim wsData As Worksheet
Dim aLookup() As Variant
Dim aData() As Variant
Dim lCodesLookupCol As Long
Dim lCodesConvertCol As Long
Dim i As Long
Set wb = ActiveWorkbook
Set wsData = wb.Worksheets("Indicadores")
Set wsLookup = wb.Worksheets("Variáveis")
'This line loads the codes we need to lookup and their associated Int Value into an array
aLookup = wsLookup.Range("A2:N1309")
lCodesLookupCol = LBound(aLookup, MonthNum - 4)
lCodesConvertCol = UBound(aLookup, MonthNum - 4)
'This is the range containing the Formulas stored as text in data worksheet
With wsData.Range("C2", wsData.Cells(wsData.Rows.Count, "C").End(xlUp))
'Loop through the lookup array and convert all codes into their associated Int Values using the Range.Replace method
For i = 1 To UBound(aLookup, 1)
.Replace aLookup(i, lCodesLookupCol), aLookup(i, lCodesConvertCol), xlPart, , False
Next i
'Now all of the Codes should have been replaced with their associated Int Values, but the formulas are still just text
'This block will load the formulas as text into an array
If .Cells.Count = 1 Then
ReDim aData(1 To 1, 1 To 1)
aData(1, 1) = .Formula
Else
aData = .Formula
End If
'Loop through the aData array to prepend an = sign to convert them into formulas
For i = 1 To UBound(aData, 1)
'Verifies if the cell isn't blank and that it's not already a formula
If Len(aData(i, 1)) > 0 And Left(aData(i, 1), 1) <> "=" Then aData(i, 1) = "=" & aData(i, 1)
wsData.Cells(i + 1, MonthNum) = aData(i, 1)
If Left(aData(i, 1), 1) <> "=" Then
MsgBox "Error # row " & Str(i + 1)
End If
'On Error Resume Next
Next i
End With
End Sub
Note that I extended the aLookup array to cover up to column N, and changed the column number in the Array lookup from a hardcoded "2", to "MonthNum - 4", which should increment the column of data it's working with in the way that you want.
Related
I cobbled together something that does work for me as is, but it runs very slowly and I'm sure the code can be simplified.
Sub CopyPasteValues()
Dim strSht1, strSht2 As String
Dim c, rng As Range
strSht1 = "Edit"
strSht2 = "LOB"
With ThisWorkbook.Sheets(strSht1)
Set rng = Range("J2:AJ37")
For Each c In rng
If Not c.Value = 0 Then
Cells(c.Row, 2).Copy
ThisWorkbook.Sheets(strSht2).Range("B" & Rows.Count).End(xlUp).Offset(1).PasteSpecial xlPasteValues
Range(Cells(c.Row, 4), Cells(c.Row, 5)).Copy
ThisWorkbook.Sheets(strSht2).Range("C" & Rows.Count).End(xlUp).Offset(1).PasteSpecial xlPasteValues
c.Copy
ThisWorkbook.Sheets(strSht2).Range("F" & Rows.Count).End(xlUp).Offset(1).PasteSpecial xlPasteValues
Cells(c.Column).Copy
ThisWorkbook.Sheets(strSht2).Range("G" & Rows.Count).End(xlUp).Offset(1).PasteSpecial xlPasteValues
End If
Next c
End With
End Sub
I appreciate any assistance.
As BigBen Mentioned, array method.
Super Fast.
Sub Move_Values_Array_Method()
Dim SourceSheet As Worksheet 'Source Worksheet
Dim DestinationSheet As Worksheet 'Destination Worksheet
Dim RG As Range 'Source Range
Dim InArr() 'Data In Array
Dim OutArr() 'Data Out Array
Dim X As Long 'Array X Position for purposes of iterating through array.
Dim Y As Long 'Array Y Position for purposes of iterating through array.
Dim Cnt As Long 'Found Value Count
Set SourceSheet = ThisWorkbook.Worksheets("Edit") 'Set Source Worksheet
Set DestinationSheet = ThisWorkbook.Worksheets("LOB") 'Set Dest Worksheet
Set RG = SourceSheet.Range("J2:AJ37") 'Set Source Range
ReDim OutArr(1 To RG.Cells.Count) 'Count Cells in Range, resize output array to be at least that big.
InArr = RG 'Transfer Range Data to Array
Cnt = 0
Debug.Print LBound(InArr, 1) & " - " & UBound(InArr, 1) 'Rows
Debug.Print LBound(InArr, 2) & " - " & UBound(InArr, 2) 'Columns
For Y = 1 To UBound(InArr, 1) 'For Each Row in Array (or each Y position)
For X = 1 To UBound(InArr, 2) 'For Each Column in Array (or each X position)
If InArr(Y, X) <> "" Then 'If not blank Value (you can change this to "If InArr(Y, X) <> 0 Then" if that works best for you.
Cnt = Cnt + 1 'Increment "found value count" by 1
OutArr(Cnt) = InArr(Y, X) 'Add found value to output array
End If
Next X
Next Y
'Output to Dest Sheet
DestinationSheet.Range("F2").Resize(UBound(OutArr, 1), 1).Value = Application.Transpose(OutArr())
End Sub
Based on the information in your previous comments, try these alternative solution using formulas and filters...
1) Array Formulas
To note:
I have put everything on one sheet for clarity, but it works just as well over multiple sheets, or even workbooks.
If you want to filter the entire sheet, with same column order, you only need to enter formula once and expand "Array" criteria in formula to encapsulate entire data set.
Formula used in cell "J4" = "=FILTER($I$4:$I$30,$C$4:$C$30>0)"
(filter range I4 to I30 to show rows where value in range C4 to C30 is greater than 0)
2) Directly Filter
Alternatively, you could (either manually or programmatically) copy all data to LOB sheet, (or selectively copy), then filter for Qty>0.
I got a sheet that contain weekly roster of each employee. The code below run perfectly to display unique data of one column:
Dim lastrow As Long
Application.ScreenUpdating = False
Dim rng, lastcell As Range
Set rng = Application.InputBox("Select a range", "Obtain Range Object", Type:=8)
lastrow = Cells(Rows.Count, "B").End(xlUp).Row
ActiveSheet.Range(rng.Address & lastrow).AdvancedFilter _
Action:=xlFilterCopy, _
CopyToRange:=ActiveSheet.Range(rng.Cells(rng.Rows.Count + 1, rng.Columns.Count).Address), _
Unique:=True
Application.ScreenUpdating = True
But my issue is that I want the code to exclude some text like OFF
and LEAVE. The only data to display is their shift which is in the format, 0430_1145 for timein_timeout in an asecending way.
The data normally is displayed at the end of each column:
If column have data such as:
0700_1500
0430_1145
leave
off
0700_1500
0830_1615
result would be(ascending way ignoring off and leave)-
0430_1145
0700_1500
0830_1615
Below is the link of my excel sheet:
https://drive.google.com/file/d/1CYGS9ZgsulG8J_qzYEUXWFiXkBHneibv/edit
If you have O365 with the appropriate functions, you can do this with a worksheet formula:
=SORT(UNIQUE(FILTER(A1:A6,(A1:A6<>"off")*(A1:A6<>"leave"))))
In the below image, the formula is entered into cell A8
Edit: Here is a VBA routine based on the worksheet you uploaded.
The result of the extraction of each column is stored as an ArrayList in a Dictionary.
I used an ArrayList because it is easy to sort -- but you could use any of a number of different objects to store this information, and write a separate sorting routine.
I also used late-binding for the dictionary and arraylist objects, but could switch that to early-binding if you have huge amounts of data to process and need the increased speed.
Note that the data is processed from a VBA array rather than on the worksheet.
many modifications are possible depending on your needs, but this should get you started.
Option Explicit
Sub summarizeShifts()
Dim wsSrc As Worksheet 'data sheet
Dim vSrc As Variant, vRes As Variant 'variant arrays for original data and results
Dim rRes As Range 'destination for results
Dim dShifts As Object ' store shifts for each day
Dim AL As Object 'store in AL to be able to sort
Dim I As Long, J As Long, S As String, V As Variant, W As Variant
'read source data into array
Set wsSrc = Worksheets("fnd_gfm_1292249")
With wsSrc
vSrc = .Range(.Cells(1, 1), .Cells(.Rows.Count, 1).End(xlUp)).Resize(columnsize:=9)
Set rRes = .Cells(UBound(vSrc, 1) + 1, 3) 'bottom of source data
End With
Set dShifts = CreateObject("Scripting.Dictionary")
'Populate the dictionary by columns
For J = 3 To UBound(vSrc, 2)
Set AL = CreateObject("System.Collections.ArrayList")
For I = 2 To UBound(vSrc, 1)
S = vSrc(I, J)
If S Like "####_####" Then
If Not AL.contains(S) Then AL.Add S
End If
Next I
AL.Sort
dShifts.Add J, AL
Next J
'size vres
I = 0
For Each V In dShifts
J = dShifts(V).Count
I = IIf(I > J, I, J)
Next V
ReDim vRes(1 To I, 1 To UBound(vSrc) - 2)
'populate results array
For Each V In dShifts
I = 0
For Each W In dShifts(V)
I = I + 1
vRes(I, V - 2) = W
Next W
Next V
'write the results
Application.ScreenUpdating = False
Set rRes = rRes.Resize(UBound(vRes, 1), UBound(vRes, 2))
With rRes
.Resize(rowsize:=rRes.Rows.Count * 3).ClearContents 'or something to clear rows below the data
.Value = vRes
End With
End Sub
Approach via FilterXML()
In addition to the valid solutions above I demonstrate an alternative solution via FilterXML() available since vers. 2013+:
Sub ExtractUniques20201019()
'a) define Worksheet
Dim ws As Worksheet: Set ws = Sheet1 ' << change to project's sheet Code(Name)
'b) get first target Row (2 rows below original data)
Dim tgtRow As Long: tgtRow = UBound(getData(ws, "A", 1)) + 2
Dim i As Long
For i = 3 To 9 ' columns C:I (Monday to Sunday)
'[1] get data
Dim data: data = getData(ws, i) ' << function call getData()
'[2] get valid unique data
Dim uniques: uniques = getFilterUniques(data) ' << function call getFilterUniques()
BubbleSortColumnArray uniques ' << call procedure BubbleSortColumnArray
'[3] write results to target below data range
ws.Range("A" & tgtRow).Offset(columnoffset:=i - 1).Resize(UBound(uniques), 1) = uniques
Next i
End Sub
Help functions
Function getData(ws As Worksheet, ByVal col, Optional ByVal StartRow& = 2) As Variant()
' Purpose: assign column data to variant array
If IsNumeric(col) Then col = Split(ws.Cells(1, col).Address, "$")(1)
Dim lastRow As Long
lastRow = ws.Range(col & Rows.Count).End(xlUp).Row
getData = ws.Range(col & StartRow & ":" & col & lastRow).Value2
End Function
Function getFilterUniques(arr, Optional Fltr As String = "_")
'Purpose: get unique items containing e.g. Fltr "_" using XPath search
'Note: WorksheetFunction.FilterXML() is available since vers. 2013+
' XPath examples c.f. https://stackoverflow.com/questions/61837696/excel-extract-substrings-from-string-using-filterxml/61837697#61837697
Dim content As String ' well formed xml content string
content = "<t><s>" & Join(Application.Transpose(arr), "</s><s>") & "</s></t>"
getFilterUniques = WorksheetFunction.FilterXML(content, "//s[not(preceding::*=.)][contains(., '" & Fltr & "')]")
End Function
Bubblesort
Sub BubbleSortColumnArray(arr, Optional ByVal ColNo As Long = 1)
'Purpose: sort 1-based 2-dim datafield array
'correct differing column index
Dim colIdx As Long: colIdx = LBound(arr) + ColNo - 1
'bubble sort
Dim cnt As Long, nxt As Long, temp
For cnt = LBound(arr) To UBound(arr) - 1
For nxt = cnt + 1 To UBound(arr)
If arr(cnt, colIdx) > arr(nxt, colIdx) Then
temp = arr(cnt, colIdx) ' remember element
arr(cnt, colIdx) = arr(nxt, colIdx) ' swap
arr(nxt, colIdx) = temp
End If
Next nxt
Next cnt
End Sub
Consider using the one argument of AdvancedFilter you do not use: CriteriaRange. This can allow you to set up a multiple set criteria that leaves out those values. See Microsoft's Filter by using advanced criteria tutorial doc section: Multiple sets of criteria, one column in all sets.
Essentially, this involves adding a new region outside of data region somewhere in worksheet or workbook with column headers and needed criteria which would be <>LEAVE AND <>OFF which as link above shows would require two same named columns for AND logic.
Criteria Region
A B C D E F G H I J K L M N
1 Monday Monday Tuesday Tuesday Wednesday Wednesday Thursday Thursday Friday Friday Saturday Saturday Sunday Sunday
2 <>LEAVE <>OFF <>LEAVE <>OFF <>LEAVE <>OFF <>LEAVE <>OFF <>LEAVE <>OFF <>LEAVE <>OFF <>LEAVE <>OFF
VBA
Below defines worksheet objects and avoids the use of ActiveSheet. See Two main reasons why .Select, .Activate, Selection, Activecell, Activesheet, Activeworkbook, etc. should be avoided.
...
Set data_ws = ThisWorkbook.Worksheets("myCurrentWorksheet")
Set criteria_ws = ThisWorkbook.Worksheets("myCurrentWorksheet")
data_ws.Range(rng.Address & lastrow).AdvancedFilter _
Action:=xlFilterCopy, _
CriteriaRange:=criteria_ws.Range("A1:N2")
CopyToRange:=data_ws.Range(rng.Cells(rng.Rows.Count + 1, rng.Columns.Count).Address), _
Unique:=True
I made a macro that calculates some formulas, that are stored as text at first in one column, by replacing some codes by their associated int values and later print the result in the desired column.
e.g. dAGR99001/dAGR99002 is replaced by 2/2 since their values for certain month/year are both 2, later this text formula converted into a proper formula by adding the equal sign at the beginning and prints the result of it in another column.
My problem is that when replacing the codes by their associated int values, Excel automatically converts it to a date. For example on the above formula, it should be replaced by 2/2 but instead, it's returning 2/Feb (2/Fev in Portuguese) as in 2/2/2019 and when later calculating it the final result is 43498 (days since 1/1/1900).
How can I prevent this from happening?
→
Note that both the column where the text formula is and the associated int values of the codes are stored has General format. I also tried to save them as Number or Text but the problem still persisted.
This is my code
Sub Looper()
Dim x As Integer
For x = 10 To 60
getformulas x
Next x
End Sub
Sub getformulas(MonthNum As Integer)
Dim wb As Workbook
Dim wsLookup As Worksheet
Dim wsData As Worksheet
Dim wsPaste As Worksheet
Dim aLookup() As Variant
Dim aData() As Variant
Dim lCodesLookupCol As Long
Dim lCodesConvertCol As Long
Dim i As Long
Dim lrow As Long
Call OptimizeCode_Begin
With Sheets("Variaveis")
lrow = .Range("A:BA").Find(What:="", after:=.Range("A2"), searchdirection:=xlPrevious).Row
End With
Set wb = ActiveWorkbook
Set wsData = wb.Worksheets("Indicadores")
Set wsLookup = wb.Worksheets("Variaveis")
Set wsPaste = wb.Worksheets("Formulas")
aLookup = wsLookup.Range("A2:BA" & lrow) '("A2:AO441")
lCodesLookupCol = LBound(aLookup, 2)
lCodesConvertCol = UBound(aLookup, 2)
With wsData.Range("H2", wsData.Cells(wsData.Rows.Count, "H").End(xlUp))
For i = 1 To UBound(aLookup, 1)
.Replace aLookup(i, lCodesLookupCol), aLookup(i, MonthNum - 4), xlPart, , False
Next i
If .Cells.Count = 1 Then
ReDim aData(1 To 1, 1 To 1)
aData(1, 1) = .Formula
Else
aData = .Formula
End If
For i = 1 To UBound(aData, 1)
If Len(aData(i, 1)) > 0 And Left(aData(i, 1), 1) <> "=" Then aData(i, 1) = "=" & aData(i, 1)
wsData.Cells(i + 1, MonthNum) = aData(i, 1)
If Left(aData(i, 1), 1) <> "=" Then
wsData.Cells(i + 1, MonthNum).Value = "Error"
End If
On Error Resume Next
Next i
End If
Call OptimizeCode_End
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = True
End With
Worksheets("Formulas").Range("B2:B228").Copy
Worksheets("Indicadores").Range("H2:H228").PasteSpecial Paste:=xlPasteFormulas
Application.Goto Worksheets("Indicadores").Cells(2, 6)
End Sub
I developed a macro that loads some codes that I need to lookup and their associated int values into an array. After this, the macro will loop through the lookup array and convert all codes that compose a formula in one column (for now they are not stored as a formula but as a text in "Fórmula Calculo" column, e.g. dAGR99001/dAGR99002 with no = sign) into their associated int values using the Range.Replace method. Later the macro will load the formulas, which already have the codes with their associated int values, as text into an array and loop through the array to prepend an = sign to convert them into proper formulas and print the result in the desired columns.
Note that the results we can see here (2016) are returning the correct value (#DIV/0!) because the associated values for the codes in this year are 0's. The problem only occurs in 2019 for this formula.
The macro is working fine but some of the formulas are returning the wrong result in some situations.
e.g. in the above formula (dAGR99001/dAGR99002) both codes are associated with the following values for each month of 2019:
But instead of returning =2/2, for Jan/19, and the result of 1, it's returning =43498 and similar values for the following months of the year. It's weird because for the previous years the results are the expected.
I noticed that this error is only occurring when the formulas (divisions, sums, subtractions, etc.) deal with values below 10.
I did some research but haven't found any solution, did find some similar problems but not quite the same thing. I tried to change the values for a number format but that didn't work.
This is the vba code that I'm using:
Sub Looper()
Dim x As Integer
For x = 10 To 60
getformulas x
Next x
End Sub
Sub getformulas(MonthNum As Integer)
Dim wb As Workbook
Dim wsLookup As Worksheet
Dim wsData As Worksheet
Dim wsPaste As Worksheet
Dim aLookup() As Variant
Dim aData() As Variant
Dim lCodesLookupCol As Long
Dim lCodesConvertCol As Long
Dim i As Long
Dim lrow As Long
Call OptimizeCode_Begin
With Sheets("Variaveis")
lrow = .Range("A:BA").Find(What:="", after:=.Range("A2"), searchdirection:=xlPrevious).Row
End With
Set wb = ActiveWorkbook
Set wsData = wb.Worksheets("Indicadores")
Set wsLookup = wb.Worksheets("Variaveis")
Set wsPaste = wb.Worksheets("Formulas")
aLookup = wsLookup.Range("A2:BA" & lrow) '("A2:AO441")
lCodesLookupCol = LBound(aLookup, 2)
lCodesConvertCol = UBound(aLookup, 2)
With wsData.Range("H2", wsData.Cells(wsData.Rows.Count, "H").End(xlUp))
For i = 1 To UBound(aLookup, 1)
'.Replace aLookup(i, lCodesLookupCol), aLookup(i, lCodesConvertCol), xlPart, , False
.Replace aLookup(i, lCodesLookupCol), aLookup(i, MonthNum - 4), xlPart, , False
Next i
If .Cells.Count = 1 Then
ReDim aData(1 To 1, 1 To 1)
aData(1, 1) = .Formula
Else
aData = .Formula
End If
For i = 1 To UBound(aData, 1)
If Len(aData(i, 1)) > 0 And Left(aData(i, 1), 1) <> "=" Then aData(i, 1) = "=" & aData(i, 1)
wsData.Cells(i + 1, MonthNum) = aData(i, 1)
If Left(aData(i, 1), 1) <> "=" Then
wsData.Cells(i + 1, MonthNum).Value = "Error"
End If
On Error Resume Next
Next i
End If
Call OptimizeCode_End
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = True
End With
Worksheets("Formulas").Range("B2:B228").Copy
Worksheets("Indicadores").Range("H2:H228").PasteSpecial Paste:=xlPasteFormulas
Application.Goto Worksheets("Indicadores").Cells(2, 6)
End Sub
At a high level I am trying to set a cell equal to a random cell within a range. The issue I am having is that in this range I want to pull a random Value from, the Value I am taking is the result of an 'if' expression that either sets the cell to a Value or "". So when I chose the random value I only want to choose cells that have an actual value, not the "".
Does anyone know how to get this expected behavior?
The code below shows what I have tried currently, each large block is commented to help with understanding. The block I need help with replaces the values in each column until the next cell is blank then moves to the next column.
upperBound = 1798
lowerBound = 2
Randomize
'This loop section populates the data area with a static value in cell 9,3 then 9,4 etc..
For j = 3 To 15
val = Cells(9, j).Value
For i = 1 To val
Cells(12 + i, j).Value = Cells(9, j)
Next i
Next j
'This loop section uses the cells already populated down each column and replaces that value with the random value from the other range
Dim x As Integer
' Set numrows = number of rows of data.
For j = 3 To 15
NumRows = Range(Cells(13, j), Cells(13, j).End(xlDown)).Rows.Count
' Select cell 13,j.
Cells(13, j).Select
' Establish "For" loop to loop "numrows" number of times.
For x = 1 To NumRows
ActiveCell.Value = Worksheets("2017 Role IDs").Cells(Int((upperBound - lowerBound + 1) * Rnd + lowerBound), 2).Value
' Selects cell down 1 row from active cell.
ActiveCell.Offset(1, 0).Select
Next
Next j
This is the data before the second block runs. I want to replace the values that just match the number in the second row with the random number in the range:
This is what I would like to look like:
But currently it looks like this because the random selector is taking blank values:
Something like this should work for you:
Sub tgr()
Dim wb As Workbook
Dim wsNums As Worksheet
Dim wsDest As Worksheet
Dim aData As Variant
Dim vData As Variant
Dim aNums() As Double
Dim aResults() As Variant
Dim lNumCount As Long
Dim lMaxRows As Long
Dim lRowCount As Long
Dim ixNum As Long
Dim ixResult As Long
Dim ixCol As Long
Set wb = ActiveWorkbook
Set wsNums = wb.Worksheets("2017 Role IDs")
Set wsDest = wb.ActiveSheet
With wsNums.Range("B2", wsNums.Cells(wsNums.Rows.Count, "B").End(xlUp))
If .Row < 2 Then Exit Sub 'No data
lNumCount = WorksheetFunction.Count(.Cells)
If lNumCount = 0 Then Exit Sub 'No numbers
ReDim aNums(1 To lNumCount)
If .Cells.Count = 1 Then
ReDim aData(1 To 1, 1 To 1)
aData(1, 1) = .Value
Else
aData = .Value
End If
'Load populated numeric cells into the aNums array
For Each vData In aData
If Len(vData) > 0 And IsNumeric(vData) Then
ixNum = ixNum + 1
aNums(ixNum) = vData
End If
Next vData
End With
lMaxRows = Application.Max(wsDest.Range("C9:O9"))
If lMaxRows = 0 Then Exit Sub 'Row count not populated in row 9 for each column
ReDim aResults(1 To WorksheetFunction.Max(wsDest.Range("C9:O9")), 1 To 13)
'Populate each column accordingly and pull a random number from aNums
For ixCol = 1 To UBound(aResults, 2)
If IsNumeric(wsDest.Cells(9, ixCol + 2).Value) Then
For ixResult = 1 To CLng(wsDest.Cells(9, ixCol + 2).Value)
Randomize
aResults(ixResult, ixCol) = aNums(Int(Rnd() * lNumCount) + 1)
Next ixResult
End If
Next ixCol
wsDest.Range("C13").Resize(UBound(aResults, 1), UBound(aResults, 2)).Value = aResults
End Sub