VBA why do I have blank rows after appending tables? - excel

VBA newb here.
Essentially, I'm collecting weekly compliance records for week over week data.
My main issue is that I have a queried table that is dynamic and on a good week it's empty.
I would like to be able to pull the contents of this table and paste them to the first empty row below a static table that contains year to date data.
This step is an easy one to accomplish manually, but I'm looking to automate for the sake of handing this report off to my less-than-tech-savvy team members.
This question: How to copy and paste two separate tables to the end of another table in VBA? has given me most of what I'm using so far. I've swapped a few of their values and declarations to be relevant to my sheet and ranges, but for the most part it's copy/paste with the listed solution for "Destination: ="
For the most part, this block does the exact thing I'm after:
(I've commented out GCC's second range, but intend to utilize it once this one's settled.)
Sub Inv_Copy_Paste()
Dim TC As Worksheet
'Dim Chart As Worksheet
Dim lr2 As Long
Set TC = Worksheets("TC Data Dump")
'Set Chart = Worksheets("Inventory for Charts")
lr2 = TC.Cells(Rows.Count, 1).End(xlUp).Row
With TC
.Range("O2", ("W2" & .Range("O" & Rows.Count).End(xlUp).Row)).Copy Destination:=TC.Cells(Rows.Count, 1).End(xlUp).Offset(1)
'.Range("K2", ("S2" & .Range("K" & Rows.Count).End(xlUp).Row)).Copy Destination:=Chart.Range("A" & lr2 + 1)
End With
End Sub
The one exception that I'm running into is that once the code copies populated data over, it adds a handful of blank lines below the data:
20 Blank Rows
Is this something I'm overlooking in the code that's already here?
I'll grant that I barely understand what the code is doing in the With TC portion, so any additional context would be greatly appreciated.
Bonus question: Will I need a separate Sub/Worksheet when I attempt to copy another dynamic query table to a second static table?

Dealing With Blanks
If your data is in Excel tables, you should use their methods and properties.
If you don't wanna, you'll need to write special, often complicated codes.
End(xlUp) will only go up to the last row (cell) in the table. If there are empty or blank rows at the bottom, they will also be copied.
The Find method with xlFormulas will go up to the last non-empty row while with xlValues, it will go up (further) to the last non-blank row.
Initial
Result
Main
Sub InvCopyPaste()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim wsTC As Worksheet: Set wsTC = wb.Sheets("TC Data Dump")
Dim wsInv As Worksheet: Set wsInv = wb.Sheets("Inventory for Charts")
Dim srg As Range, drg As Range
' Source: 'wsTC' to Destination: 'wsTC'
Set srg = RefNonBlankRange(wsTC.Range("O2:W2"))
If Not srg Is Nothing Then
Set drg = RefFirstNonBlankRowRange(wsTC.Range("A2") _
.Resize(, srg.Columns.Count)).Resize(srg.Rows.Count)
drg.Value = srg.Value ' for only values (most efficient)
'srg.Copy drg ' instead: for values, formulas and formats
Debug.Print "Copied from " & srg.Address & " to " & drg.Address & "."
End If
' Source: 'wsTC' to Destination: 'wsInv'
Set srg = RefNonBlankRange(wsTC.Range("K2:S2"))
If Not srg Is Nothing Then
Set drg = RefFirstNonBlankRowRange(wsInv.Range("A2") _
.Resize(, srg.Columns.Count)).Resize(srg.Rows.Count)
drg.Value = srg.Value ' for only values (most efficient)
'srg.Copy drg ' instead: for values, formulas and formats
Debug.Print "Copied from " & srg.Address & " to " & drg.Address & "."
End If
End Sub
The Help
Function RefNonBlankRange( _
ByVal FirstRowRange As Range) _
As Range
With FirstRowRange
Dim cel As Range: Set cel = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlValues, , xlByRows, xlPrevious)
If Not cel Is Nothing _
Then Set RefNonBlankRange = .Resize(cel.Row - .Row + 1)
End With
End Function
Function RefFirstNonBlankRowRange( _
ByVal FirstRowRange As Range) _
As Range
Dim rg As Range: Set rg = FirstRowRange.Rows(1)
With rg
Dim cel As Range: Set cel = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlValues, , xlByRows, xlPrevious)
If Not cel Is Nothing Then Set rg = .Offset(cel.Row - .Row + 1)
End With
Set RefFirstNonBlankRowRange = rg
End Function
Debug.Print Results in the Immediate window (Ctrl+G)
Copied from $O$2:$W$6 to $A$4:$I$8.
Copied from $K$2:$S$6 to $A$6:$I$10.

