I have a list of files with the same structure and I want to extract some information from columns A, B, and C and print it to another workbook.
I found a way to do it for a single file, but now I don't understand how can I do it using the list of given files. I tried using collections, but it doesn't work.
Here's what I came up with:
Sub Pulsante1_Click()
Dim FileGeStar As Variant
Dim myCol As Collection
Set myCol = New Collection
myCol.Add "C:\Users\xxx\Desktop\articoli_def.xlsx"
myCol.Add "C:\Users\xxx\Desktop\pippo\SS20_def_ENG.xlsx"
For Each FileGeStar In myCol
Workbooks.Open Filename:=FileGeStar
FileGeStar = Application.ActiveWorkbook.Name
Dim Code As String
Dim Description As String
Dim FilePath As String
Dim i As Long
i = 2
While Range("A" & i) <> ""
FilePath = Application.ActiveWorkbook.Path
Code = Trim(Range("A" & i).Value)
Description = Trim(Range("B" & i).Value)
Workbooks("Report.xlsm").Worksheets(1).Range("A" & i).Value = FilePath
Workbooks("Report.xlsm").Worksheets(1).Range("B" & i).Value = Code
Workbooks("Report.xlsm").Worksheets(1).Range("C" & i).Value = Description
i = i + 1
Wend
Next FileGeStar
End Sub
What can I do?
This might look like an overkill, but I hope the code and comment's are self explanatory:
Option Explicit
Sub Pulsante1_Click()
Dim DestinationWorkbook As Workbook
Set DestinationWorkbook = ThisWorkbook 'I think report.xlsm is the workbook running the code
'if report.xlsm is not the workbook running the code then change thisworkbook for workbooks("Report.xlsm")
'add as many paths as you need to, another way would be to write them in a sheet and loop through to fill the array
Dim MyPaths As Variant
MyPaths = Array("C:\Users\xxx\Desktop\articoli_def.xlsx", "C:\Users\xxx\Desktop\pippo\SS20_def_ENG.xlsx")
'Declare a workbook variable for the source workbooks
Dim SourceWorkbook As Workbook
'Declare a long variable to loop through your path's array
Dim i As Long
'loop through the start to the end of your array (will increase as the array does)
For i = LBound(MyPaths) To UBound(MyPaths)
Set SourceWorkbook = OpenWorkbook(MyPaths(i)) 'this will set the workbook variable and open it
CopyData SourceWorkbook, DestinationWorkbook 'this will copy the data to your destination workbook
SourceWorkbook.Close , False
Set SourceWorkbook = Nothing
Next i
End Sub
Private Function OpenWorkbook(FullPath As String) As Workbook
Set OpenWorkbook = Workbooks.Open(FullPath, False, True)
End Function
Private Sub CopyData(wbO As Workbook, wbD As Workbook)
'this procedure calculates the last row of your source workbook and loops through all it's data
'later calls the AddDataToMasterWorkbook procedure to paste the data
With wbO.Sheets(1) 'Im assuming your source workbook has the data on sheet1
Dim LastRow As Long
LastRow = .Cells(Rows.Count, "A").End(xlUp).Row
Dim FilePath As String
FilePath = wbO.Path
Dim Code As String
Dim Description As String
Dim C As Range
For Each C In .Range("A2:A" & LastRow) 'this will loop from A2 to the last row with data
Code = Trim(C)
Description = Trim(C.Offset(, 1))
AddDataToMasterWorkbook wbD, FilePath, Code, Description
Next C
End With
End Sub
Private Sub AddDataToMasterWorkbook(wb As Workbook, FilePath As String, Code As String, Description As String)
'This procedure calculates the last row without data and adds the items you need every time
With wb.Sheets(1)
Dim LastRow As Long
LastRow = .Cells(Rows.Count, "A").End(xlUp).Row + 1
.Range("A" & LastRow) = FilePath
.Range("B" & LastRow) = Code
.Range("C" & LastRow) = Description
End With
End Sub
To loop though files, you can indeed use a collection, or an array, you can also loop through all files in directory with certain extension, or partial file name. Check out DIR function.
Best not to use ActiveWorkbook, better approach would be to set a workbook object like so: Set wb = Workbooks.Open(fullPathToYourWorkbook).
For what you're doing, there's no need to go row by row, much more efficient way would be to copy entire range, not only it's a lot quicker but also it's only 1 line of code; assuming your destination is ThisWorkbook.Sheets(1) and wb object is set: wb.Range("A:C").Copy Destination:=Thisworkbook.Sheets(1).Range("A:C"). If you need to edit copied data (trim or whatever) consider Range Replace method.
However, if you want to go row by row for whatever reason, as BigBen mentioned in the comment - get rid of While loop.
It's a good idea to set Application.ScreenUpdating to False when opening/closing workbooks, then back to True once it's all done. It will prevent user from accidentaly clicking something etc and will make it look like it's not opening any workbook.
Here's my approach (untested) assuming the workbook you want to copy data to is Workbooks("Report.xlsm").Worksheets(1):
Sub Pulsante1_Click()
'set workbook object for the destination workbook
set wb_dest = Workbooks("Report.xlsm").Worksheets(1)
'disable screen updating
Application.ScreenUpdating = False
For Each target_wb In Array("C:\Users\xxx\Desktop\articoli_def.xlsx", "C:\Users\xxx\Desktop\pippo\SS20_def_ENG.xlsx")
'set wb object and open workbook
Set wb = Workbooks.Open(target_wb)
'find last row in this workbooks in columns A:B (whichever is greater)
LastRow = wb.Range("A:B").Find(What:="*", After:=wb.Range("A1"), SearchDirection:=xlPrevious).row
'copy required data
wb.Range("A1:B" & LastRow).Copy Destination:=wb_dest.Range("B1:C" & LastRow)
'fill column A with path to the file
wb_dest.Range("A1:A" & LastRow).Value = wb.Path
'close workbook
wb.Close False
Next
'enable screen updating
Application.ScreenUpdating = True
End Sub
Obviously an array is not the best approach if you have loads of different files, collection would be a lot clearer to read and edit in the future, unless you want to create a dynamic array, but there's no need for that in my opinion. I didn't declare variables or write any error handling, it's a simple code just to point you in the right direction.
If you want to disable workbook events or/and alerts, you can set Application.DisplayAlerts and Application.EnableEvents to False temporarily.
Related
i have 2 workbooks 1.remediation.xlsm 2.int_calculation.xlsx
on both workbooks some new worksheets are created dynamically when everytime I run a macro.
here I have added one new worksheet "123456" on remediation.xlsm file and another one called
"Corrected_Accruals-123456" on int_calculation.xlsx file.
i need a piece of code which will take values from a range and search that in both workbooks , if there is a match it will copy data from 123456 and paste it on Corrected_Accruals-123456.
attaching my code for this one, I'm getting error-6 "overflow when running this on.
drive link for 2 workbooks
Sub Copy_Data()
Dim lastRow As Long
Dim offsetRow As Long
Dim i As Range
Dim opsheet As Worksheet
Dim inputsheet As Worksheet
Dim ip As Worksheet
Set inputsheet = Workbooks("remediation.xlsm").Worksheets("reference")
inputsheet.Activate
lastRow = inputsheet.Columns("A").Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
For Each i In inputsheet.Range("A2: A" & lastRow)
Set opsheet = Workbooks("int_Calculation.xlsx").Worksheets("Corrected Accruals" & "-" & i.Value)
Set ip = Workbooks("remediation.xlsm").Worksheets(i.Value)
opsheet.Cells("A5") = ip.Cells("A5")
opsheet.Cells("B5") = ip.Cells("B5")
opsheet.Cells("D5") = ip.Cells("D5")
opsheet.Cells("E5") = ip.Cells("E5")
Next i
End Sub
Beginner here and I managed to modify a code to extract data from a sheet and copy and paste them to other sheets. Problem is when I click run Macro or the button assigned to the Macro, it is duplicating rows again. Please help me to avoid the duplication.
TIA
Sub UpdateHistory()
Dim wsData As Worksheet, wsCostCode As Worksheet
Dim LastRow As Long, NextRow As Long, i As Long
Dim CostCode As String
Dim Company As String
Dim Invoice As String
Dim Price As Double
Application.ScreenUpdating = False
Set wsData = Sheets("Signed Invoices")
LastRow = wsData.Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To LastRow
CostCode = wsData.Range("A" & i).Value
Company = wsData.Range("B" & i).Value
Invoice = wsData.Range("C" & i).Value
Total = wsData.Range("D" & i).Value
If WorksheetExists(CostCode) = True Then
Set wsCostCode = Sheets(CostCode)
NextRow = wsCostCode.Cells(Rows.Count, 1).End(xlUp).Row + 1
wsCostCode.Range("A" & NextRow).Value = CostCode
wsCostCode.Range("B" & NextRow).Value = Company
wsCostCode.Range("C" & NextRow).Value = Invoice
wsCostCode.Range("D" & NextRow).Value = Total
Else
wsData.Range("A1:D1").Copy
Worksheets.Add(After:=Sheets(Sheets.Count)).Name = CostCode
ActiveSheet.Cells(1, 1).PasteSpecial
ActiveSheet.Range("A2").Value = CostCode
ActiveSheet.Range("B2").Value = Company
ActiveSheet.Range("C2").Value = Invoice
ActiveSheet.Range("D2").Value = Total
End If
Next
Application.CutCopyMode = False
Sheets("Signed Invoices").Select
Application.ScreenUpdating = True
End Sub
Function WorksheetExists(shtName As String, Optional wb As Workbook) As Boolean
Dim sht As Worksheet
If wb Is Nothing Then Set wb = ThisWorkbook
On Error Resume Next
Set sht = wb.Sheets(shtName)
On Error GoTo 0
WorksheetExists = Not sht Is Nothing
End Function
When you find that your code isn't doing what you expect, try stepping through it line-by-line and see exactly where and when it goes wrong. You can do this by pressing F8 while your cursor is anywhere in your macro. I also recommend commenting out Application.ScreenUpdating = False until your code is working as expected. Otherwise, following the code's behavior can become difficult when the code is supposed to write things to worksheets.
You've found that your code is duplicating entries. Let's check all places in your macro that write data to the sheet. There is only one place: inside your For i = 2 to LastRow loop. Because you have set up a loop, you are expecting (or at least preparing) for this block of code to run more than once. The next question should be, why is the data not changing between two iterations like you're expecting?
Check that Else block of code. It seems like you copy the headers, add a new sheet, and then use the ActiveSheet to specify which sheet to write the data. Is ActiveSheet the sheet you think it is? (Very easy to verify with line-by-line debugging.) If you really want to use ActiveSheet, make sure the sheet you expect to be active is active with Worksheets(Worksheets.Count).Activate. This will activate the last worksheet, which is where you want to write your data.
Try stepping line-by-line through your code and see if this is correct before modifying your code.
With below code, no errors are displayed, the read file opens but it seems not data is copied.
I am trying to copy only a number of columns, but it seems nothing is been copied to current workbook.
Any help would be appreciated as I am very new with VBA
Sub ReadDataFromCloseFile()
On Error GoTo ErrHandler
'stop screen update
Application.ScreenUpdating = False
Dim src As Workbook
Dim sTheSourceFile As String
sTheSourceFile = "C:\Users\grmn\Desktop\testreadfile.xlsx"
Set src = Workbooks.Open(sTheSourceFile, True, True)
Dim iRowsCount As Long
'source of data
With src.Worksheets("Sheet1")
iRowsCount = .Range("A1:A" & .Cells(Rows.Count, "A").End(xlUp).Row).Rows.Count
End With
Dim iCnt As Long
'destination sheet thisWorkbook.sheet("rapport")
For iCnt = 1 To iRowsCount
Worksheets("rapport").Range("A" & iCnt).Formula = src.Worksheets("Sheet1").Range("A" & iCnt).Formula
Worksheets("rapport").Range("F" & iCnt).Formula = src.Worksheets("Sheet1").Range("B" & iCnt).Formula
Next iCnt
'close but not overide source file (src).
src.Close False
Set src = Nothing
ErrHandler:
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
No worries being new, we all were at some point.
The first part of your code 'source of data doesn't work as intended. iRowsCount is an Integer and not an Array. To make use of an array, as you seemingly tried to do, you should use
Dim iRowsCount(8) As Long
With src.Worksheets("Sheet")
iRowsCount(1) = .Range("A1:A" & .Cells(Rows.Count, "A").End(xlUp).Row).Rows.Count
' ...
End With
' ...
If you use an Integer only the last row will be assigned. So if "AT", for some reason, has 5 rows, iRowsCount will be 5. Nothing else. Not accounting for "AQ" or "AS".
But in your case, Integer/Long would probably suffice if all rows have the exact same count. One assignment would be enough then.
Regarding .Formula - are you really trying to write formulas? Have you tried .value instead?
And, what may be the crux of the matter, try Worksheets("rapport").Save or Worksheets("rapport").SaveAs at the end of your function.
(Haven't tested it on my end so far.)
Additionally, please remember to set Exit Sub (or Exit Function respectively, if a Function) to avoid executing ErrHandler if no error occurs.
(Sorry, I'm new to Stackoverflow, so I can't write comments as of yet.)
(Edit: Thanks for the reminder, #FunThomas, Integer is only -32768 to 32767. Long is 8 bytes.)
I know this is a super generic error but I am new to VBA / Macros and cant get past this.
I have an and excel workbook that has data I need to copy to another excel workbook.
The excel workbook that the data is copied to is on a network share and will be written to frequently.
here is my macro code:
Sub export()
Dim exportFile As String
Dim importSheet As String
Dim rowData As String
exportFile = "\\<server>\spd\_Spec_ParaData\data_import.xlsx"
importSheet = "OutPutValues"
importRange = "A2:ZZ2"
' Get the row from the workbook that we are running in
rowData = Workbooks().Worksheets(importSheet).Range(importRange)
' Not sure if this will work, or always overwrite the last row. May need to be .Row+1
newRow = Workbooks(exportFile).Worksheets(1).Cells(Rows.Count, "A").End(xlUp).Row
exportRange = "A" & (newRow + 1) & ":ZZ" & (newRow + 1)
' Assuming Workbooks() gets the current workbook.
Workbooks(exportFile).Sheets(exportSheet).Range(exportRange) = Workbooks().Sheets(importSheet).Range(importRange)
End Sub
My error is poping up on the rowData=Workbooks(exportFile).Worksheets
Can someone help me figure out what I am doing wrong?
Thank you,
Jennifer
Try your code with the following modifications, I'm just opening the workbook and referencing the worksheet (I guess the problem is that). I'm closing the workbook straight after.
Sub export()
Dim exportFile As String
Dim importSheet As String
Dim rowData As String
Dim wb As Workbook
Dim ws1 As Worksheet, ws2 As Worksheet
exportFile = "\\<server>\spd\_Spec_ParaData\data_import.xlsx"
importSheet = "OutPutValues"
importRange = "A2:ZZ2"
'Open your workbook and point to your spreadsheet
Set wb = Workbooks.Open(exportFile)
Set ws1 = wb.Sheets(importSheet)
' Get the row from the workbook that we are running in
rowData = wb.ws.Range(importRange)
' Not sure if this will work, or always overwrite the last row. May need to be .Row+1
newRow = wb.Worksheets(1).Cells(Rows.Count, "A").End(xlUp).Row
exportRange = "A" & (newRow + 1) & ":ZZ" & (newRow + 1)
'NOTE: consider definying the variable "exportSheet", I will do it just for example purpose
Dim exportSheet As String: exportSheet = "InputValues"
Set ws2 = wb.Sheets(exportSheet)
' Assuming Workbooks() gets the current workbook.
wb.ws2.Range(exportRange) = wb.ws1.Range(importRange)
wb.Close
End Sub
I want to:
Copy the used range of a sheet called "Kommentar"
Create a ".txt" file ("Kommentar.txt") in the same directory as ThisWorkbook
Paste the previously copied used range
Save the ".txt" file
I have:
Sub CreateAfile()
Dim pth As String
pth = ThisWorkbook.path
Dim fs As Object
Set fs = CreateObject("Scripting.FileSystemObject")
Dim a As Object
Set a = fs.CreateTextFile(pth & "\Kommentar.txt", True)
Dim sh As Worksheet
Set sh = ThisWorkbook.Sheets("Kommentar")
Dim rng As Range
Set rng = sh.UsedRange
a.WriteLine (rng)
a.Close
End Sub
I get
run-time error '13' Mismatch
In line a.WriteLine (rng) the function doesn't accept range to be written.
Since your range is probably made up of several cells, you have to loop through them to get all the text into a string variable. If you use a Variant variable you can copy the values and automatically get an array with the correct dimensions of all the data in the cells, then loop it and copy the text:
Function GetTextFromRangeText(ByVal poRange As Range) As String
Dim vRange As Variant
Dim sRet As String
Dim i As Integer
Dim j As Integer
If Not poRange Is Nothing Then
vRange = poRange
For i = LBound(vRange) To UBound(vRange)
For j = LBound(vRange, 2) To UBound(vRange, 2)
sRet = sRet & vRange(i, j)
Next j
sRet = sRet & vbCrLf
Next i
End If
GetTextFromRangeText = sRet
End Function
Call the function in your code by replacing the a.WriteLine (rng) line with the following:
Dim sRange As String
sRange = GetTextFromRangeText(rng)
Call a.WriteLine(sRange)
Not sure you can do that. I believe you would have to write it out line by line.
Here is an alternative option.
Rather than use the FSO, you could just try saving the sheet as a .txt file.
Here's some sample code.
Credit should goto http://goo.gl/mEHVx
Option Explicit
'Copy the contents of a worksheet, and save it as a new workbook as a .txt file
Sub Kommentar_Tab()
Dim wbSource As Workbook
Dim wsSource As Worksheet
Dim wbDest As Workbook
Dim fName As String
'References
Set wbSource = ActiveWorkbook
Set wsSource = ThisWorkbook.Sheets("Kommentar")
Set wbDest = Workbooks.Add
'Copy range on original sheet
'Using usedrange can be risky and may return the wrong result.
wsSource.UsedRange.Copy
'Save in new workbook
wbDest.Worksheets(1).Cells(1, 1).PasteSpecial Paste:=xlPasteValuesAndNumberFormats
Application.CutCopyMode = False
'Get file name and location
fName = ThisWorkbook.Path & "\Kommentar.txt"
'Save new tab delimited file
wbDest.SaveAs fName, xlText
wbDest.Close SaveChanges:=True
End Sub
Let's say yourRange is the range you want to copy as string.
Use yourRange. Copy to copy it.
After you copied it, Excel saves the text value to the clipboard. Cells in a row separated by tabs, and every row ends with an enter. You can use DataObject's GetFromClipboard and GetText method to save it to a string variable.
Use CreateTextFile to save it to a file.
#xwhitelight gives a good outline. Thanks. But I needed to supply details to myself to accomplish my own task and thought I'd share.
First, a Reference to Microsoft Scripting Runtime and another to Microsoft Forms 2.0 Object Library are required.
The coding details I added to produce an output file follow.
Note that textfilename is the fully-qualified name of the output file that contains the spreadsheet range.
Note that textfilename is opened in the last line of the sub, which isn't necessary, but it's reassuring to SEE what the file contains. Of course, the MsgBox is also unnecessary.
Sub turnRangeIntoTextFile(rg As Range, textfilename As String)
Dim textFile as TextStream
Dim fs As FileSystemObject
Dim myData As DataObject
Set myData = New DataObject
Set fs = CreateObject("Scripting.FileSystemObject")
rg.Copy
myData.GetFromClipboard
MsgBox myData.GetText ' reassurance (see what I got)
Set textFile = fs.CreateTextFile(textfilename, True)
textFile.WriteLine (myData.GetText)
textFile.Close
CreateObject("Shell.Application").Open (textfilename)
End Sub