I have a data export that pulls customer info with one row for each parent, and 8 port columns (port1, port2, etc... to port8). I need to transpose the port columns into a unique record for each port that retains the customer info in the parent. The source sheet can have 100+ records, the destination sheet will have a maximum of x8 as many records as source sheet because no row has more than 8 ports. I am struggling with how to proceed from here. My idea was to loop through each SourceData row, build an array for each row that contains all ports field values, transpose this into a new sheet and paste, and continue this until last row. The struggle is the paste destination must paste in gaps of 8, then the sheet must be filtered so blanks are not present, and then vlookup against the remaining data.
Source Format
Desired Format
Sub test3()
Dim wb As Workbook
Dim sourceData As Worksheet
Dim outputData As Worksheet
Set wb = Workbooks("Book1")
Set sourceData = Worksheets("Sheet1")
Set outputData = Worksheets("Sheet2")
Dim Rng As Range
Dim ctr As Long
ctr = 2
Dim iCol As Long, lCol As Long, lRow As Long 'iteration column, last column
Const fCol = 15 'first column
With sourceData
lCol = 22 'last used column
lRow = .Range("A" & .Rows.Count).End(xlUp).Row 'find last used row
For i = 2 To lRow
For iCol = fCol To lCol
Set Rng = Cells(i, iCol)
outputData.Cells(ctr, fCol).Value = Rng
ctr = ctr + 1
Next iCol
Next i
End With
End Sub
Edit 2: updated to include extra rows as output.
Lightly tested...
Sub test3()
Dim wsSrc As Worksheet, srcData, outData, r As Long
Dim c As Long, rOut As Long, p As Long, prt
'get input data as array
Set wsSrc = Worksheets("Sheet1")
srcData = wsSrc.Range("A2:W" & wsSrc.Cells(Rows.Count, "A").End(xlUp).Row).Value
'size output array to max potential size (+ added some space for pepwave/remote cases)
ReDim outData(1 To 10 * UBound(srcData, 1), 1 To 16)
For r = 1 To UBound(srcData, 1) 'loop input data rows
For p = 1 To 8 'loop ports
prt = srcData(r, 14 + p)
If Len(prt) > 0 Then 'if any port value...
rOut = rOut + 1 'add output row
For c = 1 To 14 'populate common columns
outData(rOut, c) = srcData(r, c)
Next c
outData(rOut, 15) = prt 'add port value
outData(rOut, 16) = srcData(r, 23) 'col W value
End If
Next p
'test to see if we're adding additional rows...
If InStr(1, srcData(r, 6), "pepwave", vbTextCompare) > 0 Then
rOut = rOut + 1
'populate pepwave row from srcdata (r,x)
End If
If InStr(1, srcData(r, 6), "data remote", vbTextCompare) > 0 Then
rOut = rOut + 1
'populate data remote row from srcdata (r,x)
End If
'done testing for additional rows
Next r
If rOut > 0 Then
Worksheets("Sheet2").Range("A2").Resize(rOut, UBound(outData, 2)).Value = outData
End If
End Sub
Related
I`m not a super experienced VBA developer and mostly relly on the Macro recorder, hence would appreciate any help by the community in helping me wrap my head around this problem. I havent used loops in the past but imagine this would be the best application for my problem.
I have the following Table;
Name
Year
Sec A
Sec B
Sec C
Joe
2020
15
20
30
Mary
2019
5
25
0
Peter
2020
7
0
0
I would like to copy/paste the name,year and amounts bigger than zero on a new sheet like the following;
Name
Year
Section
Total
Joe
2020
A
15
Joe
2020
B
20
Joe
2020
C
30
Mary
2019
A
5
Mary
2019
B
25
Peter
2020
A
7
The copy/paste operation would continune until it reaches a "0" value on the section columns, then it would continue to the next row, until it reaches the end of the rows.
Many thanks!!!
#BigBen's comment is right.
In Excel, highlight your source table, choose Insert Table (or press ctrl-t) making sure you check that your table has a header row.
Then, in the table ribbon (when your cursor is in the table) rename your table to "Source"
Then, in the Data ribbon, in the "Get & Transform" section, click "From Table". This will create a query that pulls from this table, and present it for editing in the Power Query Editor.
In the Home ribbon of the Power Query editor, click Manage - Reference. This will create a new query that uses/starts with the current one. I recommend renaming it (in the right sidebar).
In the home ribbon of the Power Query editor, click Advanced Editor and paste the following:
let
Source = Source,
#"Renamed Columns" = Table.RenameColumns(Source,{{"Sec A", "A"}, {"Sec B", "B"}, {"Sec C", "C"}}),
#"Unpivoted Columns" = Table.UnpivotOtherColumns(#"Renamed Columns", {"Name", "Year"}, "Attribute", "Value"),
#"Filtered Rows" = Table.SelectRows(#"Unpivoted Columns", each [Value] <> 0)
in
#"Filtered Rows"
You'll now have what you want.
Don't be scared of that code, by the way. I didn't really type all that! After creating the second query,
I double-clicked the column headers to rename them.
I highlighted the last three columns and clicked "Unpivot Columns" from the Transform ribbon.
I clicked the filter for the "Value" column to only get rows where Value wasn't 0.
and that was it!
This function will do that. Just create a input table named ÌnputTable and an output table named OutputTable in your worksheet
Sub Macro3()
Dim input_table As Range, output_table As Range
Set input_table = Range("InputTable")
Set output_table = Range("OutputTable")
Dim i As Integer, j As Integer, k As Integer
Dim name As String, year As String, section As String
For i = 1 To input_table.Rows.Count
name = input_table(i, 1)
year = input_table(i, 2)
For j = 3 To 5
section = Chr(62 + j)
If input_table(i, j).Value > 0 Then
k = k + 1
output_table(k, 1) = name
output_table(k, 2) = year
output_table(k, 3) = section
output_table(k, 4) = input_table(i, j)
End If
Next j
Next i
End Sub
Custom UnPivot RCV by Rows
Adjust the values in the constants section.
The Code
Option Explicit
Sub UnPivotRCVbyRowsCustom()
' Define constants.
Const srcName As String = "Sheet1" ' Source Worksheet Name
Const srcFirst As String = "A1" ' Source First Cell Range
Const rlCount As Long = 2 ' Row Labels (repeating columns) Count
Const vException As Variant = 0 ' Value Exception
Const dstName As String = "Sheet2" ' Destination Worksheet Name
Const dstFirst As String = "A1" ' Destination First Cell Range
Const HeaderList As String = "Name,Year,Section,Total"
Dim wb As Workbook: Set wb = ThisWorkbook ' Workbook containing this code.
' Define Source Range.
Dim ws As Worksheet: Set ws = wb.Worksheets(srcName)
Dim rng As Range
Set rng = defineEndRange(ws.Range(srcFirst).CurrentRegion, srcFirst)
' Write values from Source Range to Data Array.
Dim Data As Variant: Data = rng.Value
Dim srCount As Long: srCount = UBound(Data, 1) ' Source Rows Count
Dim scCount As Long: scCount = UBound(Data, 2) ' Source Columns Count
' Calculate Exceptions Count.
Set rng = rng.Resize(srCount - 1, scCount - rlCount) _
.Offset(1, rlCount)
Dim eCount As Long: eCount = Application.CountIf(rng, vException)
' Rename column labels in Data Array.
Dim fvCol As Long: fvCol = 1 + rlCount ' First Value Column
Dim j As Long ' Source Columns Counter
For j = fvCol To scCount
Data(1, j) = Right(Data(1, j), 1)
Next j
' Define Result Array.
Dim drCount As Long ' Destination Rows Count
drCount = (srCount - 1) * (scCount - rlCount) - eCount + 1
Dim dcCount As Long: dcCount = rlCount + 2 ' Destination Columns Count
Dim Result As Variant: ReDim Result(1 To drCount, 1 To dcCount)
' Write headers to Result Array.
Dim Headers() As String: Headers = Split(HeaderList, ",")
For j = 1 To dcCount
Result(1, j) = Headers(j - 1)
Next j
' Write values from Data Array to Result Array.
Dim i As Long ' Source Rows Counter
Dim k As Long: k = 1 ' Destination Rows Counter
Dim l As Long ' Destination Columns Counter
For i = 2 To srCount
For j = fvCol To scCount
If Data(i, j) <> vException Then
k = k + 1
For l = 1 To rlCount
Result(k, l) = Data(i, l)
Next l
Result(k, l) = Data(1, j)
Result(k, l + 1) = Data(i, j)
End If
Next j
Next i
' Write values from Result Array to Destination Range.
With wb.Worksheets(dstName).Range(dstFirst).Resize(, dcCount)
.Resize(.Worksheet.Rows.Count - .Row + 1).ClearContents
.Resize(drCount).Value = Result
End With
End Sub
Function defineEndRange( _
rng As Range, _
ByVal FirstCellAddress As String) _
As Range
If Not rng Is Nothing Then
With rng.Areas(1)
On Error Resume Next
Dim cel As Range: Set cel = .Worksheet.Range(FirstCellAddress)
On Error GoTo 0
If Not cel Is Nothing Then
If Not Intersect(rng.Areas(1), cel) Is Nothing Then
Set defineEndRange = cel.Resize( _
.Rows.Count + .Row - cel.Row, _
.Columns.Count + .Column - cel.Column)
End If
End If
End With
End If
End Function
I am new to VBA as well, so I am taking this as a practice. Here is the code I wrote. May not be the best solution but it does work.
Sub copyandpastedata()
Dim lastrow As Long
Dim lastcol As Long
Dim i As Integer
Dim ws As Worksheet
Dim cell As Range
Dim char As String
'Define last position where a data exist
lastrow = Sheet1.Cells(Rows.Count, 1).End(xlUp).Row
lastcol = Sheet1.Cells(1, Columns.Count).End(xlToLeft).Column
'Delete all worksheets other than sheet1(where the raw data is)
Application.DisplayAlerts = False
For Each ws In Worksheets
If ws.Name <> "Sheet1" Then
ws.Delete
End If
Next
Application.DisplayAlerts = True
'Create a new sheet and name it to NewData
Sheets.Add(after:=Sheet1).Name = "NewData"
With Sheets("NewData")
.Range("A1") = "Name"
.Range("B1") = "Year"
.Range("C1") = "Section"
.Range("D1") = "Total"
End With
'Loop through raw data and find matches
i = 2
With Sheet1
For Each cell In .Range("C2", .Cells(lastrow, lastcol))
If VBA.IsNumeric(cell) Then
If cell > 0 Then
.Cells(cell.Row, 1).Copy Sheets("NewData").Cells(i, 1) 'Copy Name to the new sheet
.Cells(cell.Row, 2).Copy Sheets("NewData").Cells(i, 2) 'Copy Year to the new sheet
char = Right(.Cells(1, cell.Column), 1) 'Look for section letter ID
Sheets("NewData").Cells(i, 3) = char 'Copy section to the new sheet
.Cells(cell.Row, cell.Column).Copy Sheets("NewData").Cells(i, 4) 'Copy Total to the new sheet
i = i + 1
End If
End If
Next
End With
End Sub
I am trying to convert a set of data to a different format for export.
I want copy only rows with values.
Starting with Column D (see attached), I want to filter for non-blank values and copy across columns B, C, D, CellD2, CellD3 in five columns of a new sheet. Then repeat the same for all columns that have a value after column D.
The data set could have multiple columns (no fixed limit) and multiple rows (no fixed limit).
This is the data I am working on (Sheet name is "LJM Fert")
This is the final result I am trying to achieve (Sheet name is "Export")
The code I have written
Sub CopyPaste()
Dim Totalrows As Long
Dim Totalcolumns As Long
Dim rowloop As Long
Dim columnloop As Long
Dim rowcount As Long
Dim columncount As Long
Dim pastestart As Long
Sheets("LJM Fert").Activate
Totalrows = ActiveSheet.UsedRange.Rows.Count
Totalcolumns = ActiveSheet.UsedRange.Columns.Count
rowcount = 4
columncount = 4
pastestart = 2
For rowloop = rowcount To Totalrows
For columnloop = columncount To Totalcolumns
If ActiveSheet.Cells(rowcount, columncount).Value <> "" Then
ActiveSheet.Cells(rowcount, 2).Copy
Sheets("Export").Activate
ActiveSheet.Cells(pastestart, 1).Paste
Sheets("LJM Fert").Activate
ActiveSheet.Cells(rowcount, 3).Copy
Sheets("Export").Activate
ActiveSheet.Cells(pastestart, 2).Paste
Sheets("LJM Fert").Activate
ActiveSheet.Cells(rowcount, columncount).Copy
Sheets("Export").Activate
ActiveSheet.Cells(pastestart, 3).Paste
Sheets("LJM Fert").Activate
ActiveSheet.Cells(2, columncount).Copy
Sheets("Export").Activate
ActiveSheet.Cells(pastestart, 4).Paste
Sheets("LJM Fert").Activate
ActiveSheet.Cells(3, columncount).Copy
Sheets("Export").Activate
ActiveSheet.Cells(pastestart, 5).Paste
Sheets("LJM Fert").Activate
End If
columncount = columncount + 1
pastestart = pastestart + 1
Next
Next
Application.CutCopyMode = False
'ThisWorkbook.Worksheets("Export").Cells(1, 1).Select
End Sub
You could do something like this:
'Define Variables
Dim shtExport As Worksheet, shtFert As Worksheet
Dim i As Integer
Dim cell as Range
'Assign Variables
Set shtExport = Sheets("Export")
Set shtFert = Sheets("LJM Fert")
i = 1 'first line where to copy data in Sheet "Export"
For Each cell In shtFert.Range("D4:G20") 'Go through each cell in table
If cell.Value <> 0 Then
shtExport.Cells(i, 1) = shtFert.Cells(cell.Row, 2) 'Column A
shtExport.Cells(i, 2) = shtFert.Cells(cell.Row, 3) 'Column B
shtExport.Cells(i, 3) = shtFert.Cells(cell.Row, cell.Column) 'Column C
shtExport.Cells(i, 4) = shtFert.Cells(2, cell.Column) 'Column D
shtExport.Cells(i, 5) = shtFert.Cells(3, cell.Column) 'Column E
i = i + 1 'use next row in sheet Export
End If
Next
What this basically do is go through each cell in the range D4:G20 of your sheet "LJM Fert", checks if this cell is different than 0, if it is: it will "copy" this cell in the Export sheet. And so on for each cell different than 0.
In any case, please make sure you don't use copy/paste, it's really slow compared to what I wrote above. Best is to set ranges, or cells, equal to each other.
I have created code for 1 blank row but I need to change it to 11 blank rows
I have 4000 values in A row. i need to insert 11 rows after each unique value found in A row
Sub AddBlankRows()
'
Dim iRow As Integer, iCol As Integer
Dim oRng As Range
Set oRng = Range("a1")
iRow = oRng.Row
iCol = oRng.Column
Do
'
If Cells(iRow + 1, iCol) <> Cells(iRow, iCol) Then
Cells(iRow + 11, iCol).EntireRow.Insert shift:=x1Down
iRow = iRow + 2
Else
iRow = iRow + 1
End If
'
Loop While Not Cells(iRow, iCol).Text = ""
'
End Sub
So this code is inserting 1 blank row after each unique value.
I would like to change it to 11 blank rows.
Before:
After:
Scratch my last answer, as per your last comments (and screenshots):
"I have 4000 values in row A. i need 11 rows between each row value"
And:
"In my data value present in A row will be unique (i.e) Range A:A 4000 rows are unique"
This, to me, reads like all your values are unique. No need to test anything nomore then:
Sub Test()
Dim lr As Long, x As Long, ws As Worksheet
'Set your worksheet as variable
Set ws = ThisWorkbook.Worksheets("Sheet1")
'Get last used row of column A and fill array
lr = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
'Loop backwards
For x = lr + 1 To 3 Step -1
ws.Rows(x).Resize(11).Insert xlShiftDown
Next x
End Sub
I need a VBA code to copy dynamic range. The Rows look like below :
R1 - Title
R2 - Headers
R3 - Data
R97 - Data
R98 - Blank
R99 - Text
R100 - Title
R101 - Headers
R102 - Data
R150 - Blank
R151 - Text
R153 - Title
R153 - Headers
R154 - Data
I have a similar set of data in rows below further. I can't filter and copy because the header is only in 2 cells and there are blank rows in between. Note the columns are static here
I am looking for help to copy the data in a structured format.
I could find the below code in other conversation, which does copy only 2 rows below headers but doesn't ignore the blanks
Private Sub Search_n_Copy()
Dim LastRow As Long
Dim rng As Range, c As Range
Dim vR(), n As Long, k As Integer, j As Integer
Dim Ws As Worksheet
With Worksheets("Trial Balance Detail") ' <-- here should be the Sheet's name
'.Columns("e").ClearContents
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row ' last row in column A
Set rng = .Range("B1:B" & LastRow) ' set the dynamic range to be searched
' loop through all cells in column A and copy below's cell to sheet "Output_2"
For Each c In rng
If c.Value = "Jrnl No." Then
For j = 1 To 2
n = n + 1
ReDim Preserve vR(1 To 13, 1 To n)
For k = 1 To 13
vR(k, n) = c.Offset(j, k - 1) ' use offset to put value in sheet "Output_2", column E
Next k
Next j
End If
Next c
If n > 0 Then
Set Ws = Sheets.Add '<~~~ Sheets("your sheet name")
With Ws
.Range("a1").Resize(n, 13) = WorksheetFunction.Transpose(vR)
End With
End If
End With
End Sub
I have a spreadsheet with approx. 45,000 rows. Currently I am looping through a column and targeting any cells with a value of 0. Those row numbers get stored in an array. I am then looping through that array, and changing another cell based on the array value. I have 5000 rows with values that need to be reassigned, and it is taking over an hour to run that segment of the code (saving the row numbers to the array only takes a few seconds). Any ideas on how to get the code to run faster? Here is the code:
'Initialize array
Dim myArray() As Variant
Dim x As Long
'Looks for the last row on the "Dates" sheet
Dim lastRow As Long
With ThisWorkbook.Sheets("Dates")
lastRow = .Cells(.Rows.Count, "C").End(xlUp).Row + 1
End With
Dim i As Integer
i = 2
Dim uCounter As Integer
'Loop through all the dates on the "Dates" sheet
While i <= lastRow
'Each date has a counter next to it
uCounter = Worksheets("Dates").Range("B" & i).Value
Dim uDate As String
'Store the date as a string
uDate = Worksheets("Dates").Range("C" & i).Value
Dim startRow As Long, endRow As Long
'Finds the first and last instance of the date on the CERF Data page (45,000 rows)
With Sheets("CERF Data")
startRow = .Range("AN:AN").Find(what:=uDate, after:=.Range("AN1"), LookIn:=xlValues).Row
endRow = .Range("AN:AN").Find(what:=uDate, after:=.Range("AN1"), LookIn:=xlValues, searchdirection:=xlPrevious).Row
End With
Dim j As Long
For j = startRow To endRow
'If the cell in column BB is 0, and the counter is above 0 save row number to array, j being the variable representing row number
If Sheets("CERF Data").Range("BB" & j).Value = 0 And uCounter > 0 Then
'save row number to array
ReDim Preserve myArray(x)
myArray(x) = j
x = x + 1
'decrement counter by 1
uCounter = uCounter - 1
End If
If uCounter = 0 Then Exit For
Next j
i = i + 1
Wend
Dim y As Long
'Loop through the array and assign a value of 2 to all the rows in the array for column AS
For y = LBound(myArray) To UBound(myArray)
Sheets("CERF Data").Range("AS" & myArray(y)).Value = 2
Next y
Thanks!
Without more info this is what I can get you:
Just 1 loop through all the rows, once, checking both if the value on column BB = 0 and the date is within your range of dates:
Option Explicit
Sub Test()
Dim arr, i As Long, DictDates As Scripting.Dictionary
arr = ThisWorkbook.Sheets("CERF Data").UsedRange.Value
Set DictDates = New Scripting.Dictionary 'You need the Microsoft Scripting Runtime Reference for this to work
'Create a dictionary with all the dates you must check
With ThisWorkbook.Sheets("Dates")
LastRow = .Cells(.Rows.Count, 3).End(xlUp).Row
For i = 2 To LastRow
If Not DictDates.Exists(CDate(.Cells(i, 3))) Then DictDates.Add CDate(.Cells(i, 3)), 1
Next i
End With
'Only one loop through the whole array
For i = 1 To UBound(arr)
If arr(i, 54) = 0 And DictDates.Exists(CDate(arr(i, 40))) Then 'check your 2 criterias, date and value = 0
arr(i, 45) = 2 'input the value 2 on the column "AS"
End If
Next i
ThisWorkbook.Sheets("CERF Data").UsedRange.Value = arr
End Sub