I am new to VBA and coding in general and I am being tasked with some coding that is proving difficult. I am trying to copy/transpose/paste values from a two-column PivotTable and I need it to paste vertically on another sheet and break on blanks. (see image) I need to copy each group in the PivotTable then transpose paste values vertically on a new worksheet. I believe I need to count populated rows (using an array?) until I get to a blank row then paste the group. I can picture what I need to do but all my coding attempts are way off. Except for the copy/paste, I have no clue how to code this. I cannot figure out how to capture each group of populated rows to be pasted.
' Copy a vertical range (on "FQNID_Sites" sheet) and paste to a horizontal range in column B (next blank row on "BH_FH" sheet)
Dim sourceSheet As Worksheet
Set sourceSheet = ThisWorkbook.Worksheets("FQNID_Sites")
Dim destinationSheet As Worksheet
Set destinationSheet = ThisWorkbook.Worksheets("BH_FH")
Dim cellToPasteTo As Range
' Need to loop through each group breaking on each siteNFID in column D (or break on blanks in column E?)
Set rng = Range("$D$2:$E$" & ActiveSheet.UsedRange.Rows.Count)
For Each cell In rng
Set cellToPasteTo = destinationSheet.Cells(destinationSheet.Rows.Count, "B").End(xlUp).Offset(1, 0)
If cell.Value = "" And Not IsNull(copyStart) Then
copyEnd = cell.Offset(-1, 0).Address
ElseIf cell.Value = "" Then
copyStart = cell.Offset(0, -1).Address
End If
If Not IsNull(copyStart) And Not IsNull(copyEnd) Then
sourceSheet.Range(copyStart & ":" & copyEnd).Select
Selection.Copy
cellToPasteTo.PasteSpecial Paste:=xlPasteValuesAndNumberFormats, Transpose:=True
End If
Next cell
Application.CutCopyMode = False
I need it to break on each siteNFID/FQNID then paste values for each group vertically in column B on the BH_FH worksheet.
Example of the input and expected output format
This code will work. Tested on similar data structure. I used the code sheet name in the code `Sheet1'. Change as needed.
Option Explicit
Sub runTranspose()
With Sheet1
Dim lastRow As Long
lastRow = .Cells(.Rows.Count, 4).End(xlUp).Row
'load range starts to transpose
Dim i As Long
For i = 2 To lastRow
If Len(.Cells(i, 5)) = 0 Then
Dim startTranspose As Range
If startTranspose Is Nothing Then
Set startTranspose = .Cells(i, 5)
Else
Set startTranspose = Union(startTranspose, .Cells(i, 5))
End If
End If
Next
Dim c As Range
For Each c In startTranspose
transposeData c
Next
End With
End Sub
Sub transposeData(r As Range)
With Sheet1
Dim nextRow As Long
nextRow = .Cells(.Rows.Count, 8).End(xlUp).Row + 1
Dim fullRange As Range
Set fullRange = Range(r.Offset(1, -1), r.Offset(1).End(xlDown))
Dim arr As Variant
arr = fullRange.Value
.Cells(nextRow,7).Value = r.offset(-1).Value 'to add label
.Cells(nextRow, 8).Resize(2, UBound(arr)).Value = Application.Transpose(arr)
End With
End Sub
Related
My intention was to have the following code compile data from my "Low CPM 1" worksheet into an array and then filter my active worksheet based on this array. While the macro does seem to affect the filters, none of the values get filtered out. Any help on this matter would be greatly appreciated
Sub Macro1()
Dim CPM1Array(0 To 300) As Variant
For i = 2 To UBound(CPM1Array)
CPM1Array(i) = Sheets("Low CPM 1").Cells(i, 2).Value
Next i
ActiveSheet.Range("$A$1:$H$251").AutoFilter Field:=3, Criteria1:=("<>1 to Ubound(CPM1Array)"), Operator:=xlFilterValues
End Sub
There is no simple way with autofilter to achieve what you want. You cannot use Criteria1:="<>MyArray"
Alternative
We know which values we do not want. We can find out what are the values of the relevant column
Simply store the values of the relevant column in an array and then remove the unnecessary values from it by comparing it with the array which has values we do not want.
Remove blank cells from the array
Pass the final array to the autofilter.
In Action
Let's say our worksheet looks like as shown in the below image. I am taking an example of only 15 rows.
Code
Sub Sample()
Dim ws As Worksheet
Dim MyAr(1 To 5) As String
Dim tmpAr As Variant, ArFinal() As String
Dim LRow As Long
ReDim ArFinal(0 To 0)
Set ws = ActiveSheet
'~~> Creating an array of values which we do not want
For i = 1 To 5
MyAr(i) = i
Next i
With ws
'~~> Last Row of Col C sice you will filter on 3rd column
LRow = .Range("C" & .Rows.Count).End(xlUp).Row
'~~> Storing the values form C in the array
tmpAr = .Range("C2:C" & LRow).Value
'~~> Compare and remove values which we do not want
For i = 1 To LRow - 1
For j = 1 To UBound(MyAr)
If tmpAr(i, 1) = MyAr(j) Then tmpAr(i, 1) = ""
Next j
Next i
'~~> Remove blank cells from the array by copying them to a new array
For i = LBound(tmpAr) To UBound(tmpAr)
If tmpAr(i, 1) <> "" Then
ArFinal(UBound(ArFinal)) = tmpAr(i, 1)
ReDim Preserve ArFinal(0 To UBound(ArFinal) + 1)
End If
Next i
'~~> Filter on values which you want. Change range as applicable
.Range("$A$1:$H$15").AutoFilter Field:=3, Criteria1:=ArFinal, Operator:=xlFilterValues
End With
End Sub
Output
I'm working on a project for work and I've hit a wall. I'm trying to automate some formatting to speed up a process. On Sheet1, there is a table in the range G2 to W21. The data contained in this table is entered by the user via a userform. After the data is entered, I use this data to drive out Sheet2 is formatted. So far, I've figured out how to handle column G & H of this table the way that I want. I cant figure out how to handle columns I:M and O:W.
Here is the code I've come up with so far:
Dim LineItems As Range, Cell As Range
Dim linearr() As Variant
Dim datasetarr() As Variant
Dim i As Integer
Dim j As Integer
Dim accountnum As Range
Dim accountnumrng As Range
Set LineItems = Sheet1.Range("H2:H21")
Set DataSets = Sheet1.Range("G2:G21")
For Each Cell In LineItems
If Len(Cell.Value) > 0 Then
i = i + 1
ReDim Preserve linearr(1 To i)
linearr(i) = Cell.Value
End If
Next Cell
For Each Cell In DataSets
If Len(Cell.Value) > 0 Then
j = j + 1
ReDim Preserve datasetarr(1 To j)
datasetarr(j) = Cell.Value
End If
Next Cell
Set accountnumrng = Sheet2.Range("B6:B1000").SpecialCells(xlCellTypeConstants, 23)
For Each accountnum In accountnumrng.Cells
accountnum.Offset(1, 1).Cells(1, 1).Resize(UBound(linearr), 1).Value = Application.Transpose(linearr)
accountnum.Offset(1, 0).Cells(1, 1).Resize(UBound(datasetarr), 1).Value = Application.Transpose(datasetarr)
Next accountnum
here is a picture of the table on Sheet1. Outlined in red are the columns I'm trying to work with
I basically just want to expand on what I've figured out so far. Any help would be greatly appreciated.
Below is a Picture of what Sheet2 looks like right now
Below is what I'd like Sheet2 to look like
There is no reason to use an array. Ranges are arrays by their nature.
This should do what you want:
Dim accountnum As Range
Dim accountnumrng As Range
Dim lastrow As Long
Dim sze As Long
lastrow = Sheet1.Range("G2").End(xlDown).Row
sze = lastrow - 2 + 1
Set accountnumrng = Sheet2.Range("B6:B1000").SpecialCells(xlCellTypeConstants, 23)
For Each accountnum In accountnumrng.Cells
accountnum.Offset(1, 8).Resize(sze, 9).Value = Sheet1.Range("O2:W" & lastrow).value
accountnum.Offset(1, 0).Resize(sze, 7).Value = Sheet1.Range("G2:M" & lastrow).value
Next accountnum
I'm trying to get data posted from a non-contiguous range into a row in a separate sheet. Before I built the non-contiguous range, this code worked perfectly. I've tried several things to loop through, but nothing I tried will work. It won't copy the ranged data as it sits. It's been years since I've actually done any coding and my re-learning curve seems to be holding me back.... the logic just isn't coming to me. Help!
Sub UpdateLogWorksheet()
Dim historyWks As Worksheet
Dim inputWks As Worksheet
Dim nextRow As Long
Dim oCol As Long
Dim myCopy As Range
Dim myTest As Range
Dim myData As Range
Dim lRsp As Long
Set inputWks = Worksheets("Input")
Set historyWks = Worksheets("DataEntry")
oCol = 3 'order info is pasted on data sheet, starting in this column
'check for duplicate VIN in database
If inputWks.Range("CheckVIN") = True Then
lRsp = MsgBox("VIN already in database. Update record?", vbQuestion + vbYesNo, "Duplicate VIN")
If lRsp = vbYes Then
UpdateLogRecord
Else
MsgBox "Please change VIN to a unique number."
End If
Else
'cells to copy from Input sheet - some contain formulas
Set myCopy = inputWks.Range("VehicleEntry") 'non-contiguous named range
With historyWks
nextRow = .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0).Row
End With
With inputWks
'mandatory fields are tested in hidden column
Set myTest = myCopy.Offset(0, 2)
If Application.Count(myTest) > 0 Then
MsgBox "Please fill in all the cells!"
Exit Sub
End If
End With
With historyWks
'enter date and time stamp in record
With .Cells(nextRow, "A")
.Value = Now
.NumberFormat = "mm/dd/yyyy hh:mm:ss"
End With
'enter user name in column B
.Cells(nextRow, "B").Value = Application.UserName
'copy the vehicle data and paste onto data sheet
myCopy.Copy
.Cells(nextRow, oCol).PasteSpecial Paste:=xlPasteValues, Transpose:=True
Application.CutCopyMode = False
End With
'clear input cells that contain constants
Clear
End If
End Sub
This is an example to explain how to achieve what you want. Please amend the code to suit your needs.
Let's say, I have a Sheet1 which looks like as shown below. The colored cells make up from my non contiguous range.
Now paste the code given below in a module and run it. The output will be generated in Sheet2 and Sheet3
Code
Sub Sample()
Dim rng As Range, aCell As Range
Dim MyAr() As Variant
Dim n As Long, i As Long
'~~> Change this to the relevant sheet
With Sheet1
'~~> Non Contiguous range
Set rng = .Range("A1:C1,B3:D3,C5:G5")
'~~> Get the count of cells in that range
n = rng.Cells.Count
'~~> Resize the array to hold the data
ReDim MyAr(1 To n)
n = 1
'~~> Store the values from that range into
'~~> the array
For Each aCell In rng.Cells
MyAr(n) = aCell.Value
n = n + 1
Next aCell
End With
'~~> Output the data in Sheet
'~~> Vertically Output to sheet 2
Sheet2.Cells(1, 1).Resize(UBound(MyAr), 1).Value = _
Application.WorksheetFunction.Transpose(MyAr)
'~~> Horizontally Output to sheet 3
Sheet3.Cells(1, 1).Resize(1, UBound(MyAr)).Value = _
MyAr
End Sub
Vertical Output
Horizontal Output
Hope the above example helps you in achieving what you want.
I have the below code from Chandoo for excel. In the 'Data Sheet' it selects the sheet to copy to according to col. C, then copies col. A - G to that spreadsheet and moves to the next entry.
I am having trouble adjusting this code to suit my spreadsheet and would appreciate some assistance. My sheet name is in col. A (not c), and I only require col. B & C to be copied to the sheet. Additionally col. B & C need to be copied into col. B & G in the spreadsheet.
Sub copyPasteData()
Dim strSourceSheet As String
Dim strDestinationSheet As String
Dim lastRow As Long
strSourceSheet = "Data entry"
Sheets(strSourceSheet).Visible = True
Sheets(strSourceSheet).Select
Range("C2").Select
Do While ActiveCell.Value <> ""
strDestinationSheet = ActiveCell.Value
ActiveCell.Offset(0, -2).Resize(1, ActiveCell.CurrentRegion.Columns.Count).Select
Selection.Copy
Sheets(strDestinationSheet).Visible = True
Sheets(strDestinationSheet).Select
lastRow = LastRowInOneColumn("A")
Cells(lastRow + 1, 1).Select
Selection.PasteSpecial xlPasteValues
Application.CutCopyMode = False
Sheets(strSourceSheet).Select
ActiveCell.Offset(0, 2).Select
ActiveCell.Offset(1, 0).Select
Loop
End Sub
Public Function LastRowInOneColumn(col)
'Find the last used row in a Column: column A in this example
'http://www.rondebruin.nl/last.htm
Dim lastRow As Long
With ActiveSheet
lastRow = .Cells(.Rows.Count, col).End(xlUp).Row
End With
LastRowInOneColumn = lastRow
End Function
Any assistance in resolving this would be greatly appreciated.
Thank you
This is a leason on the dangers of copying random code form the internet. Manipulating the active selection like this is slow, hard to read, and hard to maintain.
Here's the code refactored to do this task in a more controlled fasion.
The origonal code (refactored) is included, commented out. The code modified to reference your requested cells follows each original line
Sub copyPasteData()
Dim strSourceSheet As String
Dim strDestinationSheet As String
Dim lastRow As Long
Dim wsSource As Worksheet, wsDest As Worksheet
Dim rWs As Range
Dim rSrc As Range, rDst As Range, cl As Range
strSourceSheet = "Data entry"
' Get a reference to the source sheet
Set wsSource = Worksheets(strSourceSheet)
With wsSource
' Get a reference to the list of sheet names
'Set rWs = Range(.Cells(2, 3), .Cells(.Rows.Count, 3).End(xlUp)) ' for Column C
Set rWs = Range(.Cells(2, 1), .Cells(.Rows.Count, 1).End(xlUp)) ' for Column A
' Loop through the sheet names list
For Each cl In rWs.Cells
' Get a reference to the current row of data, all cells on that row
'Set rSrc = cl.EntireRow.Resize(1, .Cells(cl.Row, .Columns.Count).End(xlToLeft).Column)
Set rSrc = cl.EntireRow.Cells(1, 2).Resize(1, 2) ' Reference columns B and C only
' Get a reference to the current Destination sheet
Set wsDest = Worksheets(cl.Value)
With wsDest
lastRow = .Cells(.Rows.Count, 2).End(xlUp).Row ' Check last row in Column B
' Copy data to destination using Value array
'.Cells(lastRow + 1, 1).Resize(1, rSrc.Columns.Count).Value = rSrc.Value ' all data
.Cells(lastRow + 1, 2).Value = rSrc.Cells(1, 1) ' copy first cell to column B
.Cells(lastRow + 1, 7).Value = rSrc.Cells(1, 2) ' copy second cell to column G
End With
Next
End With
End Sub
Hi there I have created the following two macros however it is inserting a row after the last cell with data. I believe this is a result of my loop condition being Do Until ActiveCell.Value = "". I would like to have the loop stop at the last cell with data.
I tried using variables Do Until Loop_Long = LastRow but this did not work for me.
All I would like is to have a macro insert a row between cells with unlike data. Then a macro that will find empty cells in the column,the ones we previously inserted, and then delete the row.
As outlined above the issue is it is inserting an extra row and not deleting it, if you put values all the way down column B after your data in column A you will see what I mean.
Here is my code:
Option Explicit
Sub Macro1()
'Insert Blank Row Between Names
Sheets("Sheet1").Select
Range("A1").Select
Do Until ActiveCell.Value = ""
If ActiveCell.Value <> ActiveCell.Offset(1).Value Then
ActiveCell.Offset(1).EntireRow.Insert
ActiveCell.Offset(1).Select
End If
ActiveCell.Offset(1).Select
Loop
End Sub
Sub Macro2()
Dim LastRow As Long
'Delete Inserted Rows
Sheets("Sheet1").Select
LastRow = Cells(Rows.Count, "A").End(xlUp).Row
Range("A" & LastRow).Select
Do Until ActiveCell.Value = Range("A1")
If ActiveCell.Value <> ActiveCell.Offset(-1).Value Then
ActiveCell.Offset(-1).EntireRow.Delete Shift:=xlUp
ActiveCell.Offset(-1).Select
End If
ActiveCell.Offset(-1).Select
Loop
End Sub
From what you've told me, the below code should work for you (and it better follows best practices)... Have you considered copying the data as is and then inserting the rows once you've pasted the data to the new location? That would cut out a step.
Option Explicit
'Declare module-level variables.
Dim sht As Worksheet
Dim Cell As Range
Dim NameRng As Range
Dim LastRow As Long
Sub test()
'Add blank rows.
Set sht = ActiveWorkbook.Sheets("Sheet1")
LastRow = sht.Range("A" & Rows.count).End(xlUp).Row
Set NameRng = sht.Range("A1:A" & LastRow)
For Each Cell In NameRng
If Cell <> Cell.Offset(1, 0) And Cell <> "" Then
Cell.Offset(1, 0).EntireRow.Insert
End If
Next Cell
End Sub
Sub test2()
'Delete blank rows.
Set sht = ActiveWorkbook.Sheets("Sheet1")
LastRow = sht.Range("A" & Rows.count).End(xlUp).Row
Set NameRng = sht.Range("A1:A" & LastRow + 1)
For Each Cell In NameRng
If Cell = "" Then
Cell.EntireRow.Delete
End If
Next Cell
End Sub