VBA to copy x (variable) rows to another worksheet - vba

Gods of VBA,
Have been trying all morning to tweak this piece of amateur-VBA (in which case i'm the amateur) to perform as i want.
What is does now is the following; Looks for cell value 1 in Column O on the third sheet in my workbook. When it gets a hit, it copies the row which has 1 in Column O to a new worksheet called "Blad1". It then switches back to my 3rd sheet in the workbook "Doorvoeren".
It will loop and perform the task as wanted, only thing i can't get it to do is copy rows based on a variable in sheet "Doorvoeren". When this value is 5, i want it to copy the row with 1 in column O, and the 4 rows below it. (as example).
Could you please send me in the right direction here? Trying to make it work, but also learn from it in the process.
My code is added in the sample below:
Sub testIt()
Dim r As Long, endRow As Long, pasteRowIndex As Long
endRow = 500
pasteRowIndex = 5
For r = 3 To endRow
If Cells(r, Columns("O").Column).Value = 1 Then
Rows(r).Select
Selection.Copy
Sheets("Blad1").Select
Rows(pasteRowIndex).Select
ActiveSheet.Paste
pasteRowIndex = pasteRowIndex + 1
Sheets("Doorvoeren").Select
End If
Next r
End Sub
EDIT: Thank you all for your answers, actually having real trouble to find one that works. To explain again; I need this VBA tweaked in the way that it looks at cell Q3, in sheet "Doorvoeren" to get the number of rows to copy. So, if Q3 is cell value; 5, i want it to to copy the row with number 1 in Column O, in Sheet "Doorvoeren", but also the other four rows below it.
So my 1 in Column O, is just a marker, not the number of rows i want to copy.
Please ask/tell me if i'm not being totally clear.

Here is my solution (slightly ammending your code with annotations)
Sub testIt()
'add another variable (called var)
Dim r As Long, endRow As Long, pasteRowIndex As Long, Var As Long
endRow = 500
pasteRowIndex = 5
For r = 3 To endRow
If Cells(r, Columns("O").Column).Value = 1 Then
'Grab the var number from the Doorvoeren sheet. Var will then determine how many rows need to be copied in each circumstance
Sheets("Doorvoeren").Select
Var = Cells(r, Columns("Q").Column).Value
Rows(r & ":" & r + (Var - 1)).Select
Selection.Copy
Sheets("Blad1").Select
Rows(pasteRowIndex).Select
ActiveSheet.Paste
pasteRowIndex = pasteRowIndex + Var
Sheets("Doorvoeren").Select
End If
Next r
End Sub

It's recommended if you avoid using Select and ActiveSheet, instead use referenced Sheets and Ranges.
Option Explicit
Sub testIt()
Dim r As Long, endRow As Long, pasteRowIndex As Long
Dim PasteRow As Long
With Sheets("Doorvoeren")
' find last row with data in Column "O" in "Doorvoeren" sheet
endRow = .Cells(.Rows.Count, "O").End(xlUp).Row
For r = 3 To endRow
If .Cells(r, "O").Value = 1 Then
pasteRowIndex = 1
Else
If .Cells(r, "O").Value = 5 Then
pasteRowIndex = 5
End If
End If
' find last row with data in Column "O" in "Blad1" sheet
PasteRow = Sheets("Blad1").Cells(Sheets("Blad1").Rows.Count, "O").End(xlUp).Row
' copy number of rows from "Doorvoeren" sheet to "Blad1" sheet, paste them on the first empty row in "Blad1" sheet
.Range("O" & r).Resize(pasteRowIndex).EntireRow.Copy Destination:=Sheets("Blad1").Range("A" & PasteRow + 1)
Next r
End With
End Sub

I made a slight change upon your explanation.
'====================================================
Sub testIt()
Dim r As Long, endRow As Long, pasteRowIndex As Long
Dim DestR as Range
Dim Rloop as Range
dim RowsCounter as Integer
endRow = 500
pasteRowIndex = 5
RowsCounter = 0
For Each Rloop in Sheets("Doorvoeren").range("O3:O" & endRow)
if Rloop = 1 and RowsCounter=0 then RowsCounter = Rloop.Offset(0, 2)
If Rloop = 1 or RowsCounter > 0 Then
Set DestR = Sheets("Blad1").range("A" & pasteRowIndex)
Rloop.EntireRow.Copy DestR
pasteRowIndex = pasteRowIndex + 1
RowsCounter = RowsCounter - 1
End If
Next Rloop
End Sub
Hope this helps better :)