Firstly, the row count is counting the number of lines in the first column.
-lr2 = TC.Cells(Rows.Count, 1).End(xlUp).Row
Here.
Rather than counting the number of rows in the tablese you're trying to copy.
If you change the number 1 in this line to the column you are copying. I think its "O" which would be 15.
Then I'm afraid you'd have to redefine the lr2 for the second table or make another variable for it.
lr3 = TC.Cells(Rows.Count, 11).End(xlUp).Row '11 for the k column
Please let me know if this helps.

Sub oddzac()
Dim RowCount As Integer
ActiveSheet.Range("O2", Cells(Range("W" & Rows.Count).End(xlUp).Row, "W")).Copy Cells(Range("A" & Rows.Count).End(xlUp).Row, 1)
ActiveSheet.Range("K2", Cells(Range("S" & Rows.Count).End(xlUp).Row, "S")).Copy Cells(Range("A" & Rows.Count).End(xlUp).Row + 1, 1)
End Sub
This more what you're looking for?

Another forum responded with this solution:
Sub TC_Copy_Paste()
Dim TC As Worksheet, RowNum As Long
'
Set TC = Worksheets("TC Data Dump")
On Error Resume Next
With TC.Range("P3").ListObject
RowNum = Application.WorksheetFunction.CountA(.ListColumns(1).DataBodyRange)
.DataBodyRange.Cells(1, 1).Resize(RowNum, 9).Copy Destination:=TC.Cells(Rows.Count, 5).End(xlUp).Offset(1)
End With
With TC.Range("AJ3").ListObject
RowNum = Application.WorksheetFunction.CountA(.ListColumns(1).DataBodyRange)
.DataBodyRange.Cells(1, 1).Resize(RowNum, 9).Copy Destination:=TC.Cells(Rows.Count, 26).End(xlUp).Offset(1)
End With
End Sub
Again, I'm not sure why this works and the other doesn't but I wanted to share the end result.

Related

VBA Dynamic array has a bunch of empty values

