Copy Row X amount of times based on cell value - vba

The macro copies and pastes the values of a row X amount of times based on a cell value in M2. It pastes the exact numbers over and over. Is there a way to change it so that numbers will ascend as they are copied down?
E.g. if A2 contains "hello 3", after running the macro A3 will contain "hello 4", A4 will contain "hello 5".
Sub Sample()
Dim wsI As Worksheet, wsO As Worksheet
Dim lRow_I As Long, lRow_O As Long, i As Long, j As Long
'~~> Set your input and output sheets
Set wsI = ThisWorkbook.Sheets("Sheet1")
Set wsO = ThisWorkbook.Sheets("Sheet1")
'~~> Output row
lRow_O = wsO.Range("A" & wsO.Rows.Count).End(xlUp).Row + 1
With wsI
'~~> Get last row of input sheet
lRow_I = .Range("A" & .Rows.Count).End(xlUp).Row
'~~> Loop through the rows
For i = 2 To lRow_I
'~~> This will loop the number of time required
'~~> i.e the number present in cell M
For j = 1 To Val(Trim(.Range("M" & i).Value))
'~~> This copies
.Rows(i).Copy wsO.Rows(lRow_O)
'~~> Get the next output row
lRow_O = wsO.Range("A" & wsO.Rows.Count).End(xlUp).Row + 1
Next j
Next i
End With
End Sub
Example of how input screen and output screen should look:
Example of how output screen should look:

Actually no need for j loop if you use resize method.
Sub Sample()
Dim wsI As Worksheet, wsO As Worksheet, lCounter As Long
Dim lRow_I As Long, lRow_O As Long, i As Long
Set wsI = ThisWorkbook.Sheets("Sheet1")
Set wsO = ThisWorkbook.Sheets("Sheet2")
With wsI
lCounter = Val(Trim(.Range("M" & i).Value))
lRow_I = .Range("A" & .Rows.Count).End(xlUp).Row
For i = 2 To lRow_I
lRow_O = wsO.Range("A" & wsO.Rows.Count).End(xlUp).Row + 1
.Rows(i).Copy wsO.Rows(lRow_O).Resize(lCounter)
Next i
End With

I upgrade my solution to have the "counter" incremented
Sub Sample()
Dim wsI As Worksheet, wsO As Worksheet
Dim lRow_I As Long, lRow_O As Long, i As Long, nRowsToPaste As Long
Dim rngToCopy As Range, rngToPaste As Range
'~~> Set your input and output sheets
Set wsI = ThisWorkbook.Sheets("SheetI")
Set wsO = ThisWorkbook.Sheets("SheetO") '<=== I made it different that wsI
'~~> Output row
lRow_O = wsO.Range("A" & wsO.Rows.Count).End(xlUp).row + 1
With wsI
'~~> Get last row of input sheet
lRow_I = .Range("A" & .Rows.Count).End(xlUp).row
'~~> Loop through the rows
For i = 2 To lRow_I
nRowsToPaste = val(Trim(.Range("M" & i).Value)) '<== set number of rows to be pasted
Set rngToCopy = .Range(.Cells(i, 1), .Cells(i, wsI.Columns.Count).End(xlToLeft)) '<== set range to be copied
Set rngToPaste = wsO.Rows(lRow_O).Resize(1, rngToCopy.Columns.Count) '<== set 1st row of the range to be pasted
rngToCopy.Copy rngToPaste '<== copy&paste the 1st row in wsO sheet '<== copy and paste the 1st row
Call Prefix(rngToPaste) '<== differentiate each single cell of pasted range by means of adding a different prefix. this will subsequently have autofill method work on cells with originally the same value as well
With rngToPaste
.AutoFill .Resize(nRowsToPaste + 1) ' <== fill all rows exploiting AutoFill method, which will work on every column being their 1st row different from each other
.Resize(nRowsToPaste + 1).Replace What:="%%*%%", Replacement:="", LookAt:=xlPart '<== remove prefix
End With
lRow_O = lRow_O + nRowsToPaste + 1 '<== GET the next output row
Next i
End With
End Sub
Sub Prefix(rng As Range)
Dim j As Long
With rng
For j = 1 To .Columns.Count
.Cells(1, j).Value = "%%" & j & "%%" & .Cells(1, j).Value
Next j
End With
End Sub
where it eliminates the need of the inner j-loop and simply upgrades the lRow_O