Related

EXCEL - VBA - copy paste between 3 sheets

I'm trying to copy/paste information between three different Sheets.
The first two I only want to compare information with a If and if the condition is met then from sheet n2 copy all rows to a third sheet. My problem is that the code only finds the first value and stops. Here's the code.
Sub fallidas2()
Dim iLastRow As Long
Dim I As Long
Dim l As Long
Dim erow As Long
erow = Sheets("Failed_Trades").Range("A" & Rows.Count).End(xlUp).Row + 1
Workbooks("modelo titulos UK").Worksheets("xlsConsultaConciliacion").Select
iLastRow = Worksheets("xlsConsultaConciliacion").Cells(Rows.Count, "C").End(xlUp).Row
iLastRow2 = Worksheets("Fallidas").Cells(Rows.Count, "C").End(xlUp).Row
For I = 3 To iLastRow
For l = 2 To iLastRow2
If Sheets("xlsConsultaConciliacion").Cells(I, 1) = Sheets("Fallidas").Cells(l, 2) Then
Worksheets("Fallidas").Rows(I).EntireRow.Copy _
Destination:=Sheets("Failed_Trades").Range("A" & erow)
End If
Next l
Next I
End Sub
You need to increment erow as part of the If statement. I've also included an Exit For statement in there so that it doesn't keep searching through the 2nd worksheet after the first match is found. But maybe you want it to find additional matches and populate the same information repetitively on the third sheet?
If Sheets("xlsConsultaConciliacion").Cells(i, 1) = Sheets("Fallidas").Cells(l, 2) Then
Worksheets("Fallidas").Rows(i).EntireRow.Copy Destination:=Sheets("Failed_Trades").Range("A" & erow)
erow = erow + 1
Exit For
End If

VBA Nested Do While Loop vs. Nested Do While If Loop

