VBA Excel - find a value in a column, paste to another sheet - vba

I've got a folder of excel worksheets, and also another worksheet with a column whose entries correspond to the file names of the worksheets in the folder.
The column to the right of the worksheet names has a number, which I want to paste into each corresponding worksheet... but it's not working... here's my code so far :
Sub FraisRank()
Dim folderPath As String
Dim filename As String
Dim filenameshort As String
Dim wb As Workbook
Dim fraislist As Workbook
Dim find As Range
Dim sel As Range
folderPath = "C:\Users\richard\Desktop\temp"
If Right(folderPath, 1) <> "\" Then folderPath = folderPath + "\"
Set fraislist = Workbooks.Open("C:\Users\richard\desktop\frais list.xlsx")
filename = Dir(folderPath & "*.*")
Do While filename <> ""
Application.ScreenUpdating = False
Set wb = Workbooks.Open(folderPath & filename)
filenameshort = Left(filename, Len(filename) - 4)
Set sel = fraislist.Sheets(1).Range("A1:A164")
Set find = sel.find(What:=filenameshort, After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If find Is Nothing Then
MsgBox ("Cell " & filenameshort & " not found")
Else
find.Offset(, 1).Resize(1, 1).Copy
ActiveSheet.Range("$H$5").PasteSpecial Paste:=xlPasteValues
End If
ActiveWorkbook.Save
ActiveWorkbook.Close
filename = Dir
Loop
End Sub
For the moment I'm getting a Runtime error '13', type mismatch on the 'Set find = ...' part. And in general I don't really understand how to run the '.find' on the selected cells in the 'fraislist' workbook...

The problem with ActiveCell is that it will always refer to the Activesheet and hence statements like Activecell/Select/Activate/ActiveSheet/Activeworkbook should be avoided. Always create relevant objects and work with them
INTERESTING READ
In your case it is not necessary that the ActiveSheet is fraislist.Sheets(1) so ActiveCell might not be referring to the correct sheet and hence, it's better to qualify it completely.
If you change After:=ActiveCell to After:=fraislist.Sheets(1).Range("A1") then your code will refer to the correct sheet and it will work.

Related

VBA doesn't work when looping through files (subscript out of range)

I just have started using vba.
Googled for a long time to find an answer.
I have written code for copying cells from one sheet into new one.
I have to do it for every file in a folder.
So I try to use looping. However in a middle of a process error occurs (subscript out of range)
Here is my code that works for one file.
Sub add()
Sheets.add.Name = "Good"
GetBook = ActiveWorkbook.Name
Sheets("Good").Range("A1") = GetBook
Sheets("Report Details").Range("E6:E8").Copy
With Sheets("Good").Range("B1")
.PasteSpecial Paste:=xlPasteValues, Transpose:=True
End With
Sheet2.Activate
Range(Range("A1").End(xlDown), Range("H1").End(xlDown)).Copy
With Sheets("Good").Range("E1")
.PasteSpecial Paste:=xlPasteValues
.PasteSpecial Paste:=xlPasteFormats
End With
End Sub
Here I try to loop it but it doesn't work, problem occurs here in the first code when looping
With Sheets("Good").Range("E1")
Looping code
FolderPath = "C:\Users\Maxim Osipov\Documents\Mckinsey\BorisT\Project 3(Smart city solutions)\VBA collecting" 'change to suit
If Right(FolderPath, 1) <> "\" Then FolderPath = FolderPath + "\"
Filename = Dir(FolderPath & "*.xlsx")
Do While Filename <> ""
Application.ScreenUpdating = False
Set wb = Workbooks.Open(FolderPath & Filename)
'Call a subroutine here to operate on the just-opened workbook
Call add
Filename = Dir
Loop
Application.ScreenUpdating = True
End Sub
Try this slight variation:
Sub add()
'Sheets.add.Name = "Good"
Sheets("Good").Range("A1") = ActiveWorkbook.Name
Sheets("Report Details").Range("E6:E8").Copy
Sheets("Good").Range("B1").PasteSpecial Paste:=xlPasteValues, Transpose:=True
Sheets("Sheet2").Range(Range("A1").End(xlDown).Address, Range("H1").End(xlDown).Address).Copy
Sheets("Good").Range("E1").PasteSpecial Paste:=xlPasteValues
Sheets("Good").Range("E1").PasteSpecial Paste:=xlPasteFormats
End Sub
See also:
Microsoft : Range Object (Excel)
10 ways to reference Excel workbooks and sheets using VBA
MSDN : Refer to Sheets by Name
MSDN : How to Reference Cells and Ranges
MSDN : Range.Copy Method
I was having some trouble figuring out which workbook some of your sheets were in - the ones being opened, or the one being pasted to.
This code will loop through the xlsx files in your folder and copy the ranges to the workbook holding the VBA code.
I added a function to check if the Good worksheet already exists and use that if it does.
Public Sub Main()
Dim FolderPath As String
Dim FileName As String
Dim WB As Workbook
Dim WS As Worksheet
FolderPath = "C:\Users\Maxim Osipov\Documents\Mckinsey\BorisT\Project 3(Smart city solutions)\VBA collecting\"
FileName = Dir(FolderPath & "*.xls*")
Do While FileName <> ""
Set WB = Workbooks.Open(FolderPath & FileName, False, True) 'Not updating links & is read-only.
'You can't create two sheets with the same name,
'so check if it exists first.
If WorkSheetExists("Good") Then
Set WS = ThisWorkbook.Worksheets("Good")
Else
'Add a worksheet to the workbook holding this code.
Set WS = ThisWorkbook.Worksheets.Add
WS.Name = "Good"
End If
'Pass the workbook and worksheet references to the procedure.
Add WB, WS
WB.Close SaveChanges:=False
FileName = Dir
Loop
End Sub
Public Sub Add(WrkBk As Workbook, wrkSht As Worksheet)
Dim LastCell As Range
Dim LastRow As Long
With wrkSht
'Find the last cell.
'You could use "LastRow = .Cells(.Rows.Count, "B").End(xlUp).Row"
'but not sure how much data is in the Sheet2.
Set LastCell = .Cells.Find("*", _
After:=.Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False)
If LastCell Is Nothing Then
LastRow = 1
Else
LastRow = LastCell.Row + 1
End If
.Cells(LastRow, 1) = WrkBk.Name
WrkBk.Worksheets("Report Details").Range("E6:E8").Copy
.Cells(LastRow, 2).PasteSpecial Paste:=xlPasteValues, Transpose:=True
With WrkBk.Worksheets(2)
.Range(.Cells(1, 1), .Cells(.Rows.Count, "H").End(xlUp)).Copy
End With
With .Cells(LastRow, "E")
.PasteSpecial Paste:=xlPasteValues
.PasteSpecial Paste:=xlPasteFormats
End With
End With
End Sub
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
If you can only use the Sheet2 reference in the workbook being opened this function will find it:
Public Function GetWorkSheet(sCodeName As String, Optional wrkBook As Workbook) As Worksheet
Dim wrkSht As Worksheet
If wrkBook Is Nothing Then
Set wrkBook = ThisWorkbook
End If
For Each wrkSht In wrkBook.Worksheets
If wrkSht.CodeName = sCodeName Then
Set GetWorkSheet = wrkSht
Exit For
End If
Next wrkSht
End Function
To use it just change this line at the bottom of the Add procedure:
With WrkBk.Worksheets(2)
to
With GetWorkSheet("Sheet2", WrkBk)
It's best practice (and warmly recommended) not to use Activate/ActiveXXX/Select/Selection pattern and take advantage of fully qualified range reference up to workbook one
so you could refactor your add() sub as follows (explanations in comments):
Option Explicit
Sub add(ws As Worksheet)
Dim repDetRngToCopy As Range, sht2RngToCopy As Range
With ws 'reference passed worksheet
Set repDetRngToCopy = .Parent.Worksheets("Report Details").Range("E6:E8") 'set needed range in "Report Details" worksheet of the same workbook the currently referenced sheet (i.e. the passed one) belongs to
With .Parent.Worksheets(2) 'reference Sheet2 worksheet of the same workbook the currently referenced sheet belongs to
Set sht2RngToCopy = .Range(Range("A1").End(xlDown), .Range("H1").End(xlDown)) 'set needed range in currently referenced sheet (i.e. Sheet2)
End With
'now start filling cells of referenced sheet (i.e. the passed one)
.Range("A1") = .Name
repDetRngToCopy.Copy 'copy from the range previously defined in "Report Details"
.Range("B1").PasteSpecial Paste:=xlPasteValues, Transpose:=True ' paste in currently referenced sheet
sht2RngToCopy.Copy 'copy from the range previously defined in Sheet2
.Range("E1").PasteSpecial Paste:=xlPasteValues + xlPasteFormats 'paste in currently referenced sheet
.Name = "Good" ' name currently referenced sheet
End With
End Sub
and consequently slightly change your "main" sub where you call it as follows:
Do While Filename <> ""
'Call a subroutine here to operate on the just-opened workbook
With Workbooks.Open(FolderPath & Filename) ' open and reference a new workbook
add .Sheets.add ' call add passing it a reference to a new sheet in referenced workbook (i.e. the newly opened one)
.Close True ' close referenced workbook saving changes
End With
Filename = Dir
Loop

Reference variable worksheet in another workbook in formula SUMIFS using VBA

I am trying to add a SUMIFS formula to cells, whereby I need to have variable which refers to another sheet in another workbook. However, this does not work in my code. FYI, I have already opened the workbook with msoFileDialogFilePicker.
Here is the part I have to add the formula, ws is an object of worksheet:
Set fd = Application.FileDialog(msoFileDialogFilePicker)
With fd
.Title = "..."
.AllowMultiSelect = False
If .Show = True Then
If .SelectedItems(1) <> vbNullString Then
Filename = .SelectedItems(1)
End If
Else
Exit Sub
End If
Set wb = GetObject(Filename)
Set ws = wb.Sheets("ASEAN (EUR)")
With ws
YTDws.Range("$A$1:$D$" & usedRow).AutoFilter Field:=1, Criteria1:="Singapore"
If .Range("Q20").Value = "SINGAPORE" Then
usedRow2 = YTDws.Cells(Rows.Count, 1).End(xlUp).Row
Set rng = Range("C2:C" & usedRow2)
For Each cl In rng.SpecialCells(xlCellTypeVisible)
cl.Select
cl.FormulaR1C1 = "=SUMIFS('&ws.name&'!R[32]C[3]:R[243]C[3],'&ws.name&'!C[2], C[-1])"
Next cl
End If
End With
End With
I believe the problem lies with this line of code:
cl.FormulaR1C1 = "=SUMIFS('&ws.name&'!R[32]C[3]:R[243]C[3],'&ws.name&'!C[2], C[-1])"
I have searched online of many examples of doing and this one I got it from
this post, where I found it similar to my case. However, it seems still not working, the program prompts me to select sheets from the workbook and saying it cannot find the worksheet ws.name. After I select the worksheet, the formula looks like this in the cell: =SUMIFS('[&ws.name&]ASEAN (EUR)'!R[32]C[3]:R[243]C[3],'[&ws.name&]ASEAN (EUR)'!C[2], C[-1]).
Please help me to locate the problem, thanks in advance!
Couple of things
Which sheet are you referring to here Set rng = Range("C2:C" & usedRow2)
The SumIfs() syntax is =SUMIFS(Sum_range,Criteria_range1,Criteria1 ....).
When using SUMIFS() with an external workbook, the formula looks like this
"=SUMIFS('[" & wb.Name & "]" & ws.Name & "'!" & YOUR_SUM_RANGE & ",'[" & wb.Name & "]" & ws.Name & "'!" & YOUR_CRITERIA_RANGE & "," & YOUR_CRITERIA & ")"
You are only using the worksheet name and not the workbook name.
My suggestion: Manually open the workbook and enter the formula (again manually). Check if the formula works and then use that formula in your code
probably you have error in join/concat string...
if you join/concat string you need to use &. for example:
sheetname = "mysheet"
Range("D5").FormulaR1C1 = "=SUM(" & sheetname & "!C[-2])"

Ungroup Sheets from an array in VBA

I've been trying to get an easy printout (in PDF using a single button) of one sheet with only active range and one chart located in another sheet. I've got everything working, except after I print, both sheets are grouped together and I can't edit my chart.
I'm trying to make this foolproof and easy for coworkers during real time operations. Right now I can right-click and select 'Ungroup sheets' to fix it, but I hate to have to do that each time (or explain that it needs to be done).
I tried to select a sheet, a different sheet, only one sheet etc. I can't figure out how to get VBA to ungroup the sheets at the end. Any ideas?
Sub CustomPrint()
'if statement to ask for file path
If Dir(Environ("commonprogramfiles") & "\Microsoft Shared\OFFICE" _
& Format(Val(Application.Version), "00") & "\EXP_PDF.DLL") <> "" Then
If FixedFilePathName = "" Then
'Open the GetSaveAsFilename dialog to enter a file name for the PDF file.
FileFormatstr = "PDF Files (*.pdf), *.pdf"
fname = Application.GetSaveAsFilename("", filefilter:=FileFormatstr, _
Title:="Create PDF")
'If you cancel this dialog, exit the function.
If fname = False Then Exit Sub
Else
fname = FixedFilePathName
End If
'Dynamic reference to RT drilling data
Dim LastRow As Long
Dim LastColumn As Long
Dim StartCell As Range
Dim sht As Worksheet
Set sht = Worksheets("rt drilling data")
Set StartCell = Range("A1")
'Refresh UsedRange
Worksheets("rt drilling data").UsedRange
'Find Last Row
LastRow = sht.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
'Select Range
sht.Range("A1:K" & LastRow).Select
Sheets("Chart Update").Activate
ActiveSheet.ChartObjects(1).Select
ThisWorkbook.Sheets(Array("chart update", "RT drilling data")).Select
ActiveSheet.ExportAsFixedFormat Type:=xlTypePDF, _
Filename:=fname, IgnorePrintAreas:=False
'If the export is successful, return the file name.
If Dir(fname) <> "" Then RDB_Create_PDF = fname
End If
If OverwriteIfFileExist = False Then
If Dir(fname) <> "" Then Exit Sub
End If
On Error GoTo 0
Worksheets("ws model updates").Select
End Sub
If Dir(fname) <> "" Then Exit Sub will bypass Worksheets("ws model updates").Select
If OverwriteIfFileExist = False Then
If Dir(fname) <> "" Then
Worksheets("ws model updates").Select
Exit Sub
End If
End If

Search based on contents of cell

I have a command button set up, and I assigned a macro to it. I need the button to take the contents of cell B2 and search for it in column A on the next sheet. Here is my code. As you can see, it's looking for the literal text that was there when I recorded the macro. How do I get that to search for whatever is entered into B2?
Sub Button3_Click()
Range("B2").Select
Selection.Copy
Sheets("Sheet3").Select
Cells.Find(What:="", After:=ActiveCell, LookIn:=xlFormulas, LookAt:= _
xlWhole, SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False).Activate
End Sub
This might be overkill for what you need, but I've always believed in error checking as well as using complete and flexible code, so here's what you asked for, with comments:
Sub btnFindText()
'Declare variables
Dim wb As Workbook 'Used to store and reference the ActiveWorkbook
Dim wsActive As Worksheet 'Used to store and reference the ActiveSheet (the sheet containing the button)
Dim wsNext As Worksheet 'Used to store and reference the next sheet
Dim rngFound As Range 'Used to find a matching cell in the next sheet, if any
Dim rngText As Range 'Used to store and reference the cell that will contain the text
Dim sFind As String 'Used to store and reference the text in wsActive, cell B2
'Set variables
Set wb = ActiveWorkbook
Set wsActive = wb.ActiveSheet
Set rngText = wsActive.Range("B2")
sFind = wsActive.Range("B2").Value
'Perform error checking and return appropriate errors
'Check if text to search for was provided
If Len(sFind) = 0 Then
rngText.Select
MsgBox "No text provided in cell " & rngText.Address(0, 0), , "No Search Value"
Exit Sub
End If
'Check if there is a sheet after the activesheet
If wsActive.Index = wb.Sheets.Count Then
MsgBox "There is not a sheet after this one to search on", , "Next Sheet Unavailable"
Exit Sub
End If
'Next sheet found, set the wsNext variable and search for the text
Set wsNext = wb.Sheets(wsActive.Index + 1)
Set rngFound = wsNext.Columns("A").Find(sFind, , , xlWhole)
'Check if anything was found
If rngFound Is Nothing Then
'Nothing found, return error
MsgBox "No matches found for [" & sFind & "] within column A of " & wsNext.Name, , "No Matches"
Else
'Match found, prompt if user wants to go to its location
If MsgBox("Match found for [" & sFind & "] at '" & wsNext.Name & "'!" & rngFound.Address(0, 0) & Chr(10) & "Go to cell?", vbYesNo, "Match Found") = vbYes Then
wsNext.Activate
rngFound.Select
End If
End If
End Sub
Additionally, you can do this with an Inputbox instead of using cell B2 as the text entry. The code is mostly the same, I'm putting it here for you to compare/contrast, as well as hopefully learn how to do both methods. Note that this method doesn't require to check if there's a next sheet, because we're not using an input cell. It only needs to know what sheet to search on.
Sub btnFindText2()
'Declare variables
Dim wb As Workbook 'Used to store and reference the ActiveWorkbook
Dim wsSearch As Worksheet 'Used to store and reference the worksheet that will be searched
Dim rngFound As Range 'Used to find a matching cell in the next sheet, if any
Dim sFind As String 'Used to get the search text from an inputbox
'Set variables
Set wb = ActiveWorkbook
Set wsSearch = wb.Sheets("Sheet3") 'In your provided sample code, you searched on Sheet3. Update this to correct sheetname
sFind = InputBox("Enter Part Number:")
'Perform error checking and return appropriate errors
'Check if text to search for was provided
If Len(sFind) = 0 Then Exit Sub 'Pressed cancel
'Because we're using an inputbox, no need to use the Next Sheet stuff
'Just need to search for the text
Set rngFound = wsSearch.Columns("A").Find(sFind, , , xlWhole)
'Check if anything was found
If rngFound Is Nothing Then
'Nothing found, return error
MsgBox "No matches found for [" & sFind & "] within column A of " & wsSearch.Name, , "No Matches"
Else
'Match found, prompt if user wants to go to its location
If MsgBox("Match found for [" & sFind & "] at '" & wsSearch.Name & "'!" & rngFound.Address(0, 0) & Chr(10) & "Go to cell?", vbYesNo, "Match Found") = vbYes Then
wsSearch.Activate
rngFound.Select
End If
End If
End Sub
EDITS: Updated the Inputbox method code so that it doesn't use the wsNext portions, made minor adjustments in code for clarity, readability, and debugging.
Just pass Range("B2") as the value for the What parameter. For example:
Dim r As Range
Set r = Sheets("Sheet3").Range("A:A").Find(What:=Range("B2"), _
LookIn:=xlFormulas, LookAt:=xlWhole, MatchCase:=False)
If Not r Is Nothing Then
Debug.Print "Found at " & r.Address
' If you want to activate it...
r.Activate
End If
If you want to search for values instead of formulas (which is what your original macro was doing), replace xlFormulas with xlValues.

Find and replace char in multiple csv files

I have several csv files in one folder. I want to add these files into one excel files with multiple worksheets.
Before I add them in my excel sheet I want to replace . with ,, because of my convention in excel.
However, the code below gives me:
Here is my code:
Option Explicit
Sub ImportCSVs()
'Summary: Import all CSV files from a folder into separate sheets
' named for the CSV filenames
Dim fPath As String
Dim fCSV As String
Dim fnd As Variant
Dim rplc As Variant
Dim wbCSV As Workbook
'add your find and replace values!
'#############################
fnd = "."
rplc = ","
Application.ScreenUpdating = False 'speed up macro
'path to CSV files, include the final \
'#############################
fPath = "C:\Users\Desktop\Data\23-3-2015_Data\"
fCSV = Dir(fPath & "*.csv") 'start the CSV file listing
Do While Len(fCSV) > 0
Set wbCSV = Workbooks.Open(fPath & fCSV) 'open a CSV file and move
'find and replace the . by ,
For Each wbCSV In ActiveWorkbook.Worksheets
wbCSV.Cells.Replace what:=fnd, Replacement:=rplc, _
LookAt:=xlPart, SearchOrder:=xlByRows, MatchCase:=False, _
SearchFormat:=False, ReplaceFormat:=False
Next wbCSV
ActiveSheet.Move After:=ThisWorkbook.Sheets(Sheets.Count)
fCSV = Dir 'ready next CSV
Loop
Application.ScreenUpdating = True
Set wbCSV = Nothing
End Sub
Any recommendation what I am doing wrong?
I appreciate your reply!
Try this:
'add to your Dim statements:
Dim ws as Worksheet
'change your Do loop to:
Do While Len(fCSV) > 0
Set wbCSV = Workbooks.Open(fPath & fCSV) 'open a CSV file and move
'find and replace the . by ,
For Each ws In wbcsv
ws.Cells.Replace what:=fnd, Replacement:=rplc, _
LookAt:=xlPart, SearchOrder:=xlByRows, MatchCase:=False, _
SearchFormat:=False, ReplaceFormat:=False
Next ws
Also, I find it's easier to debug things if you leave the Application.ScreenUpdating = False commented out until everything's working. You're not all that worried about execution speed when you're debugging.
An alternative loop since you're opening a CSV, there can only be one worksheet in it, this should simplify things a bit:
Dim DestBook as workbook
Set DestBook = ThisWorkbook
'other setup stuff...
Do While Len(fCSV) > 0
Set wbCSV = Workbooks.Open(fPath & fCSV) 'open a CSV file and move
'find and replace the . by ,
wbCSV.worksheet(1).Cells.Replace what:=fnd, Replacement:=rplc, _
LookAt:=xlPart, SearchOrder:=xlByRows, MatchCase:=False, _
SearchFormat:=False, ReplaceFormat:=False
wbcsv.move after:=DestBook.sheets(DestBook.sheets.count)
wbCSV.close
vFCSV = Dir
Loop
I also noted that the move was moving it to ThisWorkbook and there's no guarantee what that would be when you got there. So, I declared a new WorkBook variable and assigned it to ThisWorkbook before doing anything, that way you're 100% certain where you're moving it to. I also closed the CSV that we opened, just for some tidying up.
In the error line For Each wbCSV In ActiveWorkbook.Worksheets you want to loop through all the worksheets but you are using wbCSV which is declared As Workbook.
To solve the type mismatch add a new variable Dim wsCSV As Worksheet and use this new variable in the loop as a reference for each worksheet.
The loop could look like this :
For Each wsCSV In wbCSV.Worksheets
wsCSV.Cells.Replace what:=fnd, Replacement:=rplc, _
LookAt:=xlPart, SearchOrder:=xlByRows, MatchCase:=False, _
SearchFormat:=False, ReplaceFormat:=False
Next wsCSV
wbCSV is workbook object not worksheet .
try this
Dim ws as Worksheet
for each ws in activeworkbook.worksheets
I don't know your method for going through wbCSV, never did it but doesn't seem right...
I cant suggest you use what I posted here just by switching "xml" to "csv" :
https://stackoverflow.com/questions/29184595/loop-on-all-files-in-the-same-directory-then-detect-extension-type/29187762#29187762
I think it'll be a rather good start for what you have to do! ;)