Write array to the worksheet and repeat it n times - excel

I am working on the code where I want to write 2 arrays (assigned in 'Input sheet) to 'Output' sheet n times, i.e. specifically 2 times in the loop. I want to use arrays because the range of the ids and its names can change (it can be much more).
To start with a simple example (with a small amount of data), the arrays are assigned acc. to data in 'Input' sheet:
These 2 arrays should be written to 'Output' sheet n times i.e.; They should be written once and then again in the loop i.e. 2 times. I want to do it in the loop to give it the flexibility of writing in the future e.g. 3, 4, n times. In this example, I do it 2 times. Before each written array, there should be written a heading 'Title' and at the end of the written array should written text 'Total', therefore this is my desired outcome:
My code works only to write the 2 arrays for the first time but it does not write these 2 arrays for 2nd time. Instead, I am getting something else which is wrong:
This is my code:
Sub Write1()
Dim r As Long
Dim c As Long
Dim Start_Row As Long
Dim End_Row As Long
Dim main As Integer
Dim lngRowCount As Long
Dim w_Output As Worksheet
Dim w1 As Worksheet
Dim intLastRow As Integer
Const RowStart As Integer = 3
Const ColumnID As Integer = 1
Const Column_Desc As Integer = 3
Dim arrID() As Variant
Dim arrDesc() As Variant
With ThisWorkbook
Set w1 = .Sheets("Input")
Set w_Output = .Sheets("Output")
End With
'***********************************
'arrays
With w1
intLastRow = .Cells(Rows.Count, 1).End(xlUp).Row
'IntLastCol = .Cells(4, Columns.Count).End(xlToLeft).Column
arrID = .Range(.Cells(RowStart, ColumnID), .Cells(intLastRow, ColumnID))
arrDesc = .Range(.Cells(RowStart, Column_Desc), .Cells(intLastRow, Column_Desc))
'******************************************
main = 1
End_Row = 2 'this is the 2nd iteration to write arrays
For Start_Row = 1 To End_Row
w_Output.Cells(main, 3) = "Title"
main = main + 1
For r = 1 To UBound(arrID, 1)
If Len(arrID(r, 1)) > 0 Then
'Write
w_Output.Cells(r + 1, 3) = arrID(r, 1)
w_Output.Cells(r + 1, 4) = arrDesc(r, 1)
End If
main = main + 1
w_Output.Cells(main, 3) = "Total "
Next r
main = main + 4
Next Start_Row
End With
MsgBox "Done", vbInformation
End Sub
Does anybody know what I do wrong in my loop to make it work?

I have figured it out, it turns out the I was simply supposed to use 'main' as the row to write to the sheet and not 'r' which is used for the arrays - this is part of the code where arrays are written to the sheet.
Sub Write1()
Dim r As Long
Dim c As Long
Dim d As Long
Dim Start_Row As Long
Dim End_Row As Long
Dim main As Integer
Dim lngRowCount As Long
Dim w_Output As Worksheet
Dim w1 As Worksheet
Dim intLastRow As Integer
Dim IntLastCol As Integer
Const RowStart As Integer = 3
Const ColumnID As Integer = 1
Const Column_Desc As Integer = 3
Dim arrID() As Variant
Dim arrDesc() As Variant
With ThisWorkbook
Set w1 = .Sheets("Input")
Set w_Output = .Sheets("Output")
End With
'***********************************
'arrays
With w1
intLastRow = .Cells(Rows.Count, 1).End(xlUp).Row
arrID = .Range(.Cells(RowStart, ColumnID), .Cells(intLastRow, ColumnID))
arrDesc = .Range(.Cells(RowStart, Column_Desc), .Cells(intLastRow, Column_Desc))
'******************************************
main = 1
End_Row = 2
For Start_Row = 1 To End_Row
w_Output.Cells(main, 3) = "Title"
main = main + 1
For r = 1 To UBound(arrID, 1)
If Len(arrID(r, 1)) > 0 Then
'Write
w_Output.Cells(main, 3) = arrID(r, 1)
w_Output.Cells(main, 4) = arrDesc(r, 1)
End If
main = main + 1
Next r
w_Output.Cells(main, 3) = "Total "
main = main + 4
Next Start_Row
End With
MsgBox "Done", vbInformation
End Sub
It works perfectly.

Related

Pick random names from different lists excel VBA

I would like to pick random names from columns in excel like this :
-In the first sheet "Inscrp" is where the lists are, and the second sheet "Tirage" is where the results of the picking.
-Column A in the sheet "Tirage" should pick random names from column A in the sheet "Inscrp" and the same for the column B, C , till the number of columns I chose
I managed to do this with only the first column and here is the code :
Sub PickNamesAtRandom()
Dim HowMany As Integer
Dim NoOfNames As Long
Dim RandomNumber As Integer
Dim Names() As String 'Array to store randomly selected names
Dim i As Byte
Dim CellsOut As Long 'Variable to be used when entering names onto worksheet
Dim ArI As Byte 'Variable to increment through array indexes
Application.ScreenUpdating = False
HowMany = 5
CellsOut = 8
ReDim Names(1 To HowMany) 'Set the array size to how many names required
NoOfNames = Application.CountA(Worksheets("Inscrp").Range("A3:A100")) - 1 ' Find how many names in the list
i = 1
Do While i <= HowMany
RandomNo:
RandomNumber = Application.RandBetween(3, NoOfNames + 1)
'Check to see if the name has already been picked
For ArI = LBound(Names) To UBound(Names)
If Names(ArI) = Worksheets("Inscrp").Cells(RandomNumber, 1).Value Then
GoTo RandomNo
End If
Next ArI
Names(i) = Worksheets("Inscrp").Cells(RandomNumber, 1).Value ' Assign random name to the array
i = i + 1
Loop
'Loop through the array and enter names onto the worksheet
For ArI = LBound(Names) To UBound(Names)
Worksheets("Tirage").Cells(CellsOut, 1) = Names(ArI)
CellsOut = CellsOut + 1
Next ArI
Application.ScreenUpdating = True
End Sub
Please, test the next code. If I correctly understand your nee, it will extract HowMany random numbers from each column (nrCol) of "Inscrip" sheet and placed starting from CellsOut in sheet "Tirage". The already extracted name is eliminated from the array where it used to exist (to avoid repeated names). The ranges ar placed in arrays and due to that, the code should be very fast mostly working in memory, even for large ranges:
Sub PickNamesAtRandom()
Dim shI As Worksheet, lastR As Long, shT As Worksheet, HowMany As Long
Dim rndNumber As Integer, Names() As String, i As Long, CellsOut As Long
HowMany = 5: CellsOut = 8
Set shI = Worksheets("Inscrp")
Set shT = Worksheets("Tirage")
Dim col As Long, arrCol, filt As String, nrCol As Long
nrCol = 2 'number of columns to be returned. It can be changed and also be calculated...
For col = 1 To nrCol
lastR = shI.cells(shI.rows.count, col).End(xlUp).Row 'last row in column to be processed
If lastR >= HowMany + 2 Then '+ 2 because the range is build starting with the third row...
arrCol = Application.Transpose(shI.Range(shI.cells(3, col), shI.cells(lastR, col)).Value2) 'place the range in a 1D array
ReDim Names(1 To HowMany) 'Set the array size to how many names required
For i = 1 To UBound(Names)
tryAgain:
Randomize
rndNumber = Int((UBound(arrCol) - LBound(arrCol) + 1) * Rnd + LBound(arrCol))
If arrCol(rndNumber) = "" Then GoTo tryAgain
Names(i) = arrCol(rndNumber)
filt = arrCol(rndNumber) & "##$$#": arrCol(rndNumber) = filt
arrCol = filter(arrCol, filt, False) 'eliminate the already used name from the array
Next i
shT.cells(CellsOut, col).Resize(UBound(Names), 1).Value2 = Application.Transpose(Names)
End If
Next col
MsgBox "Ready..."
End Sub
If something unclear, do not hesitate to ask for clarifications...

Nesting For loops in VBA

Sub adress()
Dim s As Long
Dim h As Long
Dim n As Long
Dim i As Long
s = 1
n = 1
h = 1
For n = 1 To 1800
For i = 1 To 2000
If ActiveSheet.Cells(h + 1, 13) = ActiveSheet.Cells(s + 1, 32) Then
ActiveSheet.Cells(h + 1, 48) = ActiveSheet.Cells(s + 1, 36)
ActiveSheet.Cells(h + 1, 51) = ActiveSheet.Cells(s + 1, 37)
End If
s = s + 1
Next i
h = h + 1
i = 1
Next n
End Sub
This code is written to grab a value in a column of an excel spread sheet, then go to the next column and search the whole column for a matching value. Once that is found it will print the value that is in a cell in the same row of the value in the second column it found, into a cell in the same row as the original value it was trying to match.
While the inner loop works and my code will do the correct process when ran, it only does it for one value in the first column. I have tried using ranges in the For Loops, I have tried do while loops and do until loops. If i manually change the value of "h" and run the code it will progress down the column and print the correct information but i cannot get "h" to update on its own.
Nested For Next Loops
Although Exit For and turning off the two application settings are used, the first procedure still takes 45 seconds on my machine (without the 'improvements' it might take half an hour).
In the second code the inner loop is replaced with Application.Match and the operations are performed using arrays. It takes less than a second.
The Code
Option Explicit
Sub loopSlow()
Dim i As Long
Dim k As Long
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
With ActiveSheet
For i = 2 To 1801
For k = 2 To 2001
If .Cells(i, 13).Value = .Cells(k, 32).Value Then
.Cells(i, 48).Value = .Cells(k, 36).Value
.Cells(i, 51).Value = .Cells(k, 37).Value
Exit For
End If
Next k
Next i
End With
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
Sub loopFast()
' Source
Const sName As String = "Sheet2"
Const sColsList As String = "AF,AJ,AK"
Const sFirstRow As Long = 2
' Destination (Lookup)
Const dName As String = "Sheet2"
Const dColsList As String = "M,AV,AY"
Const dFirstRow As Long = 2
' Define workbook.
Dim wb As Workbook: Set wb = ThisWorkbook
' Declare variables.
Dim ws As Worksheet
Dim rg As Range
Dim Cols() As String
Dim cUpper As Long
Dim cOffset As Long
Dim n As Long
' Write values from Source Columns to arrays of Data Array.
Cols = Split(sColsList, ",")
cUpper = UBound(Cols)
Set ws = wb.Worksheets(sName)
Set rg = ws.Cells(ws.Rows.Count, Cols(0)).End(xlUp)
Set rg = ws.Range(ws.Cells(sFirstRow, Cols(0)), rg)
Dim Data As Variant: ReDim Data(0 To cUpper)
For n = 0 To cUpper
cOffset = ws.Columns(Cols(n)).Column - rg.Column
Data(n) = rg.Offset(, cOffset).Value
Next n
' Write values from Lookup Column to Lookup Array of Result Array.
Cols = Split(dColsList, ",")
Set ws = wb.Worksheets(dName)
Set rg = ws.Cells(ws.Rows.Count, Cols(0)).End(xlUp)
Set rg = ws.Range(ws.Cells(dFirstRow, Cols(0)), rg)
Dim Result As Variant: ReDim Result(0 To cUpper)
Result(0) = rg.Value
' Define the (remaining) Write Arrays of Result Array.
Dim ResultNew As Variant: ReDim ResultNew(1 To UBound(Result(0)), 1 To 1)
For n = 1 To cUpper
Result(n) = ResultNew
Next n
' Write values from Data Array to Write Arrays of Result Array.
Dim cIndex As Variant
Dim i As Long
For i = 1 To UBound(Result(0))
cIndex = Application.Match(Result(0)(i, 1), Data(0), 0)
If IsNumeric(cIndex) Then
For n = 1 To cUpper
Result(n)(i, 1) = Data(n)(cIndex, 1)
Next n
End If
Next i
' Write values from Write Arrays of Result Array to Destination Columns.
For n = 1 To cUpper
cOffset = ws.Columns(Cols(n)).Column - rg.Column
rg.Offset(, cOffset).Value = Result(n)
Next n
End Sub

Select a Value from Form ListBox and Show Value Associated

I have a list of items and descriptions in column A. The first item is in the 5th row. Each item is followed by the item description.
It looks something like the following (different, but the same concept):
Apple
Red Fruit
Banana
Yellow Fruit
What I am trying to do is put these both into 2 arrays based on whether it's an item or the description.
I've done that here:
Option Explicit
Option Base 1
Sub main()
Dim rngList As Range
Dim strNetId As String
Dim strListArray() As String
Set rngList = Sheets("data").Range("A1").CurrentRegion
Call CreateArray(rngList, strListArray())
Call CreateArray2(rngList, strListArray())
End Sub
Sub CreateArray(rngIn As Range, strArray() As String)
Dim iCols As Integer
Dim iRows As Integer
Dim iRowsH As Integer
Dim i As Integer
Dim j As Integer
Dim Counter As Integer
Dim Counter2 As Integer
Dim Count2 As Integer
iRows = (rngIn.Rows.Count - 1)
iCols = 1
iRowsH = (rngIn.Rows.Count - 1) / 2
ReDim strArray(iRows, iCols)
Count2 = 3
Counter = 1
Do
If Count2 Mod 2 <> 0 Then
strArray(Counter, 1) = rngIn.Cells(Count2 + 2, 1)
Counter = Counter + 1
End If
Count2 = Count2 + 1
Loop Until Count2 > iRows
End Sub
Sub CreateArray2(rngIn2 As Range, strArray2() As String)
Dim iCols As Integer
Dim iRows As Integer
Dim iRowsH As Integer
Dim i As Integer
Dim j As Integer
Dim Counter As Integer
Dim Counter2 As Integer
Dim Count2 As Integer
iRows = (rngIn2.Rows.Count - 1)
iCols = 1
iRowsH = (rngIn2.Rows.Count - 1) / 2
ReDim strArray2(iRows, iCols)
Count2 = 3
Counter = 1
Do
If Count2 Mod 2 = 0 Then
strArray2(Counter, 1) = rngIn2.Cells(Count2 + 2, 1)
Counter = Counter + 1
End If
Count2 = Count2 + 1
Loop Until Count2 > iRows
End Sub
Where I'm running into a problem is getting my form to work. What I want to happen is you start the form and then click one of the items that will be populated in the ListBox. Then a text box will pull up the associated description. Here's what I have in my form's code, but I'm getting an error when you actually select the item from the form:
Option Base 0
Option Explicit
Dim strArray2()
Private Sub btnDone_Click()
Unload frmNetID
End Sub
Private Sub lstNetID_Click()
lblFirstName.Caption = strArray2(lstNetID.ListIndex + 2, 1)
End Sub
Private Sub UserForm_Initialize()
Dim rngList As Range
Dim strNetId As String
Dim strList() As String
Dim iR As Integer
With ThisWorkbook.Worksheets("data")
iR = .Range("A1").CurrentRegion.Rows.Count
Set rngList = .Range("A1:A" & iR) 'it assumes header row
Call CreateArray(rngList, strList())
End With
lstNetID.List() = strList()
End Sub
Where am I making the first mistake? I'm guessing it has something to do with the lblFirstName.Caption line of code.
You code can be simplified and more eficient, I think. Please, see how the necessary arrays can be a little differently built and used:
Private Sub testFillArrays()
Dim sh As Worksheet, arr As Variant, arrN As Variant, arrD As Variant
Dim n As Long, d As Long, i As Long, arrGlob() As Variant
Set sh = ActiveSheet 'you will use Sheets("data")
arr = sh.Range("A1").CurrentRegion.Value2 'base 1 array
ReDim arrN(0 To UBound(arr, 2), 0 To Int(UBound(arr, 1) / 2)) '(0 based array)
ReDim arrD(0 To UBound(arr, 2), 0 To Int(UBound(arr, 1) / 2))
For i = 2 To UBound(arr)
If i Mod 2 = 0 Then
arrN(0, n) = arr(i, 1): n = n + 1 ' fruit names array
Else
arrD(0, d) = arr(i, 1): d = d + 1 ' fruit colors array
End If
Next i
ReDim Preserve arrN(0 To 1, 0 To n - 1) 'clear the last empty element
ReDim Preserve arrD(0 To 1, 0 To d - 1)
'arrN is the array to be load in the list box.
arrGlob = Array(arrN, arrD) 'define the array of arrays
i = lstNetID.ListIndex
Debug.Print arrGlob(0)(0, i), arrGlob(1)(0, i) 'and press Ctrl + G to see the result...
End Sub

Match 2 arrays with rows' values

I want to write a code that uses two 1D arrays and based on the match with the value on the row, it should return the value in the 3rd array.
This is what I want to do:
In Sheet1, I have 3 columns with data on ID, Name, and Amount with a number of rows of uncertain size:
In Sheet2, I have already the columns with data on ID and Name but I don't have the data on Amount:
Therefore, I want to run the code that will match the arrays with ID and Name data in Sheet1 with ID and Name data in Sheet2 and then, return the respective Amount data to Sheet2 as it is in Sheet1.
This is the desired outcome in Sheet2 after running the code, i.e. the data in column Amount are returned based on the match with arrays on ID and Name in Sheet1:
This is my code that does not run as it should:
Sub ArrayMatch()
Dim r As Long
Dim d As Long
Dim w_output As Worksheet
Dim w1 As Worksheet
Dim intLastRow As Integer
Dim IntLastCol As Integer
Dim arrName() As Variant
Dim arrID() As Variant
Dim arrrAmoun() As Variant
d = 8
With ThisWorkbook
Set w1 = .Sheets("Sheet1")
Set w_output = .Sheets("Sheet2")
End With
'***********************************
'Assign arrays
With w1
intLastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
IntLastCol = .Cells(4, Columns.Count).End(xlToLeft).Column
arrID = .Range(.Cells(4, 1), .Cells(intLastRow, 1))
arrName = .Range(.Cells(4, 3), .Cells(intLastRow, 2))
arrAmoun = .Range(.Cells(4, 4), .Cells(intLastRow, 3))
For r = 1 To UBound(arrID, 1)
If Len(arrID(r, 1)) > 0 Then
d = d + 1
If w_output.Cells(d, 1) = arrID(r, 1) Then
If w_output.Cells(d, 2) = arrName(r, 1) Then
w_output.Cells(d, 4) = arrAmoun(r, 1)
End If
End If
End If
Next r
End With
End Sub
My code does not return anything, I can assume that it is because I am comparing the arrays from sheet1 with rows in sheet 2 which is not comparative in the size, but I don't know how to do in another way.
I will appreciate any help.
Just modified your code to include an inner loop to check for ID and name in w_output sheet (it could also be done with Find). Tested with makeshift data. However there are other (more efficient) ways to achieve the same goal.
Sub ArrayMatch()
Dim r As Long
Dim d As Long
Dim w_output As Worksheet
Dim w1 As Worksheet
Dim intLastRow As Long ' Modified to long
Dim IntLastRow1 As Long ' Modified to long
Dim arrName() As Variant
Dim arrID() As Variant
Dim arrrAmoun() As Variant
'd = 8
With ThisWorkbook
Set w1 = .Sheets("Sheet1")
Set w_output = .Sheets("Sheet2")
End With
'***********************************
'Assign arrays
With w1
intLastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
IntLastRow1 = w_output.Cells(Rows.Count, 1).End(xlUp).Row
arrID = .Range(.Cells(4, 1), .Cells(intLastRow, 1))
arrName = .Range(.Cells(4, 3), .Cells(intLastRow, 3))
arrAmoun = .Range(.Cells(4, 4), .Cells(intLastRow, 4))
For r = 1 To UBound(arrID, 1)
If Len(arrID(r, 1)) > 0 Then
For d = 9 To IntLastRow1 ' Modified to for loop for w_output sheet
If w_output.Cells(d, 1) = arrID(r, 1) Then
If w_output.Cells(d, 2) = arrName(r, 1) Then
w_output.Cells(d, 4) = arrAmoun(r, 1)
Exit For ' added once found and amount put in place
End If
End If
Next
End If
Next r
End With
End Sub

VBA, For Loop , subscript out of range on second loop

I am getting an error at If headers(iheaders, 1) = SR(1, iSR) Then First loop works fine. Then Second loop I get subscript out of range. But I notice it always loops back to For iSR = 1 To UBound(SR, 2) instead of For iheaders = 1 To UBound(headers, 2).
what the code does:
Headers on sheet A are supposed to match with those on Sheet B. They are both Horizontal headers.
where there is a match , assign the 5th row of SR to my variable R.
Code:
Const FirstMatch As Boolean = True
Dim lastrow As Long
Dim SR As Variant
Dim iSR As Integer
Dim R As Variant
Dim headers As Variant
Dim iheaders As Integer
SR = Worksheets("Sheet A").Range("D3:J7").Value
headers = Worksheets("Sheet B").Range("B1:H1").Value
With Worksheets("Sheet B")
ReDim R(1 To UBound(SR), 1 To 1)
For iheaders = 1 To UBound(headers, 2)
For iSR = 1 To UBound(SR, 2)
If headers(iheaders, 1) = SR(1, iSR) Then
R(iSR, 1) = SR(5, iSR)
If FirstMatch Then
Exit For
End If
End If
Next
Next
'Populate R to where I want on Total Page
End With

Resources