I'm not sure where I'm going wrong. I'm trying to compare values within a column ("B") to a cell referenced to ("A1"). If the values in Column "B" equal "A1" I want it to count up. When it gets to the end of Column "B" I'm trying to get it to loop back and compare values in column "B" with "A2", etc. For example:
So Far I've written two different codes one with a nested do while loop and a nested do while if loop but i cant get them to loop through the whole column
Sub CountDb()
Dim i As Long
Dim iRow As Long
Dim initial As Long
i = 1
iRow = 1
initial = 1
Do While Cells(iRow, "A").Value <> "" 'initial loop, whilst there are values in cell "A" continue the loop
Do While Cells(i, "B").Value = Cells(iRow, "A").Value 'nested while loop, comparing the first B1 and cell A1.
If True Then Cells(i, "C") = initial 'if they A1 and B1 are equal, print 1 in column C
initial = initial + 1 'and move on comparing A1 with B2
If False Then
i = i + 1 'if not satisfied, move on to cell B2 etc.
Loop
iRow = iRow + 1 'when you get to the end of column B, start again and compare values with A2 and B
Loop
End Sub
Sub CountDb()
Dim i As Long
Dim iRow As Long
Dim initial As Long
'same comments as above, just different methodology
i = 1
iRow = 1
initial = 1
Do While Cells(iRow, "A").Value <> ""
If Cells(i, "B").Value = Cells(iRow, "A").Value Then
Cells(i, "C") = initial
Else
initial = initial + 1
i = i + 1
End If
iRow = iRow + 1
Loop
End Sub
Any help would be appreciated. Thanks!
*EDIT - fixed up column references
**EDIT - applied comments to code
Try this instead:
Option Explicit
Sub test()
Dim sht As Worksheet
Dim lastrow As Long, i as integer, j as integer, initial as integer
Set sht = Workbooks("Book1").Worksheets("Sheet1") 'Don't forget to change this
lastrow = sht.Cells(sht.Rows.Count, "A").End(xlUp).Row
For i = 1 To lastrow
initial = 1
lastrow = sht.Cells(sht.Rows.Count, "B").End(xlUp).Row
For j = 1 To lastrow
If Workbooks("Book1").Worksheets("Sheet1").Range("A" & i).Value = Workbooks("Book1").Worksheets("Sheet1").Range("B" & j).Value Then
Workbooks("Book1").Worksheets("Sheet1").Range("C" & j).Value = initial
initial = initial + 1
End If
Next j
Next i
End Sub
I prefer using For loops as opposed to Whiles, just because I can see the ranges being looped through more easily. Here we use nested For loops, the first to loop through column A, the second to loop through column B. If our value in column A equals our value in column B, we place the initial number in column C using our variable from the nested loop.
Notice how to make this work, we re-initialize our lastrow variable to make the ranges for our loops.
It is useful to use countif.
Sub test()
Dim rngOrg As Range, rngDB As Range
Dim Wf As WorksheetFunction
Dim vR() As Variant
Dim i As Long, n As Long
Set Wf = WorksheetFunction
Set rngOrg = Range("a1", Range("a" & Rows.Count).End(xlUp))
Set rngDB = Range("b1", Range("b" & Rows.Count).End(xlUp))
n = rngDB.Rows.Count
ReDim vR(1 To n, 1 To 1)
For Each Rng In rngDB
i = i + 1
If Wf.CountIf(rngOrg, Rng) Then
vR(i, 1) = Wf.CountIf(Range("b1", Rng), Rng)
End If
Next Rng
Range("c1").Resize(n) = vR
End Sub
Here is another method, this time using Find. This is probably quicker than the looping method since it leverages the in-built find function to skip to the next match.
I've commented the code below for clarity, but basically we loop through values in column A (using a For loop because they're less prone to disguised infinite looping than While) and look for them in column B.
Note: This looks a bit longer, but that's mainly because (a) I've added lots of comments and (b) I've used a With statement to ensure the ranges are fully qualified.
Sub countdb()
Dim c As Range, fnd As Range, listrng As Range, cnt As Long, addr As String
' Use with so that our ranges are fully qualified
With ThisWorkbook.Sheets("Sheet1")
' Define the range to look up in (column B in this case)
Set listrng = .Range("B1", .Range("B1").End(xlDown))
' Loop over values in the index range (column
For Each c In .Range("A1", .Range("A1").End(xlDown))
cnt = 0
' Try and find the c value
Set fnd = listrng.Find(what:=c.Value, lookat:=xlWhole, LookIn:=xlValues, after:=listrng.Cells(listrng.Cells.Count))
If Not fnd Is Nothing Then
' Store the address of the first find so we can stop when we find it again!
addr = fnd.Address
' Loop over all other matches in the range. By using a "Do ... Loop While"
' style loop, we ensure that the loop is run at least once!
Do
' Increase count and assign value to next column
cnt = cnt + 1
fnd.Offset(0, 1).Value = cnt
' Find next match after current
Set fnd = listrng.Find(what:=c.Value, lookat:=xlWhole, LookIn:=xlValues, after:=fnd)
Loop While fnd.Address <> addr
End If
Next c
End With
End Sub
The trick is in making the declarations transparent. After that the programming is very easy.
Sub CountMatches()
Dim Rng As Range ' "count" range (= column "B")
Dim Itm As String ' item from the "items' column (= "A")
Dim Rla As Long, Rlb As Long ' last row in columns A and B
Dim Ra As Long, Rb As Long ' row counters
Dim Counter As Long ' count matches
With ActiveSheet
' look for the last used rows
Rla = .Cells(.Rows.Count, "A").End(xlUp).Row
Rlb = .Cells(.Rows.Count, "B").End(xlUp).Row
' start looking for matches from row 2
Set Rng = .Range(.Cells(2, "B"), .Cells(Rlb, "B"))
' start looping in row 2
For Ra = 2 To Rla
Itm = .Cells(Ra, "A").Value
If Len(Trim(Itm)) Then ' skip if blank
' start comparing from row 2
For Rb = 2 To Rlb
' compare not case sensitive
If StrComp(.Cells(Rb, "B").Value, Itm, vbTextCompare) = 0 Then
Counter = Counter + 1
End If
Next Rb
.Cells(Ra, "C").Value = Counter
Counter = 0
End If
Next Ra
End With
End Sub
Now the question is whether the transparency that workred for me appears transparent to you. I hope it does. :-)
This should be significantly faster.
Sub CountMatches_2()
Dim Rng As Range ' "count" range (= column "B")
Dim Itm As String ' item from the "items' column (= "A")
Dim Rla As Long, Rlb As Long ' last row in columns A and B
Dim Ra As Long, Rb As Long ' row counters
With ActiveSheet
' look for the last used rows
Rla = .Cells(.Rows.Count, "A").End(xlUp).Row
Rlb = .Cells(.Rows.Count, "B").End(xlUp).Row
' start looking for matches from row 2
Set Rng = .Range(.Cells(2, "B"), .Cells(Rlb, "B"))
' start looping in row 2
For Ra = 2 To Rla
Itm = .Cells(Ra, "A").Value
If Len(Trim(Itm)) Then ' skip if blank
.Cells(Ra, "C").Value = Application.CountIf(Rng, Itm)
End If
Next Ra
End With
End Sub
This code presumes that each item in column A is unique. If it is not duplicates will be created which, however, it would be easy to eliminate either before or after they are created.