Related

Copy range A:AM in sheet 3 instead of entire row and paste in sheet1 (A1) one below the other

How do I make a change in the code so that it copies only range A:AM in sheet3 till the last row in column D, instead of copying the entire row and paste it in Sheet1 (A1) one below the other?
Option Explicit
Public Sub ABC()
Dim LastRow As Long
LastRow = Sheets(3).Cells(Rows.Count, "d").End(xlUp).Row
Dim iRow As Long
For iRow = 7 To LastRow
If Application.WorksheetFunction.CountA(Sheets(3).Range("J" & iRow & ":AM" & iRow)) <> 0 Then
Sheets(3).Rows(iRow).Copy Destination:=Sheets(1).Cells(Rows.Count, 1).End(xlUp).Offset(1, 0)
End If
Next iRow
End Sub
Like this:
Sheets(3).Range("A" & iRow & ":AM" & iRow).Copy
or
Sheets(3).Cells(irow, "A").Resize(1, 39).Copy
or
Sheets(3).Range("A:AM").Rows(iRow).Copy
If your goal is to replace the loop with a entire range copy/paste all at once then:
Create a variable to store the last row index based off column D and then just create your dynamic range to copy accordingly
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim lr As Long
lr = ws.Range("D" & ws.Rows.Count).End(xlUp).Row '<-- Find Last Row
ws.Range("A1:AM" & lr).Copy '<-- Copy
Sheets("Sheet2").Range("A1").PasteSpecial xlPasteValues '<-- Paste

Lock Entire Row Based On Date

I have Cell A1 with Month mentioned. I am trying to compare date in A2:last cell and wherever date > A1, I want the row to be unlocked, otherwise locked. The below code doesn't work"
Sub Lockrow()
Dim DestSh As Worksheet
Dim lastrow As Long
Dim i As Integer
Set DestSh = Sheets("Consultant & Volunteer")
With DestSh
'finds the last row with data on A column
lastrow = .Range("A" & .Rows.Count).End(xlUp).Row
'parse all rows
For i = 6 To lastrow
'if your conditions are met
If Month(.Cells(i, 26)) > Month(.Cells(1, 1)) Then
.Range("A" & i).EntireRow.Cells.Locked = True 'lock the row
End If
Next i
End With
End Sub
This can be done simply with below, but you have to be careful that Year doesn't change... Also the lastrow should be on Column Z.
Also, if the worksheet isn't Protected, there is no effect.
Option Explicit
Sub Lockrow()
Dim DestSh As Worksheet
Dim lastrow As Long
Dim i As Long ' Integer
Set DestSh = Sheets("Consultant & Volunteer")
With DestSh
'finds the last row with data on A column
lastrow = .Range("Z" & .Rows.Count).End(xlUp).Row ' <-- EDIT
'parse all rows
For i = 6 To lastrow
'if your conditions are met
.Rows(i).Locked = Not (Month(.Cells(i, "Z")) > Month(.Range("A1")))
' If Month(.Cells(i, 26)) > Month(.Cells(1, 1)) Then
' .Range("A" & i).EntireRow.Cells.Locked = True 'lock the row
' End If
Next i
.Protect UserInterfaceOnly:=True
End With
Set DestSh = Nothing
End Sub
Alternative to loop.
Dim r As Range, DestSh As Worksheet, lastrow As Long
Set DestSh = Sheets("Consultant & Volunteer")
With DestSh
lastrow = .Range("A" & .Rows.Count).End(xlUp).Row
Set r = .Range("A1:A" & lastrow)
r.EntireRow.Locked = False
r.AutoFilter 1, ">" & .Range("A1").Value2
r.SpecialCells(xlCellTypeVisible).EntireRow.Locked = True
.AutoFilterMode = False
.Protect UserInterfaceOnly:=True
End With

Excel VBA: Find parameter, copy found row to another Worksheet (pasting starting at specific cell)

