Column headers to new sheet - vba

I am trying to use a file picker, which I have and then get the columns of every file and every sheet within that file into a new sheet. So A1 would have file name,B1 sheet name, C1 and down would have column headers (which are A1:?? in all the files Im picking).
Also some files are large so would having automatic calculation to automatic be helpful?
Also note that I have extra variables in the beggining but not necessarily used.
Here is the code, its a mess:
Sub ColumnHeaders()
'includes filling down
'Skips unreadable files
Dim wb As Workbook, fileNames As Object, errCheck As Boolean
Dim ws As Worksheet, wks As Worksheet, wksSummary As Worksheet
Dim y As Range, intRow As Long, i As Integer
Dim r As Range, lr As Long, myrg As Range, z As Range
Dim boolWritten As Boolean, lngNextRow As Long
Dim intColNode As Integer, intColScenario As Integer
Dim intColNext As Integer, lngStartRow As Long
Dim lngLastNode As Long, lngLastScen As Long
'Skipped worksheet for file names
Dim wksSkipped As Worksheet
Set wksSkipped = ThisWorkbook.Worksheets("Skipped")
' Turn off screen updating and automatic calculation
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' Create a new worksheet, if required
On Error Resume Next
Set wksSummary = ActiveWorkbook.Worksheets("Headers")
On Error GoTo 0
If wksSummary Is Nothing Then
Set wksSummary = ActiveWorkbook.Worksheets.Add(After:=ActiveWorkbook.Worksheets(ActiveWorkbook.Worksheets.Count))
wksSummary.Name = "headers"
End If
' Set the initial output range, and assign column headers
With wksSummary
Set y = .Cells(.Rows.Count, 3).End(xlUp).Offset(1, 0)
Set r = y.Offset(0, 1)
Set z = y.Offset(0, -2)
lngStartRow = y.Row
.Range("A1:C1").Value = Array("File Name", "Sheet Name", "headers")
End With
'get user input for files to search
Set fileNames = CreateObject("Scripting.Dictionary")
errCheck = UserInput.FileDialogDictionary(fileNames)
If errCheck Then
Exit Sub
End If
'''
For Each Key In fileNames 'loop through the dictionary
On Error Resume Next
Set wb = Workbooks.Open(fileNames(Key))
If Err.Number <> 0 Then
Set wb = Nothing ' or set a boolean error flag
End If
On Error GoTo 0 ' or your custom error handler
If wb Is Nothing Then
wksSkipped.Cells(wksSkipped.Cells(wksSkipped.Rows.Count, "A").End(xlUp).Row + 1, 1) = fileNames(Key)
Else
Debug.Print "Successfully loaded " & fileNames(Key)
wb.Application.Visible = False 'make it not visible
' more working with wb
Code should go in here
wb.Close savechanges:=False 'close the workbook do not save
Set wb = Nothing 'release the object
End If
Next 'End of the fileNames loop
Set fileNames = Nothing
' Autofit column widths of the report
wksSummary.Range("A1:C1").EntireColumn.AutoFit
' Reset system settings
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
.Visible = True
End With
End Sub
I have the picker(a separate function) , I a skipped worksheet incase the file is corrupt, but I obviously am missing the part where to get the headers and sheet names.
Can anyone help?
UPDATE WITH MATTHEW'S CODE~~~~~~~~~~~~~~~~~~~~
Sub ColumnHeaders()
'includes filling down
'Skips unreadable files
Dim wb As Workbook, fileNames As Object, errCheck As Boolean
Dim ws As Worksheet, wks As Worksheet, wksSummary As Worksheet
Dim y As range, intRow As Long, i As Integer
Dim r As range, lr As Long, myrg As range, z As range
Dim boolWritten As Boolean, lngNextRow As Long
Dim intColNode As Integer, intColScenario As Integer
Dim intColNext As Integer, lngStartRow As Long
Dim lngLastNode As Long, lngLastScen As Long
'need addition
Dim wsReport As Excel.Worksheet
Set wsReport = ActiveWorkbook.Sheets("Sheet1") 'Whatever sheet you want to write to
'Skipped worksheet for file names
Dim wksSkipped As Worksheet
Set wksSkipped = ThisWorkbook.Worksheets("Skipped")
' Turn off screen updating and automatic calculation
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' Create a new worksheet, if required
On Error Resume Next
Set wksSummary = ActiveWorkbook.Worksheets("Headers")
On Error GoTo 0
If wksSummary Is Nothing Then
Set wksSummary = ActiveWorkbook.Worksheets.Add(After:=ActiveWorkbook.Worksheets(ActiveWorkbook.Worksheets.Count))
wksSummary.Name = "headers"
End If
' Set the initial output range, and assign column headers
With wksSummary
Set y = .Cells(.Rows.Count, 3).End(xlUp).Offset(1, 0)
Set r = y.Offset(0, 1)
Set z = y.Offset(0, -2)
lngStartRow = y.Row
.range("A1:C1").Value = Array("File Name", "Sheet Name", "headers")
End With
'get user input for files to search
Set fileNames = CreateObject("Scripting.Dictionary")
errCheck = UserInput.FileDialogDictionary(fileNames)
If errCheck Then
Exit Sub
End If
'''
For Each Key In fileNames 'loop through the dictionary
On Error Resume Next
Set wb = Workbooks.Open(fileNames(Key))
If Err.Number <> 0 Then
Set wb = Nothing ' or set a boolean error flag
End If
On Error GoTo 0 ' or your custom error handler
If wb Is Nothing Then
wksSkipped.Cells(wksSkipped.Cells(wksSkipped.Rows.Count, "A").End(xlUp).Row + 1, 1) = fileNames(Key)
Else
Debug.Print "Successfully loaded " & fileNames(Key)
wb.Application.Visible = False 'make it not visible
' more working with wb
'New addition
Dim iIndex As Integer
Dim lCol As Long
Dim lRow As Long
lRow = 1
'Loop through the worksheets in the current workbook.
For iIndex = 1 To wb.Worksheets.Count
'Set the current worksheet
Set ws = Application.Worksheets(iIndex)
'List out the workbook and worksheet names
wsReport.range("A" & lRow).Value = wb.Name
wsReport.range("B" & lRow).Value = ws.Name
'Start a counter of the columns that we are writing to
lOutputCol = 3
'Loop through the columns.
For lCol = 1 To ws.UsedRange.Columns.Count
'Write the header
wsReport.range(Col_Letter(lOutputCol) & lRow).Value = ws.range(Col_Letter(lCol) & "1").Value
'Increment our column counters.
lOutputCol = lOutputCol + 1
lCol = lCol + 1
Next lCol
'Increment the row we are writing to
lRow = lRow + 1
Next iIndex
wb.Close savechanges:=False 'close the workbook do not save
Set wb = Nothing 'release the object
End If
Next 'End of the fileNames loop
Set fileNames = Nothing
' Autofit column widths of the report
wksSummary.range("A1:C1").EntireColumn.AutoFit
' Reset system settings
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
.Visible = True
End With
End Sub
TWO FUNCTIONS:
Function Col_Letter(lngCol As Long) As String
Dim vArr
vArr = Split(Cells(1, lngCol).Address(True, False), "$")
Col_Letter = vArr(0)
End Function
and
Function FileDialogDictionary(ByRef file As Object) As Boolean ' returns true if the user cancels
'Declare a variable as a FileDialog object.
Dim fd As FileDialog
Dim item As Variant
Dim i As Long
'Create a FileDialog object as a File Picker dialog box.
file.RemoveAll 'clear the dictionary
Set fd = Application.FileDialog(msoFileDialogFilePicker)
'Declare a variable to contain the path
'of each selected item. Even though the path is a String,
'the variable must be a Variant because For Each...Next
'routines only work with Variants and Objects.
'Use a With...End With block to reference the FileDialog object.
With fd
'Use the Show method to display the File Picker dialog box and return the user's action.
'The user pressed the action button.
.Title = "Select Excel Workbooks" 'Change this to suit your purpose
.AllowMultiSelect = True
.Filters.Clear
.Filters.Add "Microsoft Excel files", "*.xlsx,*.xls"
If .Show = -1 Then
'Step through each string in the FileDialogSelectedItems collection.
For Each item In .SelectedItems 'loop through all selected and add to dictionary
i = i + 1
file.Add i, item
Next item
FileDialogDictionary = False
'The user pressed Cancel.
Else
FileDialogDictionary = True
Set fd = Nothing
Exit Function
End If
End With
Set fd = Nothing 'Set the object variable to Nothing.
End Function