Excel VBA row copy into archive

I am trying to copy a whole bunch of rows into an archive with VBA. (VBA noob here). I have two sheets, 1 called Active, and the Archive. In active I have a column called Job Number which is what the other data is based off of. I need to copy all the rows where job numbers is not blank and copy each row individually over to my archive sheet where a empty row is available. and then clear the job number cell.
At the bottom is what i have so far. Basically i need to take all rows that have have an nonempty cell in column e.( i tried to count rows until last non empty records --> NumRows) also find the location of the last non empty variable in Archive (lr2) and then i tried a for loop to copy over but it doesn't do anything. Any help would be awesome.
Sub Archiver()
Dim lr As Long, lr2 As Long, r As Long, NumRows As Integer
lr2 = Sheets("Archive").Cells(Rows.Count, "E").End(xlUp).Row
NumRows = Range("A1", Range("A1").End(xlDown)).Rows.Count
For r = 5 To NumRows
Rows(r).Copy Destination:=Sheets("Archive").Range("A" & lr2 + 1)
lr2 = Sheets("Archive").Cells(Rows.Count, "E").End(xlUp).Row
Next r
End Sub
Try this:
Sub Archiver()
Dim lr2 As Long
Dim y As Worksheet, z As Worksheet
Dim copyRng As Range
Set y = Sheets("Active")
Set z = Sheets("Archive")
lr2 = y.Cells(rows.Count, "E").End(xlUp).Row
'Here you select the range of your sheet you want to copy, for me is A:E
Set copyRng = y.Range("A2:E" & lr2)
z.Range("A2:E" & copyRng.rows.Count + 1).Insert Shift:=xlDown
copyRng.Copy z.Range("A2")
y.Range("A2:E" & lr2).ClearContents
End Sub

Excel VBA - Why does this macro delete everything

