VB -Copy and Paste Nested Loop in Excel - excel

So I have a problem that this is generating random results with the Qty.
I am trying to make each qty (in their qty's) a new line on a new spreadsheet.
It creates the new sheet, and references the old sheet...
the code copies and pastes the lines...
It just doesn't loop the do while in the correct amount of times. I have tried different operands (>= 0) and altering the variable values to make this work.
It does not seem to be patternized as to why it is happening. Sometimes it does it in the correct amount of loop cycles, others it does not. This occurs on multiple values. Any help is greatly appreciated.
Sub copyPasta()
'
' copyPasta Macro
' This will take the qty, if greater than one in Column C and copy the row
'to a new sheet the amount of time the qty.
'
'
'Set Variable Types
Dim lineItemQty As Integer
Dim newLineItemQty As Integer
Dim LastRow As Integer
Dim strSheetName As String
Dim newSheetName As String
Dim i As Integer
Application.DisplayAlerts = False
'name a variable after the existing active sheet
strSheetName = ActiveSheet.Name
'add a sheet in addition to the current
Sheets.Add After:=ActiveSheet
'set a variable used in loops to the sheet being copied to
newSheetName = ActiveSheet.Name
'Return to first sheet
Sheets(strSheetName).Activate
' Set For Loop to max row
LastRow = Sheets(strSheetName).Range("C:C").Find("*", searchdirection:=xlPrevious).Row
'for loop to run through all rows
For i = 3 To LastRow Step 1
'initializing variable to Qty value in table
lineItemQty = Range("C" & i).Value
'initializing variable within in line of for looping
newLineItemQty = lineItemQty
'do while loop to keep copying/pasting while there are still qty's
Do While newLineItemQty > 0
'do while looped copy and paste
'copy the active row
Sheets(strSheetName).Activate
Rows(i).Select
Selection.Copy
'paste active row into new sheet
Sheets(newSheetName).Select
Rows("3:3").Select
Selection.Insert Shift:=xlDown
newLineItemQty = newLineItemQty - 1
Loop
Next i
Application.DisplayAlerts = True
End Sub

You can consider using (or taking parts from) the below alternative. A couple of note worthy notes are
You should avoid using .Select and .Activate. See here for details
Life is easier when you declare short variables. Here we just have ws for worksheet and ns for newsheet. You then need to actively state what sheet you are refferring to in your code (instead of using .Select or .Activate to do so by prefixing all objects with the appropriate worksheet variable)
You do not need to add Step 1 in your loop. This is the default - you only need to add this when you are deviating from the default!
There are a few ways to add sheets. Nothing wrong with the way you did - here is just an alternative (yay learning) that happens to be my preferred method.
To copy n many times, just create a nested loop and for 1 to n. Notice we never really use the variable n inside the loop which means the exact same operation will execute, we just want it to execute n times.
Sub OliveGarden()
Dim ws As Worksheet: Set ws = ThisWorkbook.ActiveSheet
Dim ns As Worksheet: Set ns = ThisWorkbook.Sheets.Add(After:=Sheets(ThisWorkbook.Sheets.Count))
ns.Name = ws.Name & " New"
Dim i As Long, c As Long
'Application.ScreenUpdating = False
For i = 3 To ws.Range("C" & ws.Rows.Count).End(xlUp).Row
If ws.Range("C" & i) > 0 Then
For c = 1 To ws.Range("C" & i)
LRow = ns.Range("A" & ns.Rows.Count).End(xlUp).Offset(1).Row
ws.Range("C" & i).EntireRow.Copy
ns.Range("A" & LRow).PasteSpecial xlPasteValues
Next c
End If
Next i
'Application.ScreenUpdating = True
End Sub

Related

Excel VBA create new sheet and copy text into cell

I have been using the below code successfully for years but recently it has stopped working. I've since upgraded to Office 365 and still no joy. Essentially the code should copy the Sheet "Response", paste a copy of a cell from "Database" and name the new sheet appropriately. It continues creating new sheets in the workbook until the end of the Database list.
If I run the code as is I get the following: "Run-time error '1004': Microsoft Excel cannot paste the data." When I look at the worksheets, evidentally the code runs and creates a sheet "Response4" (I've only given the database 4 lines to copy). Debug highlights the line ActiveSheet.Paste link:=True. I tested
Frustratingly the code works outside of my company's system (i.e., I sent it to a friend with dummy data and it worked perfectly fine).
Any suggestions very welcome!
Sub CopyCatView()
'NumResp = last row with a responses to the question held within the question 'Themes' database sheet
Dim NumResp As Integer
'x for looping variable
Dim x As Integer
'y for response number variable
Dim y As Integer
Dim ws As Worksheet
Sheets("Database").Activate
NumResp = Range("NumRowsD1").Value + 2
'NumRowsD1 is a named range comprising cell A1 on the Database sheet, which calculates by formula the number of comments in the database
For x = 3 To NumResp
Sheets("Response").Copy before:=Sheets("Response")
y = NumResp - x + 1
ActiveSheet.Name = "Response" & y
ActiveSheet.Range("C2").Value = Sheets("Database").Range("B" & x).Value
ActiveSheet.Range("AA5:CR5").Select
Selection.Copy
Sheets("Database").Select
Cells(x, 3).Select
ActiveSheet.Paste link:=True
Sheets("Response" & y).Activate
ActiveSheet.Range("F4").Select
Selection.Copy
Sheets("database").Select
Cells(x, 70).Select
ActiveSheet.Paste link:=True
'duplicates the Response sheet as many times as there are comments (=X), numbers them Response1 to ResponseX, copies each comment into the white box on a different response sheet from Response1 to ResponseX
'Also links through the check box reporting to the relevant row in the Database sheet
Next x
'at the end hide Sheet "Response"(deleting brings up prompts for every sheet deleted!)
Sheets("Response").Select
ActiveWindow.SelectedSheets.Visible = False
Sheets("Database").Activate
Range("A1").Select
End Sub
Since the "paste with link" requires ranges to be selected before pasting, I'd skip that and create a method to perform that function.
Also - use worksheet variables to reduce the repetition in your code and make for easier maintenance.
Sub CopyCatView()
Dim NumResp As Long, x As Long, y As Long 'prefer Long over Integer
Dim wsDB As Worksheet, wsResp As Worksheet, ws As Worksheet
Set wsDB = ThisWorkbook.Worksheets("Database")
Set wsResp = ThisWorkbook.Worksheets("Response")
NumResp = wsDB.Range("NumRowsD1").Value + 2
For x = 3 To NumResp
wsResp.Copy before:=wsResp
Set ws = ThisWorkbook.Sheets(wsResp.Index - 1) 'get a reference to the copy
y = NumResp - x + 1
ws.Name = "Response" & y
ws.Range("C2").Value = wsDB.Range("B" & x).Value
LinkRanges ws.Range("AA5:CR5"), wsDB.Cells(x, 3)
LinkRanges ws.Range("F4"), wsDB.Cells(x, 70)
Next x
wsResp.Visible = False
wsDB.Activate
wsDB.Range("A1").Select
End Sub
'Link two ranges in the same workbook
' rngFrom = contiguous (single-area) source range
' rngTo = top-left cell of the destination range
Sub LinkRanges(rngFrom As Range, rngTo As Range)
Dim r As Long, c As Long, nm As String
If Not rngFrom.Parent Is rngTo.Parent Then
nm = "'" & rngFrom.Parent.Name & "'!"
End If
For r = 1 To rngFrom.Rows.Count
For c = 1 To rngFrom.Columns.Count
rngTo.Cells(r, c).Formula = "=" & nm & _
rngFrom.Cells(r, c).Address(False, False)
Next c
Next r
End Sub

Automation of selective cell linking across worksheets > Runtime Error 91

I'm looking to automate the selective copying and stacking of data from multiple sheets into a single sheet. More specifically, I have 4 columns (M, H, A, & F) from which I need to selectively copy cells based on the same-row value of Column I. E.g. with the below case:
Worksheets 2...N
Column A_____Column F_____Column H_____Column I_____Column M
_#####________AAAAAA______AAAAAA_______Rqrmnt_______Date
_#####________AAAAAA______AAAAAA_______Heading_______Blank
For all rows with column I = Rqrmnt across N worksheets, I need to copy the corresponding values in columns A, F, H, and M into worksheet 1, stacking the imports of each sheet top-to-bottom, e.g.:
Worksheet 2 Column A...Worksheet 2 Column M
Worksheet 3 Column A...Worksheet 3 Column M
...
Worksheet N Column A...Worksheet N Column M
I need to be able to perform limited manipulation on the resulting table, specifically sorting the rows by the value of Column M
As I have several hundred such entries, I would prefer to not build this up by linking cells 1-by-1. Additionally, I would prefer to place the copied pseudo-columns individually (i.e. rearrange them in the order M>H>A>F on the master spreadsheet). I Have the following macro, derived from these posts (thanks to urdearboy's comment below for the second linked post). However, I get a Run-time Error 91 fault when I try to run the macro, and the debugger highlights the identified line below. While this post explains the error itself, I has not helped me solve this problem. I have tried initializing the sourceSheetLastRow to an arbitrary number, and slapping the Set keyword in front of the formula, but to no avail.
Option Explicit
Sub Test()
Dim summarySheetTargetRow As Long
Dim sourceSheetTargetRow As Long
Dim sourceSheetLastRow As Long
Dim sourceSheetIndex As Long
Dim numSheets As Long
Dim wb As Workbook
Dim summarySheet As Worksheet
Dim sourceSheet As Worksheet
Set wb = ThisWorkbook
Set summarySheet = wb.Sheets("Summary Sheet")
numSheets = ThisWorkbook.Sheets.Count `My understanding is that this will return the total number of worksheets in the workbook. However, the sheet index seems to skip the number 5, so this may not be getting me the actual number of sheets
sourceSheetIndex = 6 `First sheet from which I want to pull values. Note that the sheets have inconsistent names, so I'm trying to use the sheet index.
summarySheetTargetRow = 38 `Where I want to start plugging in copied cell values
`Make sure receiving area for copied info is clear
Sheets("Summary Sheet").Range("A38:D1415").ClearContents
For sourceSheetIndex = 6 To numSheets
Set sourceSheet = wb.Worksheets(sourceSheetIndex)
DEBUG THORWS FAULT HERE[
sourceSheetLastRow = sourceSheet.Range("M2:M1000").Find("*", SearchDirection:=xlPrevious).Row `I understand this to return the number of cells in the specified range, starting from the last non-empty cell.
]DEBUG THORWS FAULT HERE
For sourceSheetTargetRow = 2 To sourceSheetLastRow `Start at second row because header rows will never have relevant value
If sourceSheet.Range("I" & sourceSheetTargetRow) = "Text" Then
summarySheet.Range("A" & summarySheetTargetRow) = sourceSheet.Range("A" & sourceSheetTargetRow)
summarySheet.Range("B" & summarySheetTargetRow) = sourceSheet.Range("M" & sourceSheetTargetRow)
summarySheet.Range("C" & summarySheetTargetRow) = sourceSheet.Range("H" & sourceSheetTargetRow)
summarySheet.Range("D" & summarySheetTargetRow) = sourceSheet.Range("F" & sourceSheetTargetRow)
summarySheetTargetRow = summarySheetTargetRow + 1
End If
Next sourceSheetTargetRow
Next sourceSheetIndex
End Sub
When do you copy cells, what conditionals in column I need to be true/false? You could use Union to select multiple rows of cells at once.
Courtesy of help I get from my own company's e-forums and a good bit more work, I have finally gotten my desired output. The below macro will copy data from the identified ranges into a semi-dynamic range in a "Summary Sheet". It requires some knowledge of how the source sheet(s) will be formatted.
Option Explicit
Sub Data_Rollup()
'Object variables, which need to be released from memory at the end.
Dim wb As Workbook
Dim summarySheet As Worksheet
Dim sourceSheet As Worksheet
'Non-object variables
Dim summarySheetTargetRow As Long
Dim sourceSheetTargetRow As Long
Dim sourceSheetLastRow As Long
Dim sourceSheetIndex As Long
Dim numSheets As Long
On Error GoTo Error_Test 'Escape clause
'Initialize objects
Set wb = ThisWorkbook
Set summarySheet = wb.Sheets("Summary Sheet")
'Initialize non-object variables
sourceSheetIndex = 5 'Hard-coded starting point for data pull. This should be greater than the number for the Summary sheet.
summarySheetTargetRow = 38 'Hard-coded starting point for data deposition
numSheets = wb.Sheets.Count 'returns the number of sheets in workbook "wb"
Sheets("Summary Sheet").Range("A38:D1415").ClearContents 'Clears out destination cells in summary sheet
'Main loop
For sourceSheetIndex = 5 to numSheets
Set sourceSheet = wb.Worksheets(sourceSheetIndex)
If Not (sourceSheet.Range("A2:A1000").Find("*", SearchDirection:=xlPrevious) Is Nothing) Then 'Using *If Not (* Is Empty) Then* ensures the code just skips over sheets that don't have the range A2:A1000 populated
sourceSheetLastRow = sourceSheet.Range("A2:A1000").Find("*", SearchDirection:=xlPrevious).Row 'searches through the defined range from the bottom up, and returns the number of the first populated row
For sourceSheetTargetRow = 2 To sourceSheetLastRow 'Start at second row to skip headers
If sourceSheet.Range("I" & sourceSheetTargetRow) = "Text" Then
summarySheet.Range("A" & summarySheetTargetRow) = sourceSheet.Range("A" & sourceSheetTargetRow)
summarySheet.Range("B" & summarySheetTargetRow) = sourceSheet.Range("M" & sourceSheetTargetRow)
summarySheet.Range("C" & summarySheetTargetRow) = sourceSheet.Range("H" & sourceSheetTargetRow)
summarySheet.Range("D" & summarySheetTargetRow) = sourceSheet.Range("F" & sourceSheetTargetRow)
summarySheetTargetRow = summarySheetTargetRow + 1
End If 'best practise is to always have an Else clause, but it isn't technically necessary
Next sourceSheetTargetRow 'Cycles loop to next row down
End If 'best practise is to always have an Else clause, but it isn't technically necessary
Next sourceSheetIndex 'Cycles loop to next worksheet
Exit_Test: 'Deallocates memory for object variables
Set sourceSheet = Nothing
Set summarySheet = Nothing
Set wb = Nothing
Exit Sub
Error_Test
MsgBox Err.Number & "-" & Err.Description
GoTo Exit_Test
End Sub

Excel VBA - issue deleting row/col from incorrect workbook

This code copies data from a worksheet and pastes data into a test.csv file. If the test.csv is closed then it opens it and pastes
data into it. If it is already open then just pastes data into it. Upto this step, process works fine. After the data is pasted
into test.csv, I need to delete rows with first column with value 'Old'. And then delete entire Col "A".
Here is the challenge I am facing. If the test.csv is closed, the macro opens it, pastes the data and then does the rows and col delete, and works as expected
, however, if the test.csv is already open, then it pastes the values as it is supposed to, but deleting row and col it does in
the main worksheet. I tried putting Delete code in With block but does not help. Please suggest!
Sub Macro()
Dim LR As Long, PR As Long, X As Long, MyCopyRange, MyPasteRange
Dim wb, myData As Workbook, shtPaste As Worksheet
Set wb = ThisWorkbook
'open target csv file if not already opened
If CheckFileIsOpen("test.csv") = False Then
Set myData = Workbooks.Open(strFinalizedForBulkImport & "test.csv")
Else
Set myData = Workbooks("test.csv")
End If
Set shtPaste = myData.Sheets("test")
shtPaste.UsedRange.Clear
With wb.Sheets("Report Grp")
LR = .Range("A" & .Rows.Count).End(xlUp).Row
MyCopyRange = Array("A4:A" & LR, "B4:B" & LR, "C4:C" & LR, "D4:D" & LR) 'Put ranges in an array
MyPasteRange = Array("A1", "B1", "C1", "D1")
If LR > 1 Then
j = 0
For X = LBound(MyCopyRange) To UBound(MyCopyRange) 'Loop the array copying and pasting based on element in the array
.Range(MyCopyRange(j)).Copy
shtPaste.Range(MyPasteRange(j)).PasteSpecial xlPasteValuesAndNumberFormats
j = j + 1
Next
Else
Range("A1") = "No Data Found"
End If
End With
'Problem here, when trying to delete row with "Old" in Col "A" and finally Col "A" delete -
With wb.Sheets("test")
For LR = Range("A" & Rows.Count).End(xlUp).Row To 1 Step -1
If Range("A" & LR).Value = "Old" Then
Rows(LR).EntireRow.Delete
End If
Next LR
Columns("A").Delete Shift:=xlShiftToLeft
End With
End Sub
When using with anything in relation to that sheet need to be qualified with a period
With wb.Sheets("test")
For LR = .Range("A" & .Rows.Count).End(xlUp).Row To 1 Step -1
If .Range("A" & LR).Value = "Old" Then
.Rows(LR).EntireRow.Delete
End If
Next LR
.Columns("A").Delete Shift:=xlShiftToLeft
End With
In addition to qualifying all ranges and other sheet-dependent objects like #Davesexcel already posted, you should Set wb = ActiveWorkbook instead of Set wb = ThisWorkbook. The latter refers to the workbook the macro is stored in whereas you probably intend to use the active workbook.
For debugging, start the VBA editor (ALT+F11) and set a breakpoint on the first line of the delete loop. Check the name of the workbook wb refers to ("?wb.name"). Make the comparison case-independent by using If UCase(.Range("A" & LR).Value) = "OLD" Then.
If you single step one loop you will see why nothing is deleted.

VBA Copy and Paste

So I have a VBA that is suppose to copy the on the "data" sheet and paste it on the "Internal Use" via searching a cell on cell in the "Internal Use" I'm not getting an error it is just not doing it and it after I run the macro it just stays on the "data" sheet.
What am I missing?
Sub CommandButton2_Click()
Worksheets("Internal Use").Activate
project = Range("C4")
Worksheets("data").Activate
nr = Range("A" & Rows.Count).End(xlUp).Row
For Row = 2 To nr
If Range("F" & Row) = Worksheets("Internal Use").Range("C4") Then
Range("Q" & Row) = Worksheets("Internal Use").Range("C7")
End If
Next Row
End Sub
Hard to tell what you're trying to do. Let me know if this is what you want.
Sub CommandButton2_Click()
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim nr As Long
Dim project As Variant
Set ws1 = ThisWorkbook.WorkSheets("Internal Use")
Set ws2 = ThisWorkbook.WorkSheets("data")
project = ws1.Range("C4").Value2
With ws1
nr = .Range("A" & .Rows.Count).End(xlUp).Row
For r = 2 To nr
If .Range("F" & r) = project Then
ws2.Range("Q" & r) = .Range("C7")
End If
Next
End With
End Sub
Ricardo,
Your code is working fine. Question is what are you trying to accomplish? If you are trying to paste on 'Internal Use' sheet, you need to activate it. I have added a line to activate it. Please be more specific on what you want to accomplish.
Sub CommandButton2_Click()
Worksheets("Internal Use").Activate
project = Range("C4")
Worksheets("data").Activate
nr = Range("A" & Rows.Count).End(xlUp).Row
Worksheets("Internal Use").Activate
For Row = 2 To nr
If Range("F" & Row) = Worksheets("Internal Use").Range("C4") Then
Range("Q" & Row) = Worksheets("Internal Use").Range("C7")
End If
Next Row
End Sub
You want to populate column Q on the data sheet with the value from Worksheet Internal Use cell C7, whenever column F on the same row is equal to cell C4.
I have to say that that's easily solvable with a formula using index match or a conditional formula like =If(F2='Internal Use'!$C$4,'Internal Use'!$C$7,"") (Just paste in column F). At least this is what your code currently more or less does or seems to want to achieve.
That said let's take a look at your code:
First of all avoid .Activate, it's unnecessary overhead. This will activate the worksheet. (By the way, the last .activate you use, is on the data worksheet, hence it stays there) Next you store C4 in an undeclared variable called project that you never use.
Next you reference the cells everywhere in the loop again. This means there is huge overhead on accessing and reading out these cells. Lastly you do this in a loop; I assume this is to avoid filling up any of the other rows.
To make your code work, you could use:
Sub CommandButton2_Click()
Dim project as string
Dim writeValue as string
Dim lr as long
Dim wr as long
project = Worksheets("Internal Use").Range("C4").value
writeValue = Worksheets("data").Range("C7").value
lr = Range("A" & Rows.Count).End(xlUp).Row
With Worksheets("data")
For wr = 2 To lr
If .Range("F" & wr).value = project Then
.Range("Q" & rw).value = writeValue
End If
Next wr
End With
End Sub
This will do the trick.
Neater would be to avoid the for loop and testing all cells. Two options are putting the entire F and Q columns into arrays and loop through those simultaniously while altering the Q-array before dumping the values back in the sheet, or use a Find-algorithm such as Chip Pearons FindAll: http://www.cpearson.com/excel/findall.aspx

How to keep a log of usage of a macro

I have a rather silly problem. I have a macro (linked to a button) which copies cells A1:A2 from one worksheet (namedFP) to another worksheet (Log). I intend to copy these 2 cells on the log sheet every time I hit the macro button. The problem I am facing right now is that when I use the button multiple times, these cells are getting copied over each other instead of using the next available row to paste the cells.
This is what I have now, and I tried changing the 'Rowcount+1' to 'RowCount+2' but that did not work. Any help is appreciated.
DHRSheet.Select
Range("A1:A2").Select
Selection.Copy
LogSheet.Select
RowCount = LogSheet.UsedRange.Rows.Count
Dim r As Integer
r = RowCount + 1
Dim infocell As Range
Set infocell = Cells(r, 1)
infocell.Select
ActiveSheet.Paste
infocell.Value = DHRSheet.Name & "$" & infocell.Value
DHRSheet.Select
ActiveWorkbook.Save
Is this what you are trying?
Sub Sample()
Dim LogSheet As Worksheet, DHRSheet As Worksheet
Dim lrow As Long
'~~> Change this as applicable
Set LogSheet = Sheets("Sheet1")
Set DHRSheet = Sheets("Sheet2")
With LogSheet
lrow = LogSheet.Range("A" & .Rows.Count).End(xlUp).Row + 1
DHRSheet.Range("A1:A2").Copy .Range("A" & lrow)
End With
End Sub
Here's a function I use that is very reliable and always returns the last row of a sheet without fail:
(possibly excessive for your simple use, but I always recommend it)
Public Function LastRowOfSheet(ByVal TestSheetNumber As Variant)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Input: Sheet index # or Sheet name
' Output: Last row of sheet.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim intNumberOfRowsInWorksheet As Long
intNumberOfRowsInWorksheet = Sheets(TestSheetNumber).UsedRange.Rows.Count
intNumberOfRowsInWorksheet = intNumberOfRowsInWorksheet + Sheets(TestSheetNumber).UsedRange.Row - 1
LastRowOfSheet = intNumberOfRowsInWorksheet
End Function
And I'd clean up your above code and use something like this:
Sub Move2RowsToEnd()
Dim iNextRowOfOutput As Long
Dim iRowNumber As Long
'- use the function to find the last row of the output sheet. we'll be pasting to the first row after.
iNextRowOfOutput = (LastRowOfSheet("Log") + 1)
'- you can adjust this for loop to loop through additional cells if you need to paste more than 2 rows in the future.
For iRowNumber = 1 To 2
'- for each row of input (2 total) set the value of the output sheet equal to it.
Sheets("Log").Range("A" & iNextRowOfOutput).Value = Sheets("namedFP").Range("A" & iRowNumber).Value
iNextRowOfOutput = iNextRowOfOutput + 1
Next iRowNumber
'- not sure which of these you want to save (one or both)
Sheets("namedFP").Save
Sheets("Log").Save
End Sub
Just paste the function above or below the Subroutine and let me know if you have any issues or questions regarding the 'Move2RowsToEnd' code.

Resources