Having 2 Worksheets (UPDATED,CHANGES) each one have parameters in each column in variable order
UPDATED Worksheet has the columns:
Name / Value / Units
CHANGES Worksheet has the columns:
Status / Name / Value / Units
So first I search in CHANGES a row with the Status: CHANGE
Then I need to get the Name of that row
To find it in UPDATED and copy the whole Row
And copy it in the position after the column Status where the name was found
Each Name is unique but as I mentioned before has a variable position, my code so far:
Sub CopyRealChange()
Dim sh1 As Worksheet, sh2 As Worksheet
Dim lr As Long, r As Long, x As Long
Dim chng As Range
Set sh1 = ThisWorkbook.Worksheets("UPDATED")
Set sh2 = ThisWorkbook.Worksheets("CHANGES")
lr = sh2.Cells(Rows.Count, "A").End(xlUp).Row
x = 2
For r = 2 To lr
If Range("A" & r).Value = "CHANGE" Then 'Evaluate the condition.
'Sh2.Range("B" & x).Value = Sh1.Range("B" & r).Value 'Copy same Column location
'FIND
With Worksheets(2).Range("a1:a1000")
Set chng = .Find(sh2.Range("B" & x).Value, LookIn:=xlValues)
If chng Is Nothing Then
sh1.Range(c).EntireRow.Copy Destination:=sh2.Range("B" & x)
End If
End With
'FIND
End If
x = x + 1
Next r
End Sub
Concerns with the code show an error at this line (in the FIND)
sh1.Range(c).EntireRow.Copy Destination:=sh2.Range("B" & x)
There are a few issues with your code.
You didn't declare with which worksheet the range you are searching for is located.
If Range("A" & r).Value = "CHANGE" Then
You declared the worksheets in the beginning then change how you reference them in the code.
Set sh2 = ThisWorkbook.Worksheets("CHANGES")
With Worksheets(2).Range("a1:a1000")
Here is what I've got for you: using simple loops checking to see if values match and move data.
Sub CopyRealChange()
Dim sh1 As Worksheet, sh2 As Worksheet
Dim tempName As String
Dim lastRow1 As Long
Dim lastRow2 As Long
Set sh1 = ActiveWorkbook.Worksheets("UPDATED")
Set sh2 = ActiveWorkbook.Worksheets("CHANGES")
lastRow1 = sh1.Cells(Rows.Count, "A").End(xlUp).Row 'Get last row for both sheets
lastRow2 = sh2.Cells(Rows.Count, "A").End(xlUp).Row 'because you are searching both
For s2Row = 2 To lastRow2 'Loop through "CHANGES"
If sh2.Cells(s2Row, 1).Value = "CHANGE" Then
tempName = sh2.Cells(s2Row, 2).Value 'extra step for understanding concept
'There is a match, so now
For s1Row = 2 To lastRow1 'Search through the other sheet
If sh1.Cells(s1Row, 1).Value = tempName Then
sh2.Cells(s2Row, 3).Value = sh1.Cells(s1Row, 2).Value 'Copy Values
sh2.Cells(s2Row, 4).Value = sh1.Cells(s1Row, 3).Value
End If
Next s1Row
End If
Next s2Row
End Sub

Pasting in the next empty cell in a row using excel vba