I need some help with this macro. I have a workbook that is formatted pretty poorly, but consistently every time I open it. Among other things, the goal is to find the non-blank cells in column B and delete the entire 2 rows below and the 1st row above each of those populated B cells.
The first loop I have in the code works just the way I want it to, but the second loop seems to only work on the 1st instance of a populated B cell, but then it deletes everything else above it, like 500 cells worth of data.
Can someone explain to me why this is happening, and if you could find a way to combine both of those for loops into 1, that would be nice too.
Sub test()
Dim currentSht As Worksheet
Dim startCell As Range
Dim lastRow As Long, lastCol As Long
Dim colNames As Variant
Dim i As Integer, j As Integer
Set currentSht = ActiveWorkbook.Sheets(1)
Set startCell = currentSht.Range("A1")
lastRow = startCell.SpecialCells(xlCellTypeLastCell).Row
lastCol = startCell.SpecialCells(xlCellTypeLastCell).Column
For i = lastRow To 1 Step -1
If currentSht.Cells(i, "B").Value <> "" Then
currentSht.Cells(i, "B").Offset(1).EntireRow.Delete
End If
Next i
Range("D3").Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Delete Shift:=xlUp
currentSht.Rows("1:1").EntireRow.Delete
currentSht.Range("c:d, f:g, i:k").EntireColumn.Delete
currentSht.Range("A:D").Columns.AutoFit
For j = lastRow To 2 Step -1
If currentSht.Cells(j, "B").Value <> "" Then
currentSht.Range(Cells(j, "B").Offset(-1), Cells(j, "B").Offset(-3)).EntireRow.Delete
End If
Next j
End Sub
Thank you
The second loop deletes everything because upon deletion of the lines above the found value, said value gets moved up and will be found again, triggering another deletion. To fix this, the quickest way would be to skip the next two lines by modifying j:
For j = lastRow To 2 Step -1
If currentSht.Cells(j, "B").Value <> "" Then
currentSht.Range(Cells(j, "B").Offset(-1), Cells(j, "B").Offset(-3)).EntireRow.Delete
j = j - 2
End If
Next j
It really doesn't matter much if you are looping from top to bottom or vice versa. The only difference would be if there are two entries in column B near each other. In that case, the search order would determine which one is deleted. But is deletion really what you want? Maybe you could .Clear the contents of the rows instead of deleting them.
edit: here's the new code a bit cleaned up
Sub test()
Dim currentSht As Worksheet
Dim startCell As Range
Dim lastRow As Long, lastCol As Long
Dim colNames As Variant
Dim i As Integer, j As Integer
Set currentSht = ActiveWorkbook.Sheets(1)
Set startCell = currentSht.Range("A1")
lastRow = startCell.SpecialCells(xlCellTypeLastCell).Row
lastCol = startCell.SpecialCells(xlCellTypeLastCell).Column
For i = lastRow To 1 Step -1
If currentSht.Cells(i, "B").value <> "" Then
'reference the row directly
currentSht.Rows(i + 1).Delete
End If
Next i
'Do not use selection if you can avoid it
Range("D3", Range("D3").End(xlToRight)).Delete Shift:=xlUp
currentSht.Rows(1).Delete
currentSht.Range("C:D, F:G, I:K").Delete
currentSht.Range("A:D").Columns.AutoFit
For j = lastRow To 2 Step -1
If currentSht.Cells(j, "B").value <> "" Then
currentSht.Rows(j - 1).Delete
currentSht.Rows(j - 2).Delete
j = j - 2
End If
Next j
End Sub
If you want to combine the loops the behavior of the macro will change because of the deletions that happen between the loops.

Excel VBA delete rows based on multiple column criteria

I am trying to do some cuts to a sheet of data based on if a row meets 2 criteria in different columns, i.e. if the value in column D is > -2 and if the value in the adjacent cell of column F is > -2 or NA, then delete the entire row. If only 1 or none of the criteria is met then it should keep the row. Below is what i have so far. When i run the macro, it will go on forever, but I don't see how this should be since it doesn't look like an endless loop to me (to be fair i have only let it sit for 45 minutes, but there is only around 15,000 data rows so it shouldn't take longer than 10 minutes realistically). Any help would be greatly appreciated.
Sub Cuts()
Dim wb1 As Workbook, sh1 As Worksheet
Dim lastrow1 As Long, lastrow2 As Long
Set wb1 = Workbooks(“ovaryGisticARRAYRNAseq.final.xlsx")
Set sh1 = wb1.Sheets(“Cuts”)
lastrow1 = sh1.Cells(Rows.Count, 4).End(xlUp).Row
lastrow2 = sh1.Cells(Rows.Count, 6).End(xlUp).Row
For i = 1 To lastrow1
For j = 1 To lastrow2
If sh1.Cells(i, 4).Value > -2 Then
If sh1.Cells(j, 6).Value > -2 Then
sh1.Cells(j, 6).EntireRow.Delete
ElseIf sh1.Cells(j, 6).Value = “NA” Then
sh1.Cells(j, 6).EntireRow.Delete
End If
End If
Next j
Next i
End Sub
I'm not sure how you want to handle blank cells or text in column headings but I would propose this modification.
Sub Cuts()
Dim wb1 As Workbook
Dim lr As Long, i As Long
Set wb1 = Workbooks(“ovaryGisticARRAYRNAseq.final.xlsx")
With wb1.Sheets("Cuts")
lr = Application.Max(.Cells(Rows.Count, 4).End(xlUp).Row, _
.Cells(Rows.Count, 6).End(xlUp).Row)
For i = lr To 1 Step -1
If .Cells(i, 4).Value > -2 And _
(.Cells(i, 6).Value > -2 Or UCase(.Cells(i, 6).Value) = "NA") Then
.Rows(i).EntireRow.Delete
End If
Next i
End With
End Sub