I have a filter macro that will filter a table for items and delete rows with that item. This is done by looping through an array which referenced a finite range on reference worksheet.
I am trying to change this array to be dynamic so that I can add or remove items to be deleted without having to open the code.
Before:
Dim ArrCategory As Variant
ArrCategory = Worksheets("Sheet1").Range("B8:B12")
For i = 2 To LastRowA
For Each item In ArrCategory
If Range("E" & i).Value = item Then
lo1710.Range.autofilter Field:=5, Criteria1:=item
Application.DisplayAlerts = False
lo1710.DataBodyRange.SpecialCells(xlCellTypeVisible).Delete
Application.DisplayAlerts = True
lo1710.autofilter.ShowAllData
Else
End If
Next item
Next i
After:
Dim ArrCategory As Variant
ArrCategory = Worksheets("Sheet1").Range("B8:B" & Cells(Rows.Count, "B").End(xlUp).row)
For i = 2 To LastRowA
For Each item In ArrCategory
If Range("E" & i).Value = item Then
lo1710.Range.autofilter Field:=5, Criteria1:=item
Application.DisplayAlerts = False
lo1710.DataBodyRange.SpecialCells(xlCellTypeVisible).Delete
Application.DisplayAlerts = True
lo1710.autofilter.ShowAllData
Else
End If
Next item
Next i
After making this change I started getting the "No cells were found" error. When I look in the locals window to see what is in that array, I see the values that are supposed to be in there, but then also hundreds of "empties".
The code does work to eliminate the rows containing the items in the array.
Avoid implicit ActiveSheet references. Your Rows and Cells calls implicitly reference the active sheet, which is not guaranteed to be Worksheets("Sheet1").
Change
ArrCategory = Worksheets("Sheet1").Range("B8:B" & Cells(Rows.Count, "B").End(xlUp).row)
to
With Worksheets("Sheet1")
ArrCategory = .Range("B8:B" & .Cells(.Rows.Count, "B").End(xlUp).Row)
End With
and note the . in front of Range, Cells, and Rows.
Reference Non-Blanks
If there are blank but not empty cells below your data, you could use the following function to reference the correct range.
It uses the Find method and will fail
if the worksheet is filtered,
or if there are hidden rows.
The Function
Function SetNonBlankColumn( _
FirstCell As Range) _
As Range
Dim rg As Range
With FirstCell.Cells(1)
Dim lCell As Range
Set lCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlValues, , , xlPrevious)
If lCell Is Nothing Then Exit Function
Set rg = .Resize(lCell.Row - .Row + 1)
End With
Set SetNonBlankColumn = rg
End Function
Usage
It is assumed that the column range has at least two rows.
In your code, you could utilize the function in the following way:
Dim rg As Range: Set rg = SetNonBlankColumn(Worksheets("Sheet1").Range("B8"))
Dim ArrCategory As Variant: ArrCategory = rg.Value
Also, your worksheet is not qualified, and 'the code will look for it' in the active workbook which may be the wrong one (fix with e.g. ThisWorkbook.Worksheets...).
Of course, if the blank but not empty cells are there by accident, select the cell below the last value and hold Ctrl + Shift and press Down as many times as needed to hit the bottom row, release, and press Del to clear the range and continue using your code with the corrections suggested by BigBen.

How do I code a macro in VBA that deletes columns in excel that don't appear in an array?