I am writing a macro that copies a value from one excel worksheet, and pastes it into another. As seen below, I have a code that correctly copies and pastes my value into the correct worksheet, but I want it to paste into the next empty cell in row 3, instead of just cell "C3". All help is appreciated.
Private Sub CommandButton1_Click()
Dim wsMaster As Worksheet, wbDATA As Workbook
Dim NextRow As Long, LastRow As Long
Dim vMax As Variant
Set wsMaster = ThisWorkbook.Sheets("Contract Metrics")
NextRow = wsMaster.Range("A" & Rows.Count).End(xlUp).Row + 1
Set wbDATA = Workbooks.Open("C:\Documents and Settings\Michael Palkovitz\My Documents\Test\Contracts Metrics.xlsx")
With wbDATA.Sheets("Contract Task Summary(1)")
LastRow = .Range("C" & .Rows.Count).End(xlUp).Row
If LastRow > 1 Then
.Range("C" & LastRow).Copy
wsMaster.Range("C" & 3).PasteSpecial xlPasteValues
wsMaster.Range("C" & 3).PasteSpecial xlPasteFormats
End If
End With
wbDATA.Close False
End Sub
This is the code you are looking for:
Private Sub CommandButton1_Click()
Dim wsMaster As Worksheet, wbDATA As Workbook
Dim NextRow As Long, LastRow As Long
Dim vMax As Variant
Dim columnToPaste As Integer
Dim lastColumnToPaste As Integer
Dim lastColumn as Integer
Set wsMaster = ThisWorkbook.Sheets("Contract Metrics")
NextRow = wsMaster.Range("A" & Rows.Count).End(xlUp).Row + 1
Set wbDATA = Workbooks.Open("C:\Documents and Settings\Michael Palkovitz\My Documents\Test\Contracts Metrics.xlsx")
With wbDATA.Sheets("Contract Task Summary(1)")
LastRow = .Range("C" & .Rows.Count).End(xlUp).Row
If LastRow > 1 Then
.Range("C" & LastRow).Copy
lastColumn = 3
lastColumnToPaste = lastColumn + 20
columnToPaste = lastColumn - 1
Do
columnToPaste = columnToPaste + 1
If IsEmpty(wsMaster.Cells(lastRow, columnToPaste)) Then
wsMaster.Cells(lastRow, columnToPaste).PasteSpecial xlPasteValues
wsMaster.Cells(lastRow, columnToPaste).PasteSpecial xlPasteFormats
Exit Do
End If
Loop While (columnToPaste < lastColumnToPaste)
End If
End With
wbDATA.Close False
End Sub
This is just a basic approach to how the problem should be solved. You should update some values dynamically (e.g., maximum row to check, given by the variable lastRowToPaste).
Note that writing/pasting between two different workbooks is very inefficient. In case of having to repeat this process for a long enough time, I would: open the input spreadsheet and store all the values in a temporary location (depending upon the size, in an array or in a temporary file), close it; open the destination spreadsheet and write the data from this location (without relying on copy/paste). This is a much faster approach to the problem.

VBA macro code for listing names

I have an Excel sheet with names as one column and their working hours as values in next column.
I want to copy names with values greater than 40 to new sheet without any blanks in columns. The new sheet should have both names and the working hours; any text in the values column should be ignored.
Sub CopyCells()
Dim sh1 As Worksheet, sh2 As Worksheet
Dim j As Long, i As Long, lastrow1 As Long
Set sh1 = Worksheets("Sheet1")
Set sh2 = Worksheets("Sheet2")
lastrow1 = sh1.Cells(Rows.Count, "F").End(xlUp).Row
For i = 1 To lastrow1
If sh1.Cells(i, "F").Value > 20 Then
sh2.Range("A" & i).Value = sh1.Cells(i, "F").Value
End If
Next i
End Sub
I would recommend using AutoFilter to copy and paste as it is faster than looping. See the example below.
My Assumptions
Original Data is in Sheet 1 as shown the snapshot below
You want the output in Sheet 2 as shown the snapshot below
CODE
I have commented the code so that you will not have a problem understanding it.
Option Explicit
Sub Sample()
Dim wsI As Worksheet, wsO As Worksheet
Dim lRow As Long
'~~> Set the input sheet
Set wsI = Sheets("Sheet1"): Set wsO = Sheets("Sheet2")
'~~> Clear Sheet 2 for output
wsO.Cells.ClearContents
With wsI
'~~> Remove any existing filter
.AutoFilterMode = False
'~~> Find last row in Sheet1
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
'~~> Filter Col B for values > 40
With .Range("A1:B" & lRow)
.AutoFilter Field:=2, Criteria1:=">40"
'~~> Copy the filtered range to Sheet2
.SpecialCells(xlCellTypeVisible).Copy wsO.Range("A1")
End With
'~~> Remove any existing filter
.AutoFilterMode = False
End With
'~~> Inform user
MsgBox "Done"
End Sub
SNAPSHOT
Try rhis
Sub CopyCells()
Dim sh1 As Worksheet, sh2 As Worksheet
Dim j As Long, i As Long, lastrow1 As Long
Set sh1 = Worksheets("Sheet1")
Set sh2 = Worksheets("Sheet2")
lastrow1 = sh1.Cells(Rows.Count, "F").End(xlUp).Row
j = 1
For i = 1 To lastrow1
If Val(sh1.Cells(i, "F").Value) > 20 Then
sh2.Range("A" & j).Value = sh1.Cells(i, "F").Value
j = j + 1
End If
Next i
End Sub