When you open a workbook it becomes active so you'll need to create an object that will be the sheet that you are writing to. Somewhere at the top.
Dim wsReport As Excel.Worksheet
Set wsReport = ActiveWorkbook.Sheets("Sheet1") 'Whatever sheet you want to write to
Code to write out the data. Insert where you put "Code should go in here"
Dim iIndex As Integer
Dim lCol As Long
Dim lRow As Long
Dim lOutputCol As Long
lRow = 1
'Loop through the worksheets in the current workbook.
For iIndex = 1 To wb.Worksheets.count
'Set the current worksheet
Set ws = Application.Worksheets(iIndex)
'List out the workbook and worksheet names
wsReport.Range("A" & lRow).Value = wb.name
wsReport.Range("B" & lRow).Value = ws.name
'Start a counter of the columns that we are writing to
lOutputCol = 3
'Loop through the columns.
For lCol = 1 To ws.UsedRange.Columns.count
'Write the header
wsReport.Range(Col_Letter(lOutputCol) & lRow).Value = ws.Range(Col_Letter(lCol) & "1").Value
'Increment our column counters.
lOutputCol = lOutputCol + 1
Next lCol
'Increment the row we are writing to
lRow = lRow + 1
Next iIndex
And you'll need to add this function
Function Col_Letter(lngCol As Long) As String
Dim vArr
vArr = Split(Cells(1, lngCol).Address(True, False), "$")
Col_Letter = vArr(0)
End Function

Related

VBA- Import Multiple CSV to a Sheet, Remove Certain Rows/Columns