I'm creating a macro that is formatting a collection of files and a step in this process is to delete columns that aren't required, keeping a specific set of columns.
I know I can delete columns based on their location and I have this approach implemented already ie 1,3,7 etc or A, C, G etc. But I'm conscious that the report being used might change layout or add extra columns over time and I want to ensure the required columns are kept.
Ideally this code would cycle through each column header starting at A1 until the last column and delete an entire column if the header value isn't found in a list. This list will be an array captured from a range in one of the sheets in the workbook.
List = {Blue, Green, Orange}
Original Table
Blue
Red
Green
Orange
Black
row
row
row
row
row
Formatted Table
Blue
Green
Orange
row
row
row
Does anyone have any suggestions on the approach I could take to get this working or if it's even possible? Any help would be greatly appreciated
You might profit from the following approach by reordering a datafield array via Application.Index which allows even to move the existing columns to any new position.
Note: this flexible solution can be time consuming for greater data sets,
where I would prefer other ways you can find in a lot of answers at SO.
Sub ReorderColumns()
Const headerList As String = "Blue,green,Orange"
'a) define source range
Dim src As Range
Set src = Tabelle3.Range("A1:E100")
'b) define headers
Dim allHeaders: allHeaders = src.Resize(1).Value2
Dim newHeaders: newHeaders = Split(headerList, ",")
'c) get column positions in old headers
Dim cols
cols = getCols(newHeaders, allHeaders)
'd) define data
Dim data As Variant
data = src.Value2
'e) reorder data based on found column positions
data = Application.Index(data, Evaluate("row(1:" & UBound(data) & ")"), cols)
'f) overwrite source data
src = vbNullString ' clear
src.Resize(UBound(data), UBound(data, 2)) = data
End Sub
Help function getCols()
Function getCols(individualHeaders, allHeaders)
'Purp: get 1-based column numbers of found headers via Match
getCols = Application.Match(individualHeaders, allHeaders, 0) ' 1-based
End Function
Please, test the next code. It is compact and fast enough. It will build the columns to be deleted range using Application.Match for the two involved arrays (the existing headers one and the ones to be kept). This code assumes that the headers exist in the first row of the processed sheets, starting from A:A column (If starting from a different column, the code can be adapted:
Sub DeleteColunsNotInArrayDel()
Dim sh As Worksheet, arrStay, lastCol As Long, arrH, arrCols, rngDel As Range
Set sh = ActiveSheet 'use here the sheet you need to process
lastCol = sh.cells(1, sh.Columns.count).End(xlToLeft).Column 'last column on the first row
arrStay = Split("Blue,Green,Orange", ",") 'the headers to not be deleted array
arrH = Application.Transpose(Application.Transpose(sh.Range(sh.cells(1, 1), sh.cells(1, lastCol)).Value2)) 'existing headers array
arrCols = Application.IfError(Application.match(arrH, arrStay, 0), "xx") 'match the two arrays and place "xx" where no match has been found
makeColsRng(arrCols).Delete 'delete the columns range, at once
End Sub
Function makeColsRng(arr) As Range
Dim i As Long, colL As String, strAddr As String
For i = LBound(arr) To UBound(arr) 'iterate between the matched arrays array
If arr(i) = "xx" Then 'for the not matching case:
colL = Split(cells(1, i).Address, "$")(1) 'extract the letter of the respective column
strAddr = strAddr & colL & "1," 'build the string of the columns to be deleted range
End If
Next i
Set makeColsRng = Range(left(strAddr, Len(strAddr) - 1)).EntireColumn 'return the necessary range
End Function
In case of headers not starting from the first sheet column, the function can easily be adapted by adding a new parameter (the first column number) to be added when the range to be deleted is built.
The above suggested solution is a fancy one, just for the sake of showing the respective approach, which is not too often used. It may have a limitation of the range building, in case of a string bigger than 254 digits, No error handling for the case of everything matching (even, easy to be added). The next version is standard VBA, compact, more reliable, faster and easier to be understood:
Sub DeleteColunsRangeNotInArray()
Dim sh As Worksheet, arrStay, lastCol As Long, rngH As Range, rngDel As Range, i As Long
Set sh = ActiveSheet
lastCol = sh.cells(1, sh.Columns.count).End(xlToLeft).Column'last column on the first row
arrStay = Split("Blue,Green,Orange", ",") 'the headers to not be deleted array
Set rngH = sh.Range(sh.cells(1, 1), sh.cells(1, lastCol)) 'existing headers range
For i = 1 To rngH.Columns.count
If IsError(Application.match(rngH(i).Value, arrStay, 0)) Then 'if not a match in arrStay:
addToRange rngDel, rngH(i) 'build a Union range
End If
Next i
'delete the not necessary columns at once:
If Not rngDel Is Nothing Then rngDel.EntireColumn.Delete
End Sub
Private Sub addToRange(rngU As Range, rng As Range)
If rngU Is Nothing Then
Set rngU = rng
Else
Set rngU = Union(rngU, rng)
End If
End Sub
Dynamic Named Range
I think a dynamic named range is an excellent choice for storing and retrieving your required columns. Please see the link I provided from https://exceljet.net/ to setup your dynamic named range.
Generic formula =$A$2:INDEX($A:$A,COUNTA($A:$A))
Regular Expression Approach
After reading in your named range, one approach for testing your columns is using regular expressions. To use this you will need to set a library reference to Microsoft VBScript Regular Expressions 5.5. The pipe character | represents an or statement, so we can join our array using that delimiter.
Deleting Ranges in loops
When deleting columns or rows within a loop, the best approach I have found is to union the ranges together in a variable and execute the deletion in one go. This helps performance and it prevents errors from deleting ranges the loop is working on.
I do this so often that I created a custom function for this UnionRange
' Helper function that allows
' concatinating ranges together
Public Function UnionRange( _
ByRef accumulator As Range, _
ByRef nextRange As Range _
)
If accumulator Is Nothing Then
Set UnionRange = nextRange
Else
Set UnionRange = Union(accumulator, nextRange)
End If
End Function
Putting it all together
Below is my implementation of what your code could look like, just make sure to first:
Create a Dynamic Named Range and populate with your required headers
Add Microsoft VBScript Regular Expressions 5.5 reference
Update Sheet1 to whatever sheet your table exists (possibly change logic for finding header row based on your needs)
' Need Regular Expressions Referenced in order to work!
' #libraryReference {Microsoft VBScript Regular Expressions 5.5}
Public Sub DemoDeletingNonRequiredColumns()
' Make sure to create a named range
' otherwise this section will fail. In this
' example the named range is `RequiredColumns`
Dim requiredColumns() As Variant
requiredColumns = Application.WorksheetFunction.Transpose( _
Range("RequiredColumns").Value2 _
)
' To test if the column is in the required
' columns this method uses regular expressions.
With New RegExp
.IgnoreCase = True
' The pipe charactor is `or` in testing.
.Pattern = Join(requiredColumns, "|")
Dim headerRow As Range
' This example uses `Sheet1`, but update to
' the actual sheet you are using.
With Sheet1
Set headerRow = .Range("A1", .Cells(1, Columns.Count).End(xlToLeft))
End With
Dim column As Range
For Each column In headerRow
' If the column name doesn't match the
' pattern, then concatenate it to the
' toDelete range.
If Not .Test(column.Value2) Then
Dim toDelete As Range
Set toDelete = UnionRange(toDelete, column.EntireColumn)
End If
Next
End With
' toDelete is used as it provides better performance
' and it also prevents errors when deleting columns
' while looping.
If Not toDelete Is Nothing Then
toDelete.Delete
Set toDelete = Nothing
End If
End Sub
Delete Columns Not In a List
Option Explicit
Sub DeleteIrrelevantColumns()
' Source - the worksheet containing the list of headers.
Const sName As String = "Sheet2"
Const sFirstCellAddress As String = "A2"
' Destination - the worksheet to be processed.
Const dName As String = "Sheet1"
Const dFirstCellAddress As String = "A1"
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim sfCell As Range: Set sfCell = sws.Range(sFirstCellAddress)
Dim sData() As Variant
With sfCell
Dim slCell As Range: Set slCell = .Resize(sws.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
sData = .Resize(slCell.Row - .Row + 1).Value
End With
Dim sDict As Object: Set sDict = CreateObject("Scripting.Dictionary")
sDict.CompareMode = vbTextCompare
Dim sValue As Variant
Dim sr As Long
For sr = 1 To UBound(sData)
sValue = sData(sr, 1)
If Not IsError(sValue) Then ' exclude error values
If Len(sValue) > 0 Then ' exclude blanks
sDict(sValue) = Empty ' write
End If
End If
Next sr
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dfCell As Range: Set dfCell = dws.Range(dFirstCellAddress)
Dim drg As Range
With dfCell
Dim dlCell As Range: Set dlCell = _
.Resize(, dws.Columns.Count - .Column + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
Set drg = .Resize(, dlCell.Column - .Column + 1)
End With
Dim dData() As Variant: dData = drg.Value
Dim dCells As Range
Dim dValue As Variant
Dim dc As Long
For dc = 1 To UBound(dData, 2)
dValue = dData(1, dc)
If sDict.Exists(dValue) Then
' If duplicate columns, keep only the left-most.
sDict.Remove dValue
Else
' Combine the irrelevant header cells into a range.
If dCells Is Nothing Then
Set dCells = drg.Cells(dc)
Else
Set dCells = Union(dCells, drg.Cells(dc))
End If
End If
Next dc
' Delete columns in one go.
If Not dCells Is Nothing Then
dCells.EntireColumn.Delete
End If
' Inform.
If sDict.Count = 0 Then
MsgBox "Irrelevant columns deleted.", vbInformation
Else
MsgBox "Irrelevant columns deleted." & vbLf & vbLf _
& "Columns not found:" & vbLf _
& Join(sDict.Keys, vbLf), vbCritical
End If
End Sub

Copy and Paste the Unique Values from Filtered Column

I'm trying to get the Unique values from the Filtered Range and trying to paste the same into specific worksheet. But I'm facing a Run-Time Error 1004 (Database or Table Range is not Valid).
Set DataSet = MainSht.Range(Cells(1, 1), Cells(Lrows, Lcols))
With DataSet
.AutoFilter field:=3, Criteria1:=Array("Corporate Treasury - US", "F&A"), Operator:=xlFilterValues
Set DataRng = .Offset(1, 10).Resize(.Rows.Count - 1, 1).SpecialCells(xlCellTypeVisible)
.AutoFilter
With DataRng
.AdvancedFilter Action:=xlFilterCopy, copytorange:=Wb.Sheets("Corporate Treasury - US").Range("A2"), Unique:=True 'Getting Error Here
End With
End With
Appreciate your help in advance!!
Copy Filtered Unique Data
Basically
'Remove' previous filters.
Create accurate range references before applying AutoFilter.
The filter is applied on the Table Range (headers included).
Use error handling with SpecialCells (think no cells found).
Apply SpecialCells to the Data Range (no headers).
It is usually safe to 'remove' the filter after the reference to the SpecialCells range is created.
Copy/paste and only then apply RemoveDuplicates (xlNo when Data Range).
Optionally, apply Sort (xlNo when Data Range) to the not necessarily exact destination range (ducdrg i.e. no empty cells (due to RemoveDuplicates)).
(xlYes when Table Range.)
A Study
Adjust the values in the constants section (the worksheets are off).
Option Explicit
Sub CopyFilteredUniqueData()
' Source
Const sName As String = "Sheet1"
' Copy
Const sCol As Variant = "K" ' or 11
' Filter
Const sfField As Long = 3
Dim sfCriteria1 As Variant
sfCriteria1 = Array("Corporate Treasury - US", "F&A")
Dim sfOperator As XlAutoFilterOperator: sfOperator = xlFilterValues
' Destination
Const dName As String = "Sheet2"
' Paste
Const dFirst As String = "A2"
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Source
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Debug.Print vbLf & "Source (""" & sws.Name & """)"
' Remove possble previous filters.
If sws.AutoFilterMode Then
sws.AutoFilterMode = False
End If
' Source Table Range
Dim strg As Range: Set strg = sws.Range("A1").CurrentRegion
Debug.Print strg.Address(0, 0)
' Source Column Data Range (No Headers)
Dim scdrg As Range
With strg.Columns(sCol)
Set scdrg = .Resize(.Rows.Count - 1).Offset(1)
End With
Debug.Print scdrg.Address(0, 0) & " (No Headers)"
' Filter.
strg.AutoFilter sfField, sfCriteria1, sfOperator
' Source Filtered Column Data Range (No Headers)
On Error Resume Next
Dim sfcdrg As Range: Set sfcdrg = scdrg.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
sws.AutoFilterMode = False ' no need for the filter anymore
If sfcdrg Is Nothing Then Exit Sub ' no matching cells
Debug.Print sfcdrg.Address(0, 0) & " (No Headers)"
' Destination
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Debug.Print vbLf & "Destination (""" & dws.Name & """)"
' Destination First Cell
Dim dfCell As Range: Set dfCell = dws.Range(dFirst)
' Destination Column Data Range (No Headers)
Dim dcdrg As Range: Set dcdrg = dfCell.Resize(sfcdrg.Cells.Count)
Debug.Print dcdrg.Address(0, 0) & " (No Headers)"
' Copy.
sfcdrg.Copy dcdrg
' Remove duplicates.
dcdrg.RemoveDuplicates 1, xlNo
Debug.Print dcdrg.Address(0, 0) & " (No Headers, Empty Cells Included)"
' Destination Last Cell
Dim dlCell As Range
Set dlCell = dcdrg.Find("*", , xlFormulas, , , xlPrevious)
' Destination Unique Column Data Range (No Headers)
Dim ducdrg As Range
With dcdrg
Set ducdrg = .Resize(dlCell.Row - .Row + 1)
End With
Debug.Print ducdrg.Address(0, 0) & " (No Headers, Empty Cells Excluded)"
' Sort ascending.
ducdrg.Sort ducdrg, , Header:=xlNo
End Sub
I believe the error is because it cannot past a range of non-contiguous cells within a column.
I got round this by simply using the .copy command, but this will paste your unique list with the underlying formatting. See my solution below -
> Set DataSet = MainSht.Range(Cells(1, 1), Cells(Lrows, Lcols))
>
> With DataSet
> .AutoFilter field:=3, Criteria1:=Array("Corporate Treasury - US", "F&A"), Operator:=xlFilterValues
> Set DataRng = .Offset(1, 10).Resize(.Rows.Count - 1, 1).SpecialCells(xlCellTypeVisible)
> DataRng.Copy Destination:=Wb.Sheets("Corporate Treasury - US").Range("A2:A" & (DataRng.Rows.Count + 2))
>
> End With
If you do not want to bring across cell properties/formatting from the original worksheet, you could combine the .copy command with a .pastespecial to only paste in values, formulas or whatever details you need.

Fill in the entire column according to the last data in the table - Does not work

I have a formula in Column A2.
I have a table similar to this:
Formula
Note
Datum
I am very happy because I am
Years
years old
=CONCATENATE(TEXT(C2;"dd-mm-yyyy");$D$1;E2;$F$1)
Any word, TEXT
01.04.2021
21
Autofill
Any word, TEXT 2
01.04.2021
25
I want to transfer it and use it automatically for the whole column. However, I tried possible and impossible ways to do it, but none of them worked. I also looked at forums such as here:
I don't have all the data filled in the table, so I want "excel" to look for the last row in which the record is and try to calculate the formula and return it to the last cell in column A.
Thank you in advance for all the help
(The formula joins the text together) =CONCATENATE(TEXT(C2;"dd-mm-yyyy");$D$1;E2;$F$1)
Sub AutofilCol()
' Apply to the entire column Autofill
Range("A1").Offset(1, 0).Activate
ActiveCell.FormulaR1C1 = _
"=CONCATENATE(TEXT(RC[2],""dd-mm-yyyy""),R1C4,RC[4],R1C6)"
' AutoFill
Selection.AutoFill Destination:=Range("A2:A").End(xlDown).Row
ActiveCell.EntireColumn.AutoFit
End Sub
It looks like this is what you want to do:-
Sub AutofillCol()
Dim Rl As Long ' last used row in column C
Dim Rng As Range
Rl = Cells(Rows.Count, "C").End(xlUp).Row
Set Rng = Range(Cells(2, "A"), Cells(Rl, "A"))
Rng.FormulaR1C1 = "=CONCATENATE(TEXT(RC[2],""dd-mm-yyyy""),R1C4,RC[4],R1C6)"
End Sub
Copy Formulas (Defining a Range)
In this case, there is no need to Activate (or Select) anything neither is the use of AutoFill (FillDown).
Let's say the first solution is the most flexible (reliable) but also the most complex. To better understand it, see the ranges at the various stages of the code printed in the Immediate window (CTRL+G). The flexibility is in the option to use any first cell address e.g. C5, D10, etc. and it will still work.
Depending on your data, you might easily get away with the remaining two solutions.
I didn't include any solution using End since you got that covered by another post.
Option Explicit
Sub copyFormulas()
Const First As String = "A1"
Dim ws As Worksheet: Set ws = ActiveSheet
Dim fCell As Range ' Last Cell in First Row Range
Dim frg As Range ' First Row Range of Table Range
With ws.Range(First)
Set fCell = .Resize(, .Worksheet.Columns.Count - .Column + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If fCell Is Nothing Then Exit Sub
Set frg = .Resize(, fCell.Column - .Column + 1)
Debug.Print "First", fCell.Address, frg.Address
End With
Dim tCell As Range ' Last Cell in Table Range
Dim trg As Range ' Table Range
With frg
Set tCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , xlByRows, xlPrevious)
Set trg = .Resize(tCell.Row - .Row + 1)
End With
Debug.Print "Table", tCell.Address, trg.Address
Dim drg As Range ' Destination Range
Set drg = trg.Columns(1).Resize(trg.Rows.Count - 1).Offset(1)
Debug.Print "Destination", drg.Address
drg.FormulaR1C1 = "=CONCATENATE(TEXT(RC[2],""dd-mm-yyyy""),R1C4,RC[4],R1C6)"
' Or.
'drg.Formula = "=CONCATENATE(TEXT(C2,""dd-mm-yyyy""),$D$1,E2,$F$1)"
End Sub
Sub copyFormulasUsedRange()
With ActiveSheet.UsedRange.Columns(1)
.Resize(.Rows.Count - 1).Offset(1).FormulaR1C1 _
= "=CONCATENATE(TEXT(RC[2],""dd-mm-yyyy""),R1C4,RC[4],R1C6)"
End With
End Sub
Sub copyFormulasCurrentRegion()
With ActiveSheet.Range("A1").CurrentRegion.Columns(1)
.Resize(.Rows.Count - 1).Offset(1).FormulaR1C1 _
= "=CONCATENATE(TEXT(RC[2],""dd-mm-yyyy""),R1C4,RC[4],R1C6)"
End With
End Sub

Trying to find unique IDs with all of the values it qualifies for in excel

To be quite honest I am not entirely sure how to describe what it is I am trying to accomplish? But, here it goes anyway. I have an excel sheet containing one column of IDs and a second column of values that need to be associated to the first column. The problem is that the IDs in column A contain duplicates, which is okay because one ID can qualify for multiple values. What I need is to have a third column pull back the unique id, and a fourth column pull back a semi-colon delimited list of all of the values the id qualifies for. Hopefully the attached image makes sense? For what it's worth I have tried every formula I can think of, and I really know nothing about macros, which is what I am thinking needs to be implemented.
Try below code :
Sub sample()
Dim lastRowA As Long, lastRowC As Long
lastRowA = Range("A" & Rows.Count).End(xlUp).Row
lastRowC = Range("C" & Rows.Count).End(xlUp).Row
Dim rng As Range, cell As Range
Set rng = Range("C2:C" & lastRowC)
Dim rngSearch As Range
Set rngSearch = Range("A1:A" & lastRowA)
Dim rngFind As Range
Dim firstCell As String
For Each cell In rng
Set rngFind = rngSearch.Find(What:=cell, LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows)
If Not rngFind Is Nothing Then
temp = rngFind.Offset(0, 1)
firstCell = rngFind.Address
Do While Not rngFind Is Nothing
Set rngFind = rngSearch.FindNext(After:=rngFind)
If rngFind.Address <> firstCell Then
temp = temp & ";" & rngFind.Offset(0, 1)
Else
Set rngFind = Nothing
End If
Loop
End If
cell.Offset(0, 1) = temp
Next
End Sub
Here's an alternative approach, that has several advantages
it builkds the list of unique sku's
it clear old data from columns C:D
it will run much faster than looping over a range
Sub Demo()
Dim rngA As Range, rng as Range
Dim datA As Variant
Dim i As Long
Dim sh As Worksheet
Dim dic As Object
Set sh = ActiveSheet ' can change this to your worksheet of choice
Set dic = CreateObject("Scripting.Dictionary")
With sh
' Get data from columns A:B into a variant array
Set rngA = .Range(.Cells(2, 2), .Cells(.Rows.Count, 1).End(xlUp))
datA = rngA
' Create list of unique sku's and built value strings
For i = 1 To UBound(datA)
If dic.Exists(datA(i, 1)) Then
dic(datA(i, 1)) = dic(datA(i, 1)) & ";" & datA(i, 2)
Else
dic.Add datA(i, 1), datA(i, 2)
End If
Next
' Clear exisating data from columns C:D
Set rng = .Range(.Cells(2, 4), .Cells(.Rows.Count, 3).End(xlUp))
If rng.Row > 1 Then
rng.Clear
End If
' Put results into columns C:D
.Range(.Cells(2, 3), .Cells(dic.Count + 1, 3)) = Application.Transpose(dic.Keys)
.Range(.Cells(2, 4), .Cells(dic.Count + 1, 4)) = Application.Transpose(dic.Items)
End With
End Sub
How to add this:
Start the VBS editor (Alt+F11 from excel)
show project explorer, if its not already visible (Ctrl+R)
add a Module (right click on your workbook, Insert, Module)
open the module (dbl click)
Add Option Explicit as the first line, if not already there
copy paste this code into module
How to run it, from Excel
activate the sheet with your data
open macro dialog (Alt+F8)
select Demo from list and run

Resources