Excel VBA Nested For loop issue - excel

I have a large dataset with 60 sheets from which I need to extract three values per sheet individually. These values are to be summarized in one sheet so I can have an overview of the data.
I took an internet crash course in VBA to try and write a macro to avoid having to do this manually. My idea (that I was able to translate to code) was to copy the three cells per sheet to the rows of a 2D array, so I would end up with a [60x3] matrix that could be copied to a newly created sheet 'Means'. (I understand that this is very inefficient however it's the best I could come up with for now.)
The code runs but produces undesirable results: the matrix only consists out of a triplet of values from the final sheet. What I actually need from my macro is that it copies three values from sheet1 and pastes them to MeanTable(1,:), then copies three values from sheet2 and pastes them to MeanTable(2,:) and so on. I am convinced this is happening because my first nested loop is rubbish so I have been trying different loops and adding loops (and web searching of course) but so far I haven't been able to solve it.
Sub copy_to_one_sheet() 'copy sample means from each sheet to Means
Application.ScreenUpdating = False
Dim ws As Worksheet
Dim NumSheets As Integer
Dim NumSamples As Integer
Dim MeanTable() As Long 'store sample means in this 2D array, its size defined by number of sheets and samples per sheet
NumSheets = Application.Sheets.Count 'count number of sheets
NumSamples = 3 'number of samples per sheet (hardcoded for now)
ReDim MeanTable(NumSheets, 1 To NumSamples) 'MeanTable will be filled with sample means
'============================================
'= copy sample means per sheet to MeanTable =
'============================================
For i = 1 To UBound(MeanTable, 1) 'copy sample means from fixed columns per sheet to individual rows of Table array
For Each ws In ThisWorkbook.Worksheets 'go through sheets
MeanTable(i, 1) = ws.Cells(Rows.Count, 3).End(xlUp).Offset(-3, 0).Value
MeanTable(i, 2) = ws.Cells(Rows.Count, 10).End(xlUp).Offset(-3, 0).Value
MeanTable(i, 3) = ws.Cells(Rows.Count, 17).End(xlUp).Offset(-3, 0).Value
Next ws
Next i
'=============================================
'= create Sheet("Means") and paste MeanTable =
'=============================================
With ThisWorkbook
Set Dst = .Sheets.Add(After:=.Sheets(.Sheets.Count)) 'create new worksheet
Dst.Name = "Means" 'worksheet name
With Sheets("Means")
For k = 1 To UBound(MeanTable, 1)
For l = 1 To NumSamples
Cells(k, l).Value = MeanTable(k, l) 'paste Table variable with sample means to new worksheet ("Means")
Next l
Next k
End With
End With
End Sub
My question is: how can I make my loops cycle through each sheet in the workbook and copy a triplet of values to the corresponding rows of MeanTable, before moving on to the next sheet?
Any help would be greatly appreciated!

For i = 1 To UBound(MeanTable, 1) needed to be replaced with a counter i = i + 1`. Use Range.Resize to fill a range from an array.
Sub copy_to_one_sheet() 'copy sample means from each sheet to Means
Application.ScreenUpdating = False
Dim ws As Worksheet
Dim NumSheets As Integer
Dim NumSamples As Integer
Dim MeanTable() As Long 'store sample means in this 2D array, its size defined by number of sheets and samples per sheet
NumSheets = Application.Sheets.Count 'count number of sheets
NumSamples = 3 'number of samples per sheet (hardcoded for now)
ReDim MeanTable(1 To NumSheets, 1 To NumSamples) 'MeanTable will be filled with sample means
For Each ws In ThisWorkbook.Worksheets 'go through sheets
i = i + 1
MeanTable(i, 1) = ws.Cells(Rows.Count, 3).End(xlUp).Offset(-3, 0).Value
MeanTable(i, 2) = ws.Cells(Rows.Count, 10).End(xlUp).Offset(-3, 0).Value
MeanTable(i, 3) = ws.Cells(Rows.Count, 17).End(xlUp).Offset(-3, 0).Value
Next ws
With ThisWorkbook
Set Dst = .Sheets.Add(After:=.Sheets(.Sheets.Count)) 'create new worksheet
Dst.Name = "Means" 'worksheet name
With Sheets("Means")
.Range("A1").Resize(UBound(MeanTable, 1), UBound(MeanTable, 2)).Value = MeanTable
End With
End With
End Sub

Related

Multiple return for vlookup and summing up for large no. of table arrays

I have a master list as shown here
Stock lists are as below :
enter image description here
I'm referring stock availability from different sheets and putting vlookup in each column and then processing that data like sum or average,
for large no of sheets process gets very slow and individual vlookup formulas when sheets are more makes excel very sluggish.
any advise to do it efficiently
I have tried doing vlookup through vba but process was very slow for large data ( approx 2000 rows and 60 no of sheets)
VBA code is as follows :
Sub vlookup1()
Dim myrange As Range
item_nos = Worksheets("MASTER").UsedRange.Rows(Worksheets("MASTER").UsedRange.Rows.Count).Row
For j = 3 To 3
Dim ws As Worksheet
Set ws = Worksheets("Stock-" & j)
lastcolumn = ws.UsedRange.Columns.Count
lastrow = ws.UsedRange.Rows.Count
With ws
Set myrange = .Cells(1, 1).Resize(lastrow, lastcolumn)
End With
For i = 2 To item_nos
item_tag = Worksheets("MASTER").Cells(i, 1)
stock = ""
On Error Resume Next
stock = Application.WorksheetFunction.VLookup(item_tag, myrange, 2, False)
Worksheets("MASTER").Cells(i, 5 + j) = stock
'i = i + 1
Next i
Next j
End Sub

VBA to match a line in one sheet based on a few criteria and move antire row to another sheet

I am quite new into VBA and all in all into programming.
I would like to create a tool that helps me to do some manual tasks.
So, background:
I work at one of many finance companies and we receive couple thousand trades daily and we have to match it - it is automated - the system receives the trade from outside (called HE side) and compares details with our database (Called ME side) and if the details the same it will match it. But a few hundred HE side trades get stuck in the system. And it is my job to clear the backlog manually.
To do that I have to download HE side and ME side separately (.xls files) and compare in excel.
I have tried a few VBA, but I could not do fully functioning tool.
I will download both files and paste it into one:
Sheet1 contains HE side trades, sheet2 " contains ME side trades.
I want to make a tool that run through both sheets and based on matching criteria moves the row from A to Q to 3rd sheet. (from both sheet1 and sheet2)
Problem is both trades are inverted:
Matching criteria is: sheet1 Columns I,J,M should match I,J,M on sheet2 respectively.
And sheet1 column L should match sheet2 column O and vice versa (I have put colors above)
Based on this it should move matching rows from sheet1 and sheet2(A : Q) to sheet3
end product should look like this
Sheet1 and sheet2 contains data from A : AU but I only need to move A:Q
Both sheets have a few hundred rows.
And it should 1st matching rows 1st (if in sheet1 row 15 matches to sheet2 row 102 - it should move row 15 first and paste row 102 under it)
thank you, guys, for all help in advance!
welcome to StackOverflow. It's great that you're thinking at automating your mundane tasks, but it would be even better if you give it a go yourself first, there are countless VBA tutorials around.
The below assumes you have the spreadsheets separated, and the results in a third spreadsheet (where this code would reside).
Anyways, see if this gives you a starting point at least:
Option Explicit
Sub findMatches()
Application.ScreenUpdating = False
'this workbook
Dim wb As Workbook: Set wb = ThisWorkbook
Dim wsDestination As Worksheet: Set wsDestination = wb.Sheets("your destination sheet name here")
'get HIS workbook
Dim wbHIS As Workbook: Set wbHIS = Workbooks.Open("C:\...yourpath...\wbHIS.xls")
Dim wsHIS As Worksheet: Set wsHIS = wbHIS.Sheets("his sheet name here")
'get ME workbook
Dim wbME As Workbook: Set wbME = Workbooks.Open("C:\...yourpath...\wbME.xls")
Dim wsME As Worksheet: Set wsME = wbME.Sheets("me sheet name here")
'other variables
Dim arrHis, arrME, arrDestination
Dim i As Long, j As Long, x As Long, z As Long
'get the data from the worksheets
arrHis = getData(wsHIS)
arrME = getData(wsME)
If IsEmpty(arrHis) Or IsEmpty(arrME) Then GoTo exitSub
'create the destination array with the max possible rows in case there is a match for every single row, assume same number of columns
ReDim arrDestination(1 To UBound(arrHis, 1) + UBound(arrME, 1), 1 To UBound(arrME, 2))
'iterate through all rows in his data
x = 2
For i = LBound(arrHis, 1) + 1 To UBound(arrHis, 1)
'iterate through all rows in me data
For j = LBound(arrME, 1) + 1 To UBound(arrME, 1)
'if they match on columns "I", "J", "M"
If arrHis(i, 9) = arrME(j, 9) _
And arrHis(i, 10) = arrME(j, 10) _
And arrHis(i, 13) = arrME(j, 13) Then
'we add to the temp array
For z = LBound(arrDestination, 2) To UBound(arrDestination, 2)
If x = 2 Then
'add headers
arrDestination(1, z) = arrHis(1, z)
End If
arrDestination(x, z) = arrHis(i, z)
arrDestination(x + 1, z) = arrME(j, z)
Next z
x = x + 2
Exit For
End If
Next j
Next i
'now dump the data back to the worksheet
With wsDestination
.Range(.Cells(1, 1), .Cells(x - 1, UBound(arrDestination, 2))) = arrDestination
End With
exitSub:
'close the workbooks
wbHIS.Close
wbME.Close
Application.ScreenUpdating = True
End Sub
Function getData(sht As Worksheet)
With sht
'allocate the data from the sheet to an array
getData = .Range( _
.Cells(1, 1), .Cells(.Cells(.Rows.Count, 1).End(xlUp).Row, _
.Cells(1, .Columns.Count).End(xlToLeft).Column) _
)
End With
End Function
There is very little error handling here, I leave that to you.
Let me know if it helps.

Copy a template and fill in values from another worksheet

I am now trying to creating several worksheets and copying data from an existing worksheet to the worksheet that I just created.
This is what I have tried so far:
Sub CreateTemplate()
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = "CUST001"
Worksheets("Template").Cells.Copy Worksheets("CUST001").Cells
Worksheets("CUST001").Select
Range("C4") = "='CDE Information'!R[-2]C[-2]"
Range("C5") = "='CDE Information'!R[-3]C[-1]"
Range("C6") = "1111"
Range("C7") = "2222"
End Sub
This is an example of a table that I want to copy.
Table
I also want to create the worksheets and name them by the values of each row in column A.
So, it seems to me that I should do something with loops but I have no idea about that.
Can anyone help me out? Thank you in advance!
Welcome to stack. Try this:
Option Explicit
Sub copyWs()
Dim arr, j As Long
With Sheet1
arr = .Range("A1").CurrentRegion.Value2 'get all data in memory
For j = 1 To UBound(arr) 'traverse rows
.Copy After:=ActiveWorkbook.Sheets(Worksheets.Count) 'add ws after the last ws
Sheets(ActiveWorkbook.Sheets(Worksheets.Count).Index).Name = arr(j, 1) 'name the last added ws
Next j
End With
End Sub
Now that we already have an array with all data we can also copy only part of our data to a new sheet instead of copying the whole sheet. To achieve this we'll just create a blank sheet first:
Sheets.Add After:=ActiveWorkbook.Sheets(Worksheets.Count) 'add ws after the last ws
When iterating an array we'll use 2 "counter" variables. 1 to go trough the lines, 1 to go trough the columns.
Dim j As Long, i As Long 'initiate our counter vars
For j = 1 To UBound(arr) 'traverse rows
For i = 1 To UBound(arr, 2) 'traverse columns
'here we can access each cell by referencing our array(<rowCounter>, <columnCounter>
'e.g. arr(j,i) => if j = 1 and i = 1 we'll have the values of Cell A1
Next i
Next j
The "Ubound" function allows us to get the total nr of rows and columns.
Dim arr2
ReDim arr2(1 To 1, 1 To UBound(arr)) '=> we only need 1 line but all columns of the source, as we cannot dynamically size an array with the "dim", we redim
For j = 1 To UBound(arr) 'traverse rows
For i = 1 To UBound(arr, 2) 'traverse columns
'here we can access each cell by referencing our array(<rowCounter>, <columnCounter>
'e.g. arr(j,i) => if j = 1 and i = 1 we'll have the values of Cell A1
'we can dump these values anywhere in the activesheet, other sheet, other workbook, .. but to limit the number of interactions with our sheet object we can also create new, intermediant arrays
'e.g. we could now copy cel by cel to the new sheet => Sheets(arr(j,1).Range(... but this would create significant overhead
'so we'll use an intermediant array to store the full line
arr2(1, i) = arr(j, i)
Next i
'when we have all the columns we dumb to the sheet
With Sheets(arr(j, 1)) 'the with allows us the re-use the sheet name without typing it again
.Range(.Cells(1, 1), .Cells(UBound(arr2), UBound(arr2, 2))).Value2 = arr2 'the ubound function allows us to size the "range" to the same size as our array, once that's done we can just dumb it to the sheet
End With
Next j

VBA Copy Paste suddenly repeating pasting

I've been running this script for a while with not issues, and then today it broke. It's very basic as in I'm just filtering values from one tab and then copying and pasting them onto another tab in the top row. Suddenly though, it will paste the values and then repeat paste the values 19 more times for a total of 20 copy pastes.
Sheets("BSLOG").Select
Range("Q1").Select
Selection.AutoFilter Field:=17, Criteria1:="1"
Range("A1:Q5000").Select
Range("A1:Q5000").Activate
Selection.Copy
Sheets("PENDG TRADES").Select
Range("A1:Q300").Select
ActiveSheet.Paste
Try the next code, please. No need to select, activate anything. In this case, these selections do not bring any benefit, they only consume Excel resources:
Sub testFilterCopy()
Dim shB As Worksheet, shP As Worksheet
Set shB = Sheets("BSLOG")
Set shP = Sheets("PENDG TRADES")
shB.Range("Q1").AutoFilter field:=17, Criteria1:="1"
shB.Range("A1:Q5000").Copy shP.Range("A1")
End Sub
If you want to make the range dynamic (in terms of rows) I can show you how to initially calculate the existing number of rows and set the range to be copied according to it.
FaneDuru is right.
You can also try this code, which I prefer more:
Option Base 1 'This means all array starts at 1. It is set by default at 0. Use whatever you prefer,depending if you have headers or not, etc
Sub TestFilter()
Dim shBSLOG As Worksheet
Dim shPENDG As Worksheet
Dim rngBSLOG As Range
Dim arrBSLOG(), arrCopy()
Dim RowsInBSLOG&
Dim i&, j&, k&
Set shBSLOG = Worksheets("BSLOG")
Set shPENDG = Worksheets("PENDG TRADES")
With shBSLOG
Set rngBSLOG = .Range(.Cells(1, 1), .Cells(5000, 17))
End With
RowsInBSLOG = rngBSLOG.Rows.Count
arrBSLOG = rngBSLOG
ReDim arrCopy(1 To RowsInBSLOG, 1 To 17) 'set the size of the new array as the original array
k = 1 'k is set to 1. This will be used to the row of the new array "arrCopy"
For i = 1 To RowsInBSLOG 'filter the array. From the row "i" = 1 to the total of rows "RowsinBSLOG
If arrBSLOG(i, 1) = 1 Then 'if the first column of the row i is equal to 1, then...
For j = 1 To 17
arrCopy(k, j) = arrBSLOG(i, j) 'copy the row
Next j
k = k + 1 'next copy will be in a new row
End If
Next i 'repeat
With shPENDG
.Range(.Cells(1, 1), .Cells(k, 17)) = arrCopy() 'place the new array in the new sheet
End With
End Sub

How to copy and paste unique data according to date, when the dates are repeated?

I am trying to copy data from one sheet to another according to matching dates and so far I can do this but the problem is that the most recent data for a corresponding date over writes all other data for the same date.
E.G.
I want to copy data from sheet 2 column 1 (based on the date in column 2)
I want to paste this data into sheet 1 column 2 (Based on the date in column 1)
As can be seen, only the last number from sheet 2 column 1 which corresponds to the respective date is pasted into ALL corresponding dates in sheet 1 column 2.
Instead, if there are two dates, I want two different numbers( from sheet 2 column 1 ) to be pasted into sheet 1 column 2.
My original code is as follows:
Sub Macroturnip()
'
' Macroturnip Macro
'
Dim Row As Double 'row is the row variable for the destination spreadsheet
Dim i As Date
Dim x As Long 'x is the row variable for the source spreadsheet
For Row = 1 To 825
i = Sheets("1").Cells(Row, 1)
If i <> DateSerial(1900, 1, 0) Then
'DateSerial(1900, 1, 0) --> this is the default for no data in the field, i.e. i want to skip these
For x = 2 To 450
If Sheets("2").Cells(x, 2) = Sheets("1").Cells(Row, 1) Then
Sheets("2").Select
Cells(x, 1).Select
Selection.Copy
Sheets("1").Select
Cells(Row, 2).Select
ActiveSheet.Paste
End If
Next x
End If
Next Row
End Sub
Is good practice to avoid using variable names that are already representing something in code, i.e.: Row.
Row number should a be a integer/long type
You should declare and assign your worksheets to variables
Most code in VBA can be written without using .Select, though sometimes you might need it, this is not one of those times... and you should avoid at all cost using it in a nested loop. For ex:
Sheets("2").Select
Cells(x, 1).Select
Selection.Copy
Can be easily rewritten as such:
Sheets("2").Cells(x, 1).Copy
This might need some better logic, but based on your screenshots, it works:
Sub Macroturnip()
'
' Macroturnip Macro
'
Dim wsDst As Worksheet: Set wsDst = ActiveWorkbook.Sheets("1")
Dim lRowDst As Long: lRowDst = wsDst.Cells(wsDst.Rows.Count, 1).End(xlUp).Row
Dim wsSrc As Worksheet: Set wsSrc = ActiveWorkbook.Sheets("2")
Dim lRowSrc As Long: lRowSrc = wsSrc.Cells(wsSrc.Rows.Count, 1).End(xlUp).Row
Dim rngFind As Range
Dim Rs As Long, Rd As Long 'row is the row variable for the destination spreadsheet
For Rd = 2 To lRowDst
If wsDst.Cells(Rd, 1) <> "" Then
'DateSerial(1900, 1, 0) --> this is the default for no data in the field, i.e. i want to skip these
For Rs = 2 To lRowSrc
If wsDst.Cells(Rd, 1) = wsSrc.Cells(Rs, 2) Then
Set rngFind = wsDst.Range("B2:B" & Rd).Find(wsSrc.Cells(Rs, 1), Lookat:=xlWhole)
If rngFind Is Nothing Then
wsDst.Cells(Rd, 2) = wsSrc.Cells(Rs, 1).Value
Exit For 'No need to keep checking, move on
End If
Set rngFind = Nothing
End If
Next Rs
End If
Next Rd
End Sub
PS: I've assumed that by Sheets("2") you actually referred to a sheet named 2, and not Sheet2 or Sheets(2) which though look similar, are not the same thing.

Resources