I am completely new to VBA, but I have CSV files(same format for all of them), and I want to import them to a single sheet on Excel. I was able to read the CSV file according to this code:
Sub R_AnalysisMerger()
Dim WSA As Worksheet
Dim bookList As Workbook
Dim SelectedFiles() As Variant
Dim NFile As Long
Dim FileName As String
Dim ws As Worksheet, vDB As Variant, rngT As Range
Application.ScreenUpdating = False
'Selects the CSV files as SELECTED FILES
Set ws = ThisWorkbook.Sheets(1)
ws.UsedRange.Clear 'Clears current worksheet
SelectedFiles = Application.GetOpenFilename(filefilter:="Excel Files (*.csv*), *.csv*", MultiSelect:=True) 'Selects csv files
For NFile = LBound(SelectedFiles) To UBound(SelectedFiles)
FileName = SelectedFiles(NFile)
Set bookList = Workbooks.Open(FileName, Format:=2)
Set WSA = bookList.Sheets(1)
With WSA
vDB = .UsedRange
Set rngT = ws.Range("a" & Rows.count).End(xlUp)(2)
If rngT.Row = 2 Then Set rngT = ws.Range("A1")
rngT.Resize(UBound(vDB, 1), UBound(vDB, 2)) = vDB
bookList.Close (0)
End With
Next
Application.ScreenUpdating = True
ws.Range("A1").Select
But I have additional requirements:
Skip the first column.
Skip the first four rows.
Remove a certain String from each word in the fifth row.
Im used to java, and usually I would read each line with a "for" loop and set "if" statements to skip the first row and four columns and remove the string from each string if it was present.
I don't know how to do this with this code. From what I understand it just copies the whole CSV file into the sheet?
This solution is based on reading CSV as textstream. I have tried to include feature that makes possible most all things like selecting columns, Rows and so on.
Sub ImportCSV()
Dim fso As New IWshRuntimeLibrary.FileSystemObject
Dim txtStream As IWshRuntimeLibrary.TextStream
Dim files As IWshRuntimeLibrary.files
Dim file As IWshRuntimeLibrary.file
Dim txtLine As String
Dim lineCount As Integer
Dim lastRow As Integer
Dim lineCol As Variant
Dim rng As Range
Application.ScreenUpdating = False
ThisWorkbook.Sheets(1).usedRange.Delete
Set rng = ThisWorkbook.Sheets(1).usedRange
lastRow = 1
Set files = fso.GetFolder("path\folder").files
For Each file In files
If file.Name Like "*.csv" Then
Set txtStream = file.OpenAsTextStream(ForReading, TristateUseDefault)
txtStream.SkipLine ' skip first line, since it containes headers
lineCount = 1
Do
txtLine = txtStream.ReadLine
If lineCount = 5 Then
txtLine = Replace(txtLine, "stringToReplace", "StringToReplcaeWith") ' replace certain string from words in 5'th row
End If
lineCount = lineCount + 1
lineCol = sliceStr(Split(txtLine, ";"), startIdx:=4) ' slice the array so to skip four first columns
For iCol = 0 To UBound(lineCol) ' write columns to last row
rng(lastRow, iCol + 1).Value = lineCol(iCol)
Next iCol
lastRow = lastRow + 1
'Debug.Print Join(lineCol, ";") ' debug
Loop Until txtStream.AtEndOfStream
End If
Next file
Application.ScreenUpdating = True
End Sub
This is the slicer function
Function sliceStr(arr As Variant, startIdx As Integer, Optional stopIdx As Integer = 0) As String()
If stopIdx = 0 Then
stopIdx = UBound(arr)
End If
Dim tempArrStr() As String
ReDim tempArrStr(stopIdx - startIdx)
Dim counter As Integer
counter = 0
For i = startIdx To stopIdx
tempArrStr(counter) = arr(i)
counter = counter + 1
Next
sliceStr = tempArrStr
End Function
I just did a simple test and the code below seems to work. Give it a go, and feedback.
Sub Demo()
Dim fso As Object 'FileSystemObject
Dim fldStart As Object 'Folder
Dim fld As Object 'Folder
Dim fl As Object 'File
Dim Mask As String
Application.ScreenUpdating = False
Dim newWS As Worksheet
Set newWS = Sheets.Add(before:=Sheets(1))
Set fso = CreateObject("scripting.FileSystemObject") ' late binding
'Set fso = New FileSystemObject 'or use early binding (also replace Object types)
Set fldStart = fso.GetFolder("C:\Users\ryans\OneDrive\Desktop\output\") ' <-- use your FileDialog code here
Mask = "*.csv"
'Debug.Print fldStart.Path & ""
ListFiles fldStart, Mask
For Each fld In fldStart.SubFolders
ListFiles fld, Mask
ListFolders fld, Mask
Next
Dim myWB As Workbook, WB As Workbook
Set myWB = ThisWorkbook
Dim L As Long, t As Long, i As Long
L = myWB.Sheets(1).Cells(Rows.Count, "A").End(xlUp).Row
t = 1
For i = 1 To L
Workbooks.OpenText Filename:=myWB.Sheets(1).Cells(i, 1).Value, DataType:=xlDelimited, Tab:=True
Set WB = ActiveWorkbook
lrow = WB.Sheets(1).Cells(Rows.Count, "A").End(xlUp).Row
WB.Sheets(1).Range("B4:E" & lrow).Copy newWS.Cells(t, 2)
t = myWB.Sheets(1).Cells(Rows.Count, "B").End(xlUp).Row + 1
WB.Close False
Next
myWB.Sheets(1).Columns(1).Delete
Application.ScreenUpdating = True
End Sub
Sub ListFolders(fldStart As Object, Mask As String)
Dim fld As Object 'Folder
For Each fld In fldStart.SubFolders
'Debug.Print fld.Path & ""
ListFiles fld, Mask
ListFolders fld, Mask
Next
End Sub
Sub ListFiles(fld As Object, Mask As String)
Dim t As Long
Dim fl As Object 'File
For Each fl In fld.Files
If fl.Name Like Mask Then
t = Sheets(1).Cells(Rows.Count, "A").End(xlUp).Row + 1
'Debug.Print fld.Path & "" & fl.Name
If Sheets(1).Cells(1, 1) = "" Then
Sheets(1).Cells(1, 1) = fld.Path & "\" & fl.Name
Else
Sheets(1).Cells(t, 1) = fld.Path & "\" & fl.Name
End If
End If
Next
End Sub

Moving row from one workbook to another

I am new to StackOverflow, but have been working on this problem for some time, and am pretty stuck. Right now, my code takes 'Name' input from a textbox, and if the data in column A matches the name that was input, it copies data from the entire row in that sheet, copies it to another sheet, then deletes the data from the original sheet, and tells the user how many rows were moved. This code is listed below:
I have two problems. One, I need an error check where if the name that is typed into the textbox does not exist, it displays a messagebox with that message, and two, I need to allow for this code to do what it does, but from seperate workbooks. As in, copy the data from a sheet in one workbook, and move it to a sheet in another workbook. My code only works within the same workbook right now.
Any and all help is appreciated. Thank you in advance.
Private Sub buttonDelete_Click()
'When the Delete button is clicked, the following function is ran to copy the row from Current Services, move it to Cancelled Services
'and then delete the row from Current Services.
Dim wkBk1 As Workbook
Dim wkBk2 As Workbook
Dim xRg As Range
Dim xCell As Range
Dim I As Long
Dim J As Long
Dim K As Long
Dim count As Long
On Error Resume Next
Set wkBk1 = Workbooks.Open("C:\Users\Nathan\Desktop\Sandbox\testMacro.xlsm")
Set wkBk2 = Workbooks.Open("C:\Users\Nathan\Desktop\Sandbox\testMacro2.xlsm")
If Err.Number = 1004 Then
MsgBox "File Does Not Exist"
End If
I = wkBk1.Worksheets("Current Customers").UsedRange.Rows.count
J = Worksheets("Cancelled Services").UsedRange.Rows.count
count = 0
If J = 1 Then
If Application.WorksheetFunction.CountA(Worksheets("Cancelled Services").UsedRange) = 0 Then J = 0
End If
Set xRg = Worksheets("Current Customers").Range("A1:A" & I)
On Error Resume Next
Application.ScreenUpdating = False
For K = 1 To xRg.count
If CStr(xRg(K).Value) = Me.fName.Value Then
count = count + 1
xRg(K).EntireRow.Copy Destination:=Worksheets("Cancelled Services").Range("A" & J + 1)
xRg(K).EntireRow.Delete
If CStr(xRg(K).Value) = Me.fName.Value Then
K = K - 1
End If
J = J + 1
End If
Next
Application.ScreenUpdating = True
MsgBox count & " rows moved"
End Sub
EDIT : OK, here is one more wrinkle. What if I wanted to search through four or five different workbooks, and move all of the rows where the conditions are met into one worksheet called 'Cancelled Services' that will be a worksheet in one of the aforementioned workbooks.
There are a LOT of strange things going on in your code, so I've tried to clean it up a bit and left some comments as to why you shouldn't have some things in there. I've addressed the first part of your question, but in order to move rows between workbooks you need to decide what data you're looking to move and where, especially by fully qualifying your ranges using Workbook, or in your case, wkBk1 and wkBk2
Private Sub buttonDelete_Click()
'When the Delete button is clicked, the following function is ran to copy the row from Current Services, move it to Cancelled Services
'and then delete the row from Current Services.
Dim wkBk1 As Workbook, wkBk2 As Workbook
Dim xRg As Range, xCell As Range
Dim I As Long, J As Long, K As Long, count As Long
Dim MyName As String
'Assign our name value here
MyName = Me.fName.Value
'Let's use an error handler instead - this way our Err.Number will actually be triggered
On Error GoTo Handler
Set wkBk1 = Workbooks.Open("C:\Users\Nathan\Desktop\Sandbox\testMacro.xlsm")
Set wkBk2 = Workbooks.Open("C:\Users\Nathan\Desktop\Sandbox\testMacro2.xlsm")
On Error GoTo 0
I = wkBk1.Worksheets("Current Customers").UsedRange.Rows.count
J = Worksheets("Cancelled Services").UsedRange.Rows.count 'Need to add either wkBk1 or wkBk2 to the front of this
'We don't really NEED this, as count is initialized as 0 anyways
'count = 0
If J = 1 Then
'What is the purpose of this? Can it ever even return true if J = 1?
If Application.WorksheetFunction.CountA(Worksheets("Cancelled Services").UsedRange) = 0 Then J = 0
End If
Set xRg = Worksheets("Current Customers").Range("A1:A" & I) 'Need to add either wkBk1 or wkBk2 to the front of this
'Here we check the range for the name. If it's not there, we throw a messsage box and exit the sub
If Not WorksheetFunction.CountIf(xRg, MyName) > 0 Then
MsgBox "Name doesn't exist in the range"
Exit Sub
End If
'Got rid of On Error Resume Next, we don't need it and it's sloppy coding
Application.ScreenUpdating = False
'This whole snippet needs to be changed
'Also since you're deleting rows, you need to step BACKWARDS through this loop
For K = 1 To xRg.count
If CStr(xRg(K).Value) = MyName Then
xRg(K).EntireRow.Copy Destination:=Worksheets("Cancelled Services").Range("A" & J + 1)
xRg(K).EntireRow.Delete
'Why do we have this? We already know this is true?
'If CStr(xRg(K).Value) = MyName Then
K = K - 1
'End If
'Move count to AFTER we've actually moved the row, with On Error Resume Next your count could've gone up without a row being moved...
count = count + 1
J = J + 1
End If
Next
Application.ScreenUpdating = True
MsgBox count & " rows moved"
Handler:
If Err.Number = 1004 Then
MsgBox "File Does Not Exist"
End If
End Sub
This code uses FIND rather than looking at each row. It creates a range of all found rows as it goes and then copies the whole lot over in one hit before deleting the original values. Before ending it tells you how many it found.
This assumes that you have a textbox called fname and button called buttonDelete on a Userform.
Private Sub buttonDelete_Click()
Dim wrkBk1 As Workbook
Dim wrkBk2 As Workbook
Dim sPath As String
Dim wrkSht1 As Worksheet
Dim wrkSht2 As Worksheet
Dim rLastCell_Cur As Range
Dim rLastCell_Can As Range
Dim sNameToSearch As String
Dim rSearchRange As Range
Dim rFound As Range
Dim sFirstAddress As String
Dim lFoundCount As Long
Dim rFoundUnion As Range
sPath = "C:\Users\Nathan\Desktop\Sandbox\"
If Not (FileExists(sPath & "testMacro.xlsm") And FileExists(sPath & "testMacro2.xlsm")) Then
'One of the files doesn't exist so display message and exit.
MsgBox "One of the files does not exist.", vbOKOnly + vbCritical
Else
Set wrkBk1 = Workbooks.Open(sPath & "testMacro.xlsm")
Set wrkBk2 = Workbooks.Open(sPath & "testMacro2.xlsm")
If Not (WorkSheetExists("Current Customers", wrkBk1) And _
WorkSheetExists("Cancelled Services", wrkBk2)) Then
'One of the sheets doesn't exist so display message and exit.
MsgBox "One of the required sheets doesn't exist.", vbOKOnly + vbCritical
Else
'Find the limits of the two sheets.
Set wrkSht1 = wrkBk1.Worksheets("Current Customers")
Set rLastCell_Cur = LastCell(wrkSht1)
Set wrkSht2 = wrkBk2.Worksheets("Cancelled Services")
Set rLastCell_Can = LastCell(wrkSht2).Offset(1) 'We want the cell below the last cell here.
'Grab what we're searching for and where we're searching for it.
sNameToSearch = Me.fName
With wrkSht1
Set rSearchRange = .Range(.Cells(1, 1), .Cells(rLastCell_Cur.Row, 1))
End With
With rSearchRange
'Perform first search.
Set rFound = .Find(What:=sNameToSearch, LookIn:=xlValues, LookAt:=xlWhole, SearchDirection:=xlNext)
'If something was found then we're good to go.
If Not rFound Is Nothing Then
sFirstAddress = rFound.Address
Do
lFoundCount = lFoundCount + 1
'Create a union of ranges to copy over.
If rFoundUnion Is Nothing Then
Set rFoundUnion = rFound.EntireRow
Else
Set rFoundUnion = Union(rFoundUnion, rFound.EntireRow)
End If
'Look for the next item.
Set rFound = .FindNext(rFound)
Loop While rFound.Address <> sFirstAddress
'All instances have been found so copy it all over and then delete the original.
rFoundUnion.Copy wrkSht2.Cells(rLastCell_Can.Row, 1)
rFoundUnion.Delete Shift:=xlUp
End If
MsgBox "Found " & lFoundCount & " occurrences of " & sNameToSearch, vbOKOnly + vbInformation
End With
End If
End If
End Sub
Public Function FileExists(FilePath As String) As Boolean
FileExists = Dir(FilePath) <> ""
End Function
Public Function WorkSheetExists(SheetName As String, Optional WrkBk As Workbook) As Boolean
Dim wrkSht As Worksheet
If WrkBk Is Nothing Then
Set WrkBk = ThisWorkbook
End If
On Error Resume Next
Set wrkSht = WrkBk.Worksheets(SheetName)
WorkSheetExists = (Err.Number = 0)
Set wrkSht = Nothing
On Error GoTo 0
End Function
Public Function LastCell(wrkSht As Worksheet, Optional Col As Long = 0) As Range
Dim lLastCol As Long, lLastRow As Long
On Error Resume Next
With wrkSht
If Col = 0 Then
lLastCol = .Cells.Find("*", , , , xlByColumns, xlPrevious).Column
lLastRow = .Cells.Find("*", , , , xlByRows, xlPrevious).Row
Else
lLastCol = .Cells.Find("*", , , , xlByColumns, xlPrevious).Column
lLastRow = .Columns(Col).Find("*", , , , xlByColumns, xlPrevious).Row
End If
If lLastCol = 0 Then lLastCol = 1
If lLastRow = 0 Then lLastRow = 1
Set LastCell = wrkSht.Cells(lLastRow, lLastCol)
End With
On Error GoTo 0
End Function
This update to the buttonDelete_Click() procedure will open all Excel files within a specific folder and copy the found name to another file that isn't in that folder.
Private Sub buttonDelete_Click()
Dim colFiles As Collection
Dim vFile As Variant
Dim sTemp As String
Dim wrkBk1 As Workbook
Dim wrkBk2 As Workbook
Dim sPath As String
Dim wrkSht1 As Worksheet
Dim wrkSht2 As Worksheet
Dim rLastCell_Cur As Range
Dim rLastCell_Can As Range
Dim sNameToSearch As String
Dim rSearchRange As Range
Dim rFound As Range
Dim sFirstAddress As String
Dim lFoundCount As Long
Dim rFoundUnion As Range
sPath = "C:\Users\Nathan\Desktop\Sandbox\"
'Put the full path of each Excel file in to a collection.
'These contain the "Current Customers" sheet.
Set colFiles = New Collection
sTemp = Dir$(sPath & "*.xls*")
Do While Len(sTemp) > 0
colFiles.Add sPath & sTemp
sTemp = Dir$
Loop
If Not (FileExists("C:\Users\Nathan\Desktop\Cancelled.xlsx")) Then
'Cancelled Services book doesn't exist.
MsgBox "Cancelled Services doesn't exist.", vbOKOnly + vbCritical
Else
'Open Cancelled Services before working through the collection of Current Customers.
Set wrkBk2 = Workbooks.Open("C:\Users\Nathan\Desktop\Cancelled.xlsx")
Set wrkSht2 = wrkBk2.Worksheets("Cancelled Services")
For Each vFile In colFiles
Set wrkBk1 = Workbooks.Open(vFile)
'The file will only be processed if it contains "Current Customers" sheet.
If WorkSheetExists("Current Customers", wrkBk1) Then
Set wrkSht1 = wrkBk1.Worksheets("Current Customers")
Set rLastCell_Can = LastCell(wrkSht2).Offset(1)
Set rLastCell_Cur = LastCell(wrkSht1)
'Grab what we're searching for and where we're searching for it.
sNameToSearch = Me.fName
With wrkSht1
Set rSearchRange = .Range(.Cells(1, 1), .Cells(rLastCell_Cur.Row, 1))
End With
With rSearchRange
'Perform first search.
Set rFound = .Find(What:=sNameToSearch, LookIn:=xlValues, LookAt:=xlWhole, SearchDirection:=xlNext)
'If something was found then we're good to go.
If Not rFound Is Nothing Then
sFirstAddress = rFound.Address
Do
lFoundCount = lFoundCount + 1
'Create a union of ranges to copy over.
If rFoundUnion Is Nothing Then
Set rFoundUnion = rFound.EntireRow
Else
Set rFoundUnion = Union(rFoundUnion, rFound.EntireRow)
End If
'Look for the next item.
Set rFound = .FindNext(rFound)
Loop While rFound.Address <> sFirstAddress
'All instances have been found so copy it all over and then delete the original.
rFoundUnion.Copy wrkSht2.Cells(rLastCell_Can.Row, 1)
rFoundUnion.Delete Shift:=xlUp
End If
End With
End If
Set rFound = Nothing
Set rFoundUnion = Nothing
sFirstAddress = ""
wrkBk1.Close SaveChanges:=True
Next vFile
MsgBox "Found " & lFoundCount & " occurrences of " & sNameToSearch, vbOKOnly + vbInformation
End If
End Sub
To answer the first question about checking if a sheet exist in a workbook, you can use a Function like this:
Public Function U_W_DoesWorksheetExist(ByVal sheetname As String, aWorkbook As Workbook) As Boolean
On Error Resume Next
U_W_DoesWorksheetExist = (Not aWorkbook.Sheets(sheetname) Is Nothing)
On Error GoTo 0
End Function
Now, when you reference the destination, you are saying just Worksheets("Cancelled Services") and the Macro will assume the ActiveWorkook as the main workbook where to copy the Sheet. You need to reference the Workbook where the sheet you are pasting is located. See if the code below works for you and take a look at the comments I added on it:
Private Sub buttonDelete_Click()
'When the Delete button is clicked, the following function is ran to copy the row from Current Services, move it to Cancelled Services
'and then delete the row from Current Services.
Dim wkBk1 As Workbook
Dim wkBk2 As Workbook
Dim xRg As Range
Dim xCell As Range
Dim i As Long
Dim J As Long
Dim K As Long
Dim count As Long
Dim arrFromWorkbookPath(1 To 4) As String
Dim c As Long
' If you need more than 4 rearrange the Array to as many as you need.
arrFromWorkbookPath(1) = "C:\Users\Nathan\Desktop\Sandbox\FromWB1.xlsm"
arrFromWorkbookPath(4) = "C:\Users\Nathan\Desktop\Sandbox\FromWB2.xlsm"
arrFromWorkbookPath(3) = "C:\Users\Nathan\Desktop\Sandbox\FromWB3.xlsm"
arrFromWorkbookPath(4) = "C:\Users\Nathan\Desktop\Sandbox\FromWB4.xlsm"
' The Workbook were you will be pasting the sheets.
Set wkBk2 = Workbooks.Open("C:\Users\Nathan\Desktop\Sandbox\testMacro2.xlsm")
For c = LBound(arrFromWorkbookPath) To UBound(arrFromWorkbookPath)
On Error Resume Next
' Open the Workbook from where the sheet will be copied from.
Set wkBk1 = Workbooks.Open(arrFromWorkbookPath(c))
If Err.Number = 1004 Then
MsgBox "File Does Not Exist"
Exit Sub
End If
' USE PROCEDURE LIKE THIS TO CHECK "Current Customers" in wkBk1 and Cancelled Services in wkBk2.
If U_W_DoesWorksheetExist("Current Customers", wkBk1) And U_W_DoesWorksheetExist("Cancelled Services", wkBk1) Then
i = wkBk1.Worksheets("Current Customers").UsedRange.Rows.count
J = wkBk2.Worksheets("Cancelled Services").UsedRange.Rows.count
count = 0
If J = 1 Then
If Application.WorksheetFunction.CountA(wkBk2.Worksheets("Cancelled Services").UsedRange) = 0 Then J = 0
End If
Set xRg = wkBk1.Worksheets("Current Customers").Range("A1:A" & i)
On Error Resume Next
Application.ScreenUpdating = False
For K = 1 To xRg.count
If CStr(xRg(K).Value) = Me.fName.Value Then
count = count + 1
' Here you need to specify the workbook, not just the sheet wkBk2.Worksheets("Cancelled Services").
xRg(K).EntireRow.Copy Destination:=wkBk2.Worksheets("Cancelled Services").Range("A" & J + 1)
xRg(K).EntireRow.Delete
If CStr(xRg(K).Value) = Me.fName.Value Then
K = K - 1
End If
J = J + 1
End If
Next
wkBk1.Close False
Else
' Display error if the sheet doesn't exist.
MsgBox "Sheets Current Customers or Cancelled Services don't exists."
End If
Next c
Application.ScreenUpdating = True
End Sub

Excel VBA code error type mismatch using worksheetfunction to find duplicates

I get a
Type Mismatch Error "13"
with the below code. Can anyone assist with where I'm going wrong with my VBA syntax and use of variables.
If Application.WorksheetFuntion.CountIf(Target, r.Value) > 1 Then
I've tried the matchFoundIndex code method to no success...Likely due to incorrect VBA syntax.
The intent of the CountIf line is to look for duplicates in column A. The rest of the code loops through files and worksheets copying the file name, worksheet name, and cell C1 for further analysis. I am a novice at coding and I'm sure there may be Dimmed variables that I'm not using, other formatting, and errors that I have not found yet. Any Help would be appreciative.
Sub CopyFileAndStudyName()
Dim sPath As String, SName As String
Dim xlWB As Workbook
Dim sh As Worksheet
Dim lngRow As Long
Dim lngwsh As Long
Dim xlApp As Excel.Application
Dim sfile As String
Dim wbk As Workbook
Dim iCntr As Long
Dim matchFoundIndex As Long
Dim FindDuplicates As Boolean
Dim IsDup As Boolean
sPath = "C:\Users\mypath\"
' which row to begin writing to in the activesheet
lngRow = 2
SName = Dir(sPath & "*.xlsx") ' for xl2007 & "*.xls"
Set xlApp = New Excel.Application
xlApp.Visible = False
If MsgBox("Are you sure you want to copy all the file and Cell C1 in " & sPath & "?", vbYesNo) = vbNo Then Exit Sub
Do While SName <> ""
lngwsh = 1
' Will cycle through all .xlsx files in sPath
Set xlWB = xlApp.Workbooks.Open(sPath & SName, , True) ' opens in read-only mode
' Will cycle through first 3 of the worksheets in each file copying file name and cell C1 in columns C and D
For lngwsh = 1 To 3
Set sh = ActiveSheet
sh.Cells(lngRow, "A") = xlWB.Name
sh.Cells(lngRow, "B") = xlWB.Worksheets(lngwsh).Range("C1")
sh.Cells(lngRow, "C") = xlWB.Sheets(lngwsh).Name
Dim Target As Range
Dim r As Range
Dim lastRow As Long
Dim ws As Worksheet
Set ws = xlWB.Worksheets(lngwsh)
With ws
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
Set Target = ws.Range("A1:A" & lastRow)
End With
For Each r In Target
If r.Value <> "" Then
If Application.WorksheetFunction.CountIf(Target, r.Value) > 1 Then
FindDuplicates = True
Exit For
Else
FindDuplicates = False
End If
End If
Next r
Debug.Print FindDuplicates
IsDup = FindDuplicates
sh.Cells(lngRow, "D") = IsDup
FindDuplicates = False
End If
lngRow = lngRow + 1
Next lngwsh
xlWB.Close False
xlApp.Quit
SName = Dir()
Loop
MsgBox "Report Ready!"
End Sub
If you want to check for Duplicates in a Range, you can use a Dictionary object.
Dim Dict As Object
Set Dict = CreateObject("Scripting.Dictionary")
For Each r In Target
If Trim(r.Value) <> "" Then
If Not Dict.exists(r.Value) Then ' not found in dictionary >> add Key
Dict.Add r.Value, r.Value
FindDuplicates = False
Else ' found in Dictionary >> Exit
FindDuplicates = True
Exit For
nd If
End If
Next r
Sub CopyFileAndStudyName()
Dim sPath As String, SName As String
Dim xlWB As Workbook
Dim sh As Worksheet
Dim lngRow As Long
Dim lngwsh As Long
Dim xlApp As Excel.Application
Dim sfile As String
Dim wbk As Workbook
Dim iCntr As Long
Dim matchFoundIndex As Long
Dim FindDuplicates As Boolean
Dim IsDup As Boolean
sPath = "C:\Users\mypath\"
' which row to begin writing to in the activesheet
lngRow = 2
SName = Dir(sPath & "*.xlsx") ' for xl2007 & "*.xls"
Set xlApp = New Excel.Application
xlApp.Visible = False
If MsgBox("Are you sure you want to copy all the file and Cell C1 in " & sPath & "?", vbYesNo) = vbNo Then Exit Sub
Do While SName <> ""
lngwsh = 1
' Will cycle through all .xlsx files in sPath
Set xlWB = xlApp.Workbooks.Open(sPath & SName, , True) ' opens in read-only mode
' Will cycle through first 3 of the worksheets in each file copying file name and cell C1 in columns C and D
For lngwsh = 1 To 3
Set sh = ActiveSheet
sh.Cells(lngRow, "A") = xlWB.Name
sh.Cells(lngRow, "B") = xlWB.Worksheets(lngwsh).Range("C1")
sh.Cells(lngRow, "C") = xlWB.Sheets(lngwsh).Name
Dim Target As Range
Dim r As Range
Dim lastRow As Long
Dim ws As Worksheet
Set ws = xlWB.Worksheets(lngwsh)
With ws
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
Set Target = ws.Range("A1:A" & lastRow)
End With
For Each r In Target
If r.Value <> "" Then
If Application.WorksheetFunction.CountIf(Target, r.Value) > 1 Then
FindDuplicates = True
Exit For
Else
FindDuplicates = False
End If
End If
Next r
Debug.Print FindDuplicates
IsDup = FindDuplicates
sh.Cells(lngRow, "D") = IsDup
FindDuplicates = False
lngRow = lngRow + 1
Next lngwsh
xlWB.Close False
xlApp.Quit
SName = Dir()
Loop
MsgBox "Report Ready!"
End Sub
I was having a similar experience using CountIF and passing it a range. In my case I was using:
i = Application.WorksheetFunction.CountIf(ws.UsedRange, r.Value)
which was giving me a Type Mismatch error. I had seen other people having success with the first parameter wrapped in Range() so after a few tries I found out that this would work:
i = Application.WorksheetFunction.CountIf(Range(ws.UsedRange.Address), r.Value)
So, I suggest that you change your code to this and see if it works:
If Application.WorksheetFuntion.CountIf(Range(Target.Address), r.Value) > 1 Then

Vlookup on external workbook VBA

I don't know how it isn't working.
I have my active workbook. I want to run macros from active sheet.
1. I want to add 2 more columnes with headers . - works
2. I want to open external file, which is base in my vloop. - works
3. I want to use vloop to find my variable from active sheet in external workbook and save result in my active sheet
Sub ImpFPQ()
Application.ScreenUpdating = False
On Error Resume Next
Dim Imp_Row As Integer
Dim Imp_Col As Integer
Dim Baza1 As Workbook
Dim Baza2 As Workbook
Dim wksheet As Worksheet
Dim plik As Variant
Set wksheet = ActiveWorkbook.ActiveSheet
'add columns with names
wksheet.Columns("A:B").Insert Shift:=xlToRight
wksheet.Columns("A").Cells(1, 1) = "KOD"
wksheet.Columns("B").Cells(1, 1) = "LICZNIK"
'open file
plik = Application.GetOpenFilename(Title:="Wybierz raport")
If plik = False Then Exit Sub
Workbooks.Open Filename:=plik
Set Baza1 = ThisWorkbook 'activesheet
Set Baza2 = Workbooks(plik) 'external workbook
Set lastel = Baza2.Range("F3", Range("F3").End(xlDown)).Select
Set lookFor = Baza1.Cells(2, 4) 'aktualny subsyst do znalezienia
Set srchRange = Baza2.Sheets(1).Range("A3:lastel")
Range("A2").Value = Application.VLookup(lookFor, srchRange, 6, False)
Application.ScreenUpdating = True
MsgBox "Done!"
End Sub
I have these columns, but rows dont have results. Can someone help me?
This should do the trick.
Sub ImpFPQ()
Application.ScreenUpdating = False
On Error Resume Next
Dim Imp_Row As Integer
Dim Imp_Col As Integer
Dim Baza1 As Workbook
Dim Baza2 As Workbook
Dim wksheet As Worksheet
Dim plik As Variant
Dim lastRow As Long
Dim lookfor As Variant
Dim srchRange As Range
Set wksheet = ActiveWorkbook.ActiveSheet
'add columns with names
wksheet.Columns("A:B").Insert Shift:=xlToRight
wksheet.Columns("A").Cells(1, 1) = "KOD"
wksheet.Columns("B").Cells(1, 1) = "LICZNIK"
'open file
plik = Application.GetOpenFilename(Title:="Wybierz raport")
If plik = False Then Exit Sub
Workbooks.Open Filename:=plik
Set Baza1 = ThisWorkbook 'activesheet
Set Baza2 = Workbooks.Open(plik) 'external workbook
With Baza2.Sheets(1)
lastRow = .Cells(.Rows.Count, 6).End(xlUp).Row
End With
lookfor = Baza1.Cells(2, 4) 'aktualny subsyst do znalezienia
Set srchRange = Baza2.Sheets(1).Range("A3:F" & lastRow)
Range("A2").Value = Application.VLookup(lookfor, srchRange, 6, False)
Application.ScreenUpdating = True
MsgBox "Done!"
End Sub
Change this:
If plik = False Then Exit Sub
Workbooks.Open Filename:=plik
Set Baza1 = ThisWorkbook 'activesheet
Set Baza2 = Workbooks(plik) 'external workbook
To this:
If plik = False Then Exit Sub
Set Baza2 = Workbooks.Open(Filename:=plik)
Set Baza1 = ThisWorkbook 'activesheet
since plik is giving you a full filename (including a path) I don't think it can be used as an index for the Workbooks collection
See here: https://msdn.microsoft.com/en-us/vba/excel-vba/articles/workbook-object-excel

Search for column header, copy column and paste to master workbook

How can I copy the columns (data only) with these column header names "TOOL CUTTER" and "HOLDER" and paste them (as an append in just one column each with the same column header name) into another workbook sheet where the VBA code (Sheet Module) is. Thanks.
The column header HOLDER occurs in F10 (preferably written as (10, 6), and TOOL CUTTER is in G10 (10, 11) but it would be preferred to have it search for the header name and print whatever is in that column until it is completely empty (blank spaces may occur).
Any help is greatly appreciated!!
Working code: opens files in folder in a loop – opens file, prints name of file to Masterfile sheet, prints item J1 from file to Masterfile sheet, closes file, opens next file in the folder until all have been looped through.
Option Explicit
Sub LoopThroughDirectory()
Dim objFSO As Object
Dim objFolder As Object
Dim objFile As Object
Dim MyFolder As String
Dim Sht As Worksheet, ws As Worksheet
Dim WB As Workbook
Dim i As Integer
Dim LastRow As Integer, erow As Integer
Dim Height As Integer
Application.ScreenUpdating = False
MyFolder = "C:\Users\trembos\Documents\TDS\progress\"
Set Sht = Workbooks("masterfile.xlsm").Sheets("Sheet1")
'create an instance of the FileSystemObject
Set objFSO = CreateObject("Scripting.FileSystemObject")
'get the folder object
Set objFolder = objFSO.GetFolder(MyFolder)
i = 1
'loop through directory file and print names
For Each objFile In objFolder.Files
If LCase(Right(objFile.Name, 3)) = "xls" Or LCase(Left(Right(objFile.Name, 4), 3)) = "xls" Then
'print file name
Workbooks.Open Filename:=MyFolder & objFile.Name
Set WB = ActiveWorkbook
With WB
For Each ws In .Worksheets
Sht.Cells(i + 1, 1) = objFile.Name
With ws
.Range("J1").Copy Sht.Cells(i + 1, 4)
End With
i = i + 1
Next ws
.Close SaveChanges:=False
End With
End If
Next objFile
Application.ScreenUpdating = True
End Sub
Code I’m working on to try to print the values in the HOLDER and TOOL CUTTER columns (returns error Tool variable is not defined in line For Each Tool In TOOLList in the block that starts with the comment 'paste the TOOL list found back to this sheet :
Option Explicit
Sub LoopThroughDirectory()
Dim objFSO As Object
Dim objFolder As Object
Dim objFile As Object
Dim MyFolder As String
Dim StartSht As Worksheet, ws As Worksheet
Dim WB As Workbook
Dim i As Integer
Dim LastRow As Integer, erow As Integer
Dim Height As Integer
'Application.ScreenUpdating = False
MyFolder = "C:\Users\trembos\Documents\TDS\progress\"
Set StartSht = ActiveSheet
'create an instance of the FileSystemObject
Set objFSO = CreateObject("Scripting.FileSystemObject")
'get the folder object
Set objFolder = objFSO.GetFolder(MyFolder)
i = 2
'loop through directory file and print names
For Each objFile In objFolder.Files
If LCase(Right(objFile.Name, 3)) = "xls" Or LCase(Left(Right(objFile.Name, 4), 3)) = "xls" Then
'print file name
StartSht.Cells(i, 1) = objFile.Name
Dim NewWb As Workbook
Set NewWb = Workbooks.Open(Filename:=MyFolder & objFile.Name)
'print TDS values
With WB
For Each ws In .Worksheets
StartSht.Cells(i + 1, 1) = objFile.Name
With ws
.Range("J1").Copy StartSht.Cells(i + 1, 4)
End With
i = i + 1
Next ws
.Close SaveChanges:=False
End With
End If
'print CUTTING TOOL and HOLDER lists
Dim k As Long
Dim width As Long
Dim TOOLList As Object
Dim count As Long
Set TOOLList = CreateObject("Scripting.Dictionary")
Dim ToolRow As Integer 'set as As Long if more than 32767 rows
' search for all on other sheets
' Assuming header means Row 1
If objFile.Name <> "masterfile.xls" Then 'skip any processing on "Masterfile.xls"
For Each ws In NewWb.Worksheets 'assuming we want to look through the new workbook
With ws
width = .Cells(10, .Columns.count).End(xlToLeft).Column
For k = 1 To width
If Trim(.Cells(10, k).Value) = "CUTTING TOOL" Then
Height = .Cells(.Rows.count, k).End(xlUp).Row
If Height > 1 Then
For ToolRow = 2 To Height
If Not TOOLList.exists(.Cells(ToolRow, k).Value) Then
TOOLList.Add .Cells(ToolRow, k).Value, ""
End If
Next ToolRow
End If
End If
Next
End With
Next
End If
' paste the TOOL list found back to this sheet
With StartSht
width = .Cells(10, .Columns.count).End(xlToLeft).Column
For k = 1 To width
If Trim(.Cells(10, k).Value) = "CUTTING TOOL" Then
Height = .Cells(.Rows.count, k).End(xlUp).Row
count = 0
For Each Tool In TOOLList
count = count + 1
.Cells(Height + count, k).Value = Tool
Next
End If
Next
End With
'close current file, do not save changes
NewWb.Close SaveChanges:=False
i = i + 1
'move to next file
Next objFile
'Application.ScreenUpdating = True
End Sub
Refactoring some distinct tasks into separate functions keeps your code cleaner and easier to follow.
Compiled but untested:
Option Explicit
Sub LoopThroughDirectory()
Const SRC_FOLDER As String = "C:\Users\trembos\Documents\TDS\progress\"
Const ROW_HEADER As Long = 10
Dim f As String
Dim StartSht As Worksheet, ws As Worksheet
Dim WB As Workbook
Dim i As Integer
Dim dict As Object
Dim hc As Range, hc2 As Range, d As Range
Set StartSht = ActiveSheet
i = 3
f = Dir(SRC_FOLDER & "*.xls*", vbNormal) 'get first file name
'find the header on the master sheet
Set hc2 = HeaderCell(StartSht.Cells(ROW_HEADER, 1), "CUTTING TOOL")
If hc2 Is Nothing Then
MsgBox "No header found on master sheet!"
Exit Sub
End If
'loop through directory file and print names
Do While Len(f) > 0
If f <> ThisWorkbook.Name Then
Set WB = Workbooks.Open(SRC_FOLDER & f)
For Each ws In WB.Worksheets
StartSht.Cells(i, 1) = f
ws.Range("J1").Copy StartSht.Cells(i, 4)
i = i + 1
'find the header on the source sheet
Set hc = HeaderCell(ws.Cells(ROW_HEADER, 1), "CUTTING TOOL")
If Not hc Is Nothing Then
Set dict = GetUniques(hc.Offset(1, 0))
If dict.count > 0 Then
Set d = StartSht.Cells(Rows.count, hc2.Column).End(xlUp).Offset(1, 0)
'add the values to the master list
d.Resize(dict.count, 1).Value = Application.Transpose(dict.keys)
End If
Else
'header not found on source worksheet
End If
Next ws
WB.Close savechanges:=False
End If 'not the master file
f = Dir() 'next file
Loop
End Sub
'get all unique column values starting at cell c
Function GetUniques(ch As Range) As Object
Dim dict As Object, rng As Range, c As Range, v
Set dict = CreateObject("scripting.dictionary")
For Each c In ch.Parent.Range(ch, ch.Parent.Cells(Rows.count, ch.Column).End(xlUp)).Cells
v = Trim(c.Value)
If Len(v) > 0 And Not dict.exists(v) Then
dict.Add v, ""
End If
Next c
Set GetUniques = dict
End Function
'find a header on a row: returns Nothing if not found
Function HeaderCell(rng As Range, sHeader As String) As Range
Dim rv As Range, c As Range
For Each c In rng.Parent.Range(rng, rng.Parent.Cells(rng.Row, Columns.count).End(xlToLeft)).Cells
If Trim(c.Value) = sHeader Then
Set rv = c
Exit For
End If
Next c
Set HeaderCell = rv
End Function
Are the values "TOOL CUTTER" and "HOLDER" always on row 10? Will there always be values in these columns? Do you need to allow for exceptions other than blank values in the column?
Meanwhile, here's a few things to try:
Sub macro1()
Dim Sht As Worksheet
Dim LR As Integer, FR As Integer, ToolCol As Integer
Set Sht = ActiveSheet
With Sht 'Find column with TOOL CUTTER:
ToolCol = Application.WorksheetFunction.Match("TOOL CUTTER", .Range("10:10"), 0)
LR = .Cells(.Rows.Count, ToolCol).End(xlUp).Row 'Find last row with data in TOOL CUTTER column:
.Range(.Cells(11, ToolCol), .Cells(LR, ToolCol)).Copy
End With
End Sub