I have two similar macros I've written, and for efficiency's sake I'd like to consolidate them into one. The first macro adds 4 blank rows on another tab underneath a specific row, where column C matches certain criteria. The second macro copies 4 rows of data from an existing tab over to the new tab, and pastes that data into the 4 newly created blank rows. Any help would be greatly appreciated! Thank you
Conceptual screenshots attached:
Screenshot 1: Initial State
Screenshot 2: MACRO 1 inserts 4 rows if criteria in column C is met (in this case value = "Part A"
Screenshot 3: MACRO 2 pulls in row data from another sheet and pastes it into the new blank rows on this sheet
FIRST MACRO:
Sub RowAdder_01()
Dim Col As Variant
Dim BlankRows As Long
Dim LastRow As Long
Dim R As Long
Dim StartRow As Long
Dim PN_01 As Range
Set PN_01 = Range("M17")
Col = "C"
StartRow = 1
BlankRows = 1
LastRow = Cells(Rows.Count, Col).End(xlUp).Row
Application.ScreenUpdating = False
With Worksheets("NEW SHEET")
For R = LastRow To StartRow + 1 Step -1
If .Cells(R, Col) = PN_01 Then
.Cells(R + 1, Col).EntireRow.Insert Shift:=xlDown
.Cells(R + 1, Col).EntireRow.Insert Shift:=xlDown
.Cells(R + 1, Col).EntireRow.Insert Shift:=xlDown
.Cells(R + 1, Col).EntireRow.Insert Shift:=xlDown
End If
Next R
End With
Application.ScreenUpdating = True
End Sub
SECOND MACRO:
Sub PasteRowData_01()
Dim Col As Variant
Dim BlankRows As Long
Dim LastRow As Long
Dim R As Long
Dim StartRow As Long
Dim PN_01 As Range
Set PN_01 = Range("M17")
Col = "C"
Drop = "A"
StartRow = 1
BlankRows = 1
LastRow = Cells(Rows.Count, Col).End(xlUp).Row
Application.ScreenUpdating = False
Sheets("OLD SHEET").Rows("54:57").SpecialCells(xlCellTypeVisible).Select
Selection.Copy
With Worksheets("NEW SHEET")
For R = LastRow To StartRow + 1 Step -1
If .Cells(R, Col) = PN_01 Then
Sheets(NEW SHEET).Select
.Cells(R + 1, Drop).Select
Selection.PasteSpecial
End If
Next R
End With
Application.ScreenUpdating = True
End Sub
Please try this code.
Option Explicit
Sub AddAndPaste()
Dim Ws As Worksheet
Dim Arr As Variant
Dim PN_01 As Variant
Dim Last As Long ' column or row
Dim R As Long
' copy from source
Set Ws = Worksheets("Old Sheet")
With Ws
With .UsedRange
Last = .Columns.Count + .Column - 1
End With
Arr = Range(.Cells(54, 1), .Cells(57, Last)).SpecialCells(xlCellTypeVisible).Value
End With
Application.ScreenUpdating = False
' paste to destination
Set Ws = Worksheets("New Sheet")
With Ws
PN_01 = .Cells(7, "M").Value
Last = .Cells(.Rows.Count, "C").End(xlUp).Row
For R = Last To 1 Step -1
If .Cells(R, "C").Value = PN_01 Then
With .Cells(R, "A")
.Resize(4, 1).EntireRow.Insert Shift:=xlDown
.Offset(-4).Resize(UBound(Arr, 1), UBound(Arr, 2)).Value = Arr
End With
Exit For ' don't exit if you need to continue looping
End If
Next R
End With
Application.ScreenUpdating = True
End Sub
Your problem is caused by inserting a line. We recommend using an array.
Sub test()
Dim Ws As Worksheet, newWs As Worksheet, Temp As Worksheet
Dim vDB, vSp, vR()
Dim i As Long, r As Long, n As Long, k As Integer, cnt As Integer
Dim PN_01 As Range
Set newWs = Sheets("New Sheet")
Set oldWs = Sheets("OLD SHEET")
Set Temp = Sheets.Add
oldWs.Range("a54:d57").SpecialCells(xlCellTypeVisible).Copy Temp.Range("a1")
vSp = Temp.UsedRange
Application.DisplayAlerts = False
Temp.Delete
Application.DisplayAlerts = True
With newWs
vDB = .Range("a1", "d" & .Range("a" & Rows.Count).End(xlUp).Row)
Set PN_01 = .Range("M17")
End With
cnt = UBound(vSp, 1)
r = UBound(vDB, 1)
For i = 1 To r
n = n + 1
ReDim Preserve vR(1 To 4, 1 To n)
For j = 1 To 4
vR(j, n) = vDB(i, j)
Next j
If vDB(i, 3) = PN_01 Then
For k = 1 To cnt
n = n + 1
ReDim Preserve vR(1 To 4, 1 To n)
For j = 1 To 4
vR(j, n) = vSp(k, j)
Next j
Next k
End If
Next i
newWs.Range("a1").Resize(n, 4) = WorksheetFunction.Transpose(vR)
newWs.Activate
End Sub
Related
I have the issue with stacking in the loop
The macro should combine all columns (changeable number of rows) into one column.
Sub CombineColumns()
Dim xRng As Range
Dim i As Integer
Dim xLastRow As Integer
On Error Resume Next
Set xRng = Application.Range("A1", Range("A1").End(xlDown).End(xlToRight))
xLastRow = xRng.Columns(1).Rows.Count + 1
For i = 2 To xRng.Columns.Count
Range(xRng.Cells(1, i), xRng.Cells(xRng.Columns(i).Rows.Count, i)).Cut
ActiveSheet.Paste Destination:=xRng.Cells(xLastRow, 1)
xLastRow = xLastRow + xRng.Columns(i).Rows.Count
Next
End Sub
Using Array is simple and fast.
Sub test()
Dim Ws As Worksheet, toWS As Worksheet
Dim vDB, vR()
Dim i As Long, j As Integer, n As Long
Set Ws = ActiveSheet
vDB = Ws.UsedRange
r = UBound(vDB, 1)
c = UBound(vDB, 2)
For i = 1 To r
For j = 1 To c
n = n + 1
ReDim Preserve vR(1 To n)
vR(n) = vDB(i, j)
Next j
Next i
Set toWS = Sheets.Add ' set toWs = Sheets(2) ~~> set your sheet
With toWS
.Cells.Clear
.Range("a1").Resize(n) = WorksheetFunction.Transpose(vR)
End With
End Sub
If I got you right you want to do sth. like that
Option Explicit
Sub CombineColumns()
Dim xRng As Range
Dim i As Long
Dim xLastRow As Long
'On Error Resume Next
Set xRng = Application.Range("A1", Range("A1").End(xlToRight))
xLastRow = lastRow(1) + 1
For i = 2 To xRng.Columns.Count
Range(xRng.Cells(1, i), xRng.Cells(lastRow(i), i)).Cut
ActiveSheet.Paste Destination:=xRng.Cells(xLastRow, 1)
xLastRow = lastRow(1) + 1
Next
End Sub
Function lastRow(col As Long, Optional wks As Worksheet) As Long
If wks Is Nothing Then
Set wks = ActiveSheet
End If
lastRow = wks.Cells(wks.Rows.Count, col).End(xlUp).Row
End Function
The code still needs some improvement as it might loop over all columns espeically if there is no data.
This assumes on all your columns you have data on the 2nd row, to correctly identify the last column.
Option Explicit
Public Sub CombineColumns()
Dim LastColumn As Long, LastRow As Long, LastRowA As Long, i As Long, RngAddress As String
With ActiveSheet
' This assumes you have data on row 2 on all columns
LastColumn = .Cells(2, .Columns.Count).End(xlToLeft).Column
For i = 2 To LastColumn
' Get the last row of Col A on each iteration
LastRowA = .Cells(.Rows.Count, 1).End(xlUp).Row + 1
' Get last row of the Col we're checking
LastRow = .Cells(.Rows.Count, i).End(xlUp).Row
' Get the used range address of the current Col
RngAddress = .Range(.Cells(1, i), Cells(LastRow, i)).Address
' Check if we have blank cells among the rows of the current Col
.Range(.Cells(1, i), Cells(LastRow, i)).Value2 = Evaluate("IF(NOT(ISBLANK(" & RngAddress & "))," & RngAddress & ")")
' Compress data (if there's no empty cells in the current Col the below line will give error, that's the role of err handling)
On Error Resume Next
.Range(.Cells(1, i), Cells(LastRow, i)).SpecialCells(xlCellTypeConstants, 4).Delete xlShiftUp
On Error GoTo 0
' Update the last row in case we compressed data
LastRow = .Cells(.Rows.Count, i).End(xlUp).Row
' Paste data in Col A
.Range(.Cells(1, i), Cells(LastRow, i)).Cut Destination:=.Range("A" & LastRowA)
Next i
Application.CutCopyMode = False
End With
End Sub
Maybe this could be a convenient solution for you :
Sub CombineColumns()
Dim LastRow As Long
LastRow = ActiveSheet.UsedRange.Rows(ActiveSheet.UsedRange.Rows.Count).Row
Range("A2:A" & LastRow).Formula = "=B2 & C2 & D2 & E2 & F2 & G2 & H2" 'Insert here the columns you need to be combined
End Sub
Let me know if changes are necessary.
I am wondering if someone can help me with this question. I have written a macro with the objective of deleting selected rows based upon whether or not all cells in a row contain the value "<0.01". The problem is when the program tries to process the if statement it errors out.
Any help would be appreciated.
Sub deleteRows()
Dim rng As Long
Dim FirstCol, LastCol As Long
Set UsedRng = ActiveSheet.UsedRange
FirstCol = UsedRng(1).Column
LastCol = UsedRng(UsedRng.Cells.Count).Column
rng = Application.Selection.Rows.Count
For i = rng To 1 Step -1
if Range(Cells(i, FirstCol), Cells(i, LastCol)) = "<0.01" Then
Rows(i).EntireRow.Delete
End If
Next i
End Sub
New code that i wrote
`Sub for3()
Dim ma, r, c As Range
Dim counter As Long
Dim deletenum As Long
Dim firstcol As Variant
Set ma = Application.Selection
Set r = ma.Rows
Set c = ma.Columns
counter = 0
deletenum = c.Count
firstcol = ma(1).Column
For Each r In ma
For Each c In r
If c.Column = firstcol Then
counter = 0
End If
If c.Text = "<0.01" Then
counter = counter + 1
End If
If counter = deletenum Then
r.EntireRow.Delete
ma.Offset(1, 0).Activate
End If
Next c
Next r
End Sub
`
You can use the Find function per row instead:
Dim FndRng As Range
For i = rng To 1 Step -1
Set FndRng = Range(Cells(i, FirstCol), Cells(i, LastCol)).Find(What:="<0.01", LookIn:=xlValues, LookAt:=xlWhole)
If Not FndRng Is Nothing Then ' find was successful
Rows(i).Delete
End If
Next
Edit 1: check that all cells in row equal to "<0.01".
For i = rng To 1 Step -1
If WorksheetFunction.CountIf(Range(Cells(i, FirstCol), Cells(i, LastCol)), "<0.01") = Range(Cells(i, FirstCol), Cells(i, LastCol)).Cells.Count Then
Rows(i).Delete
End If
Next I
Edit 2:
Option Explicit
Sub t()
Dim Rng As Range
Dim firstCol As Long, LastCol As Long
Dim firstRow As Long, LastRow As Long
Dim i As Long
Dim C As Range
Set Rng = Selection ' only if you realy need to
' calculate the first and last column of the Selection
firstCol = Rng(1).Column
LastCol = Rng.Columns.Count + firstCol - 1
' calculate the first and last Row of the Selection
firstRow = Rng(1).Row
LastRow = Rng.Rows.Count + firstRow - 1
' loop backwards, for the Selection last row, until the first row of the selection
For i = LastRow To firstRow Step -1
' loop through current's row cells
For Each C In Range(Cells(i, firstCol), Cells(i, LastCol))
If C.Value2 <> "<0.01" Then
GoTo ExitLoop
End If
Next C
Rows(i).Delete
ExitLoop:
Next i
End Sub
your test expression might look like:
Join(WorksheetFunction.Transpose(WorksheetFunction.Transpose(Range(Cells(i, FirstCol), Cells(i, LastCol)).Value)), " ") Like "*<0.01*"
How can I generate the Excel as in the image below via a macro?
Briefly I would like to make:
numbers between a1 and b1 print to d column;
numbers between a2 and b2 print to e column;
numbers between a3 and b3 print to f column.
Columns A and B have thousands of values.
As an alternative, here is a formula solution:
=IF(ROW(D1)>INDEX($A:$B,COLUMN(D1)-COLUMN($C1),2)-INDEX($A:$B,COLUMN(D1)-COLUMN($C1),1)+1,"",INDEX($A:$B,COLUMN(D1)-COLUMN($C1),1)+ROW(D1)-1)
Though I realize that a formula solution may not be feasible based on this statement:
Columns A and B have thousands of values.
EDIT: Pure array VBA solution:
Sub tgr()
Dim ws As Worksheet
Dim rData As Range
Dim aData As Variant
Dim aResults() As Variant
Dim lMaxDiff As Long
Dim i As Long, j As Long
Dim rIndex As Long, cIndex As Long
Set ws = ActiveWorkbook.ActiveSheet
Set rData = ws.Range("A1", ws.Cells(Rows.Count, "B").End(xlUp))
lMaxDiff = Evaluate("MAX(" & rData.Columns(2).Address(external:=True) & "-" & rData.Columns(1).Address(external:=True) & ")") + 1
aData = rData.Value2
ReDim aResults(1 To lMaxDiff, 1 To rData.Rows.Count)
For i = LBound(aData, 1) To UBound(aData, 1)
If IsNumeric(aData(i, 1)) And IsNumeric(aData(i, 2)) Then
rIndex = 0
cIndex = cIndex + 1
For j = Int(aData(i, 1)) To Int(aData(i, 2))
rIndex = rIndex + 1
aResults(rIndex, cIndex) = j
Next j
End If
Next i
ws.Range("D1").Resize(UBound(aResults, 1), UBound(aResults, 2)).Value = aResults
End Sub
Only because I like puzzles:
Sub u5758()
Dim x As Long
Dim i As Long
Dim oArr() As Variant
Dim arr() As Long
Dim rng As Range
Dim ws As Worksheet
Application.ScreenUpdating = False
Set ws = ActiveSheet
x = 4
With ws
oArr = .Range(.Cells(1, 1), .Cells(.Rows.Count, 2).End(xlUp)).value
For j = LBound(oArr, 1) To UBound(oArr, 1)
ReDim arr(oArr(j, 1) To oArr(j, 2))
For i = LBound(arr) To UBound(arr)
arr(i) = i
Next i
.Cells(1, x).Resize(UBound(arr) - LBound(arr) + 1).value = Application.Transpose(arr)
x = x + 1
Next j
End With
Application.ScreenUpdating = True
End Sub
I like puzzles too.
Sub from_here_to_there()
Dim rw As Long
With Worksheets("Sheet5") '<~~ set this worksheet properly!
For rw = 1 To .Cells(Rows.Count, 1).End(xlUp).Row
If IsNumeric(.Cells(rw, 1)) And IsNumeric(.Cells(rw, 2)) Then
With .Columns(Application.Max(4, .Cells(1, Columns.Count).End(xlToLeft).Column + 1))
.Cells(1, 1) = .Parent.Cells(rw, 1).Value2
.DataSeries Rowcol:=xlColumns, Type:=xlLinear, Date:=xlDay, _
Step:=1, Stop:=.Parent.Cells(rw, 2).Value2
End With
End If
Next rw
End With
End Sub
You could use this:
Sub test()
Dim Lastrow As Long
Dim j As Double, i As Double, r As Double
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ThisWorkbook
Set ws = wb.Sheets("Sheet1") ' Change the name of your sheet
Lastrow = ws.Range("A" & Rows.Count).End(xlUp).Row
j = 4 ' Column D
With ws
For i = 1 To Lastrow ' Start the loop at A1 until the last row in column A
.Cells(1, j) = .Cells(i, 1).Value
r = 1
Do
.Cells(r + 1, j) = .Cells(r, j) + 1
r = r + 1
Loop Until .Cells(r, j) = .Cells(i, 2).Value
j = j + 1
Next i
End With
End Sub
Here's another quick one just for fun:
Sub transposeNfill()
Dim lastRow&, i&, xStart$, xEnd$, xMid$
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
For i = 1 To lastRow
xStart = Cells(i, 1)
xEnd = Cells(i, 2)
xMid = xEnd - xStart
Cells(1, i + 3).Value = xStart
Cells(1 + xMid, i + 3) = xEnd
Range(Cells(2, i + 3), Cells(xMid, i + 3)).FormulaR1C1 = "=r[-1]c+1"
Cells.Copy
Cells.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
Next i
End Sub
I have a small sample example sheet of data, which will be filled with much more data if I can get this process to work.
What I am trying to do is, based upon the number of cells that are filled in each row, insert the same number of blank lines under that same row and copy everything down all columns that are blank. I have attached two screenshots - a before and after of what the start and end look like, as well as the code used for implementing the blank row insert. So far, all it does is add 8 rows consistently, and is using an older version of Excel. I'm trying to translate it into the new VBA format, but I can't seem to get it to work.
Start:
The result I'm trying to achieve:
Code:
Sub IfYes()
Dim Col As Variant
Dim Y As Variant
Dim BlankRows As Long
Dim LastRow As Long
Dim R As Long
Dim C As Long
Dim StartRow As Long
Col = "AS"
Y = "Y"
StartRow = 1
BlankRows = 1
LastRow = Cells(Rows.Count, Col).End(xlUp).Row
Application.ScreenUpdating = False
With ActiveSheet
For R = LastRow To StartRow + 1 Step -1
If .Cells(R, Col) = "Yes" Then
.Cells(R, Col).Offset(1, 0).Resize(8, 1).EntireRow.Insert
.Cells(R, StartRow).Offset(1, 0).Resize(8, 1).Value = .Cells(R, 1).Value
For C = 1 To 8 Step 1
.Cells(R, Y).Offset(C, 0).Value = .Cells(R, Col).Offset(0, C).Value
Next C
.Cells(R, Col) = "Done"
End If
Next R
End With
Application.ScreenUpdating = True
End Sub
I also have another bit of code that I've been trying to use to get this to function properly.
Dim wb1 As Workbook, ws1 As Worksheet
Dim lRow As Long
Dim LastRow As Range
Dim StartRow As Range
Dim i As Long
Set wb1 = Application.Workbooks.Open("Z:\Employee Folders\Jason\crystal spreadsheet - start.xls")
Set ws1 = wb1.Worksheets("AMZStart")
With ws1
For lRow = Cells(Cells.Rows.Count, "B").End(xlUp).Row To 1 Step -1
If Cells(lRow, "B") = "AMZ" Then Rows(lRow).Offset(1, 0).EntireRow.Insert
Next lRow
LastRow = Range("C" & Rows.Count).End(xlUp).Row + 1
StartRow = 1
For i = StartRow To LastRow
If Cells(i, "C") = "" And i > StartRow Then
Cells(i, "C").Formula = "=SUM(C" & StartRow & ":C" & i - 1 & ")"
StartRow = i + 1
End If
Next
End With
End Sub
I find that storing the values in variant arrays can help.
Sub expand_Entries()
Dim v As Long, vAMZs As Variant, vVALs As Variant
Dim rw As Long, c1 As Long, c2 As Long, c As Long, cs As Long
With Worksheets("Sheet2")
c1 = Application.Match("status", .Rows(1), 0)
c2 = .Cells(1, Columns.Count).End(xlToLeft).Column
For rw = .Cells(Rows.Count, 1).End(xlUp).Row To 2 Step -1
cs = Application.CountA(.Cells(rw, c1 + 1).Resize(1, c2 - c1))
If CBool(cs) Then
vVALs = .Cells(rw, 1).Resize(1, c1 - 1).Value2
With .Cells(rw, c1).Resize(1, cs + 1)
vAMZs = .Cells.Value2
.Offset(0, 1).ClearContents
End With
For c = UBound(vAMZs, 2) To LBound(vAMZs, 2) + 1 Step -1
.Cells(rw + 1, 1).Resize(1, c1 - 1).EntireRow.Insert
.Cells(rw + 1, 1).Resize(1, c1 - 1) = vVALs
.Cells(rw + 1, 8) = vAMZs(1, c)
Next c
End If
Next rw
End With
End Sub
You can use a the CountA Worksheet Function inside your IF block to determine the count of filled cells. Then just replace the 8's with the count of each row.
See code:
If .Cells(R, Col) = "Yes" Then
'get count
Dim iCells As Integer
iCells = WorksheetFunction.CountA(.Range("A" & R & ":R" & R))
.Cells(R, Col).Offset(1, 0).Resize(iCells, 1).EntireRow.Insert
.Cells(R, StartRow).Offset(1, 0).Resize(iCells, 1).Value = .Cells(R, 1).Value
For C = 1 To iCells Step 1
.Cells(R, Y).Offset(C, 0).Value = .Cells(R, Col).Offset(0, C).Value
Next C
.Cells(R, Col) = "Done"
End If
I have been trying to create a macro for copying the header and insert above all the subtotal groups. So all the subtotal groups will have a heading. I tried the below macro but it is not working.
Sub header()
Rows("1:1").Select
Selection.Copy
Dim Col As Variant
Dim BlankRows As Long
Dim LastRow As Long
Dim R As Long
Dim StartRow As Long
Col = "P"
StartRow = 1
BlankRows = 1
LastRow = Cells(Rows.Count, Col).End(xlUp).Row
Application.ScreenUpdating = False
With ActiveSheet
For R = LastRow To StartRow + 1 Step -1
If .Cells(R, Col) = "Total" Then
.Cells(R+1, Col).EntireRow.Insert Shift:=xlDown
End If
Next R
End With
Application.ScreenUpdating = True
End Sub
Try the following. There are several tweaks:
1) I fixed the indenting. A matter of taste perhaps, but I find code hard to read if not logically indented.
2) I replaced the first two lines by Rows(1).Copy. There is no reason to select something in order to copy it (and 1 as an index is more idiomatic than "1:1")
3) The act of inserting the row completes the copy-paste operation. I thus recopied the header row after the insert operation. This fixes your actual problem
4) The final copy in the loop leaves Excel still looking for somewhere to paste the header row. Application.CutCopyMode = False addresses that.
Sub header()
Rows(1).Copy
Dim s As Range
Dim Col As Variant
Dim BlankRows As Long
Dim LastRow As Long
Dim R As Long
Dim StartRow As Long
Col = "P"
StartRow = 1
BlankRows = 1
LastRow = Cells(Rows.Count, Col).End(xlUp).Row
Application.ScreenUpdating = False
With ActiveSheet
For R = LastRow To StartRow + 1 Step -1
If .Cells(R, Col) = "Total" Then
.Cells(R + 1, Col).EntireRow.Insert Shift:=xlDown
Rows(1).Copy
End If
Next R
End With
Application.ScreenUpdating = True
Application.CutCopyMode = False
End Sub