Macro to subtract multiple cells and output the results - excel

I found a macro that subtracts the values in one cell in a workbook from another cell in a workbook to output the result in a final third workbook. It exists as such
Sub Sample()
Dim wb1 As Workbook, wb2 As Workbook, wb3 As Workbook
Dim lngDiff As Long
On Error GoTo Err
Application.ScreenUpdating = False
Set wb1 = ActiveWorkbook
Set wb2 = Workbooks.Open("C:\FirstDataFile.xlsx")
Set wb3 = Workbooks.Open("C:\SecondDataFile.xlsx")
lngDiff = wb2.Sheets("Sheet1").Range("A1").Value - _
wb3.Sheets("Sheet1").Range("A1").Value
wb1.Sheets("Sheet1").Range("A1").Value = lngDiff
wb3.Close savechanges:=False
wb2.Close savechanges:=False
Application.ScreenUpdating = True
Exit Sub
Err:
MsgBox Err.Description
End Sub
Is there anyway to modify this code that it can do this for multiple lines at once.
For example. get it to subtract wb2.Sheets("Sheet1").Range("A1").Value - _
wb3.Sheets("Sheet1").Range("A1").Value and output that result into wb1.Sheets("Sheet1").Range("A1").Value and then do the same for A2, A3 and so on so forth until about A:120000? I would also like to be able to get this done on multiples sheets on the two books that I am drawing info from. How would this be done?
Thanks!

I suggest to use a loop through a list of worksheet names, and outsource the subtraction to subroutine InAllValuesOfColumnA that loops through all rows of each sheet as shown below. I further recommend to use meaningful variable names instead of numbered variables (which is a bad practice and easily gets mixed up).
Option Explicit
Public Sub ExampleSample()
Dim wbResult As Workbook, wbData As Workbook, wbSubtract As Workbook
Dim lngDiff As Long
On Error GoTo Err
Application.ScreenUpdating = False
Set wbResult = ActiveWorkbook
Set wbData = Workbooks.Open("C:\FirstDataFile.xlsx")
Set wbSubtract = Workbooks.Open("C:\SecondDataFile.xlsx")
Dim WorksheetList() As Variant
WorksheetList = Array("Sheet1", "Sheet2") 'add the worksheet names here
Dim WsName As Variant
For Each WsName In WorksheetList
InAllValuesOfColumnA OfWorksheet:=wbData.Worksheets(WsName), SubtractWorksheet:=wbSubtract.Worksheets(WsName), WriteToWorksheet:=wbResult.Worksheets(WsName)
Next WsName
wbData.Close SaveChanges:=False
wbSubtract.Close SaveChanges:=False
Application.ScreenUpdating = True
Exit Sub
Err:
MsgBox Err.Description
End Sub
Private Sub InAllValuesOfColumnA(ByVal OfWorksheet As Worksheet, ByVal SubtractWorksheet As Worksheet, ByVal WriteToWorksheet As Worksheet)
Dim LastRow As Long
LastRow = OfWorksheet.Cells(OfWorksheet.Rows.Count, "A").End(xlUp).Row
Dim iRow As Long
For iRow = 1 To LastRow 'run from first to last row and subtract
WriteToWorksheet.Cells(iRow, "A").Value = CLng(OfWorksheet.Cells(iRow, "A").Value - SubtractWorksheet.Cells(iRow, "A").Value)
Next iRow
End Sub
An even faster way would be to read/write the data into arrays before/after calculation:
Private Sub InAllValuesOfColumnA(ByVal OfWorksheet As Worksheet, ByVal SubtractWorksheet As Worksheet, ByVal WriteToWorksheet As Worksheet)
Dim LastRow As Long
LastRow = OfWorksheet.Cells(OfWorksheet.Rows.Count, "A").End(xlUp).Row
'read all into array
Dim DataColumn() As Variant
DataColumn = OfWorksheet.Range("A1:A" & LastRow).Value
Dim SubtractColumn() As Variant
SubtractColumn = SubtractWorksheet.Range("A1:A" & LastRow).Value
Dim ResultColumn() As Variant
ResultColumn = WriteToWorksheet.Range("A1:A" & LastRow).Value
Dim iRow As Long
For iRow = LBound(ResultColumn) To UBound(ResultColumn) 'run from first to last row and subtract
ResultColumn(iRow) = CLng(DataColumn(iRow) - SubtractColumn(iRow))
Next iRow
WriteToWorksheet.Range("A1:A" & LastRow).Value = ResultColumn
End Sub

Related

Creating New Sheets with Names from a List

I am pretty new to VBA and am having an issue with my code. I have different hotel names from cell B4 to B27. My goal is to create new worksheets and name each one with the hotel names (going down the list). I tried running the sub procedure below but I am getting an error. The error says:
"Run-time error '1004': Application-defined or object-defined error"
It refers to the line below my comment. Any thoughts on why this is occurring and how I can fix this?
Sub sheetnamefromlist()
Dim count, i As Integer
count = WorksheetFunction.CountA(Range("B4", Range("B4").End(xlDown)))
i = 4
Do While i <= count
' next line errors
Sheets.Add(after:=Sheets(Sheets.count)).Name = Sheets("LocalList").Cells(i, 2).Text
i = i + 1
Loop
Sheets("LocalList").Activate
End Sub
Here is something that I quickly wrote
Few things
Do not find last row like that. You may want to see THIS
Do not use .Text to read the value of the cell. You may want to see What is the difference between .text, .value, and .value2?
Check if the sheet exists before trying to create one else you will get an error.
Is this what you are trying?
Option Explicit
Sub sheetnamefromlist()
Dim ws As Worksheet, wsNew As Worksheet
Dim lRow As Long, i As Long
Dim NewSheetName As String
'~~> Set this to the relevant worksheet
'~~> which has the range
Set ws = ThisWorkbook.Sheets("LocalList")
With ws
'~~> Find last row
lRow = .Range("B" & .Rows.Count).End(xlUp).Row
'~~> Loop through the range
For i = 4 To lRow
NewSheetName = .Cells(i, 2).Value2
'~~> Check if there is already a worksheet with that name
If Not SheetExists(NewSheetName) Then
'~~> Create the worksheet and name it
With ThisWorkbook
.Sheets.Add(After:=.Sheets(.Sheets.Count)).Name = NewSheetName
End With
End If
Next i
End With
End Sub
'~~> Function to check if the worksheet exists
Private Function SheetExists(shName As String) As Boolean
Dim shNew As Worksheet
On Error Resume Next
Set shNew = ThisWorkbook.Sheets(shName)
On Error GoTo 0
If Not shNew Is Nothing Then SheetExists = True
End Function
My assumptions
All cells have valid values i.e which can be used for sheet names. If not, then you will have to handle that error as well.
Workbook (not worksheet) is unprotected
Try,
Sub test()
Dim vDB As Variant
Dim rngDB As Range
Dim Ws As Worksheet, newWS As Worksheet
Dim i As Integer
Set Ws = Sheets("LocalList")
With Ws
Set rngDB = .Range("b4", .Range("b4").End(xlDown))
End With
vDB = rngDB 'Bring the contents of the range into a 2D array.
For i = 1 To UBound(vDB, 1)
Set newWS = Sheets.Add(after:=Sheets(Sheets.Count))
newWS.Name = vDB(i, 1)
Next i
End Sub
Create Worksheets from List
The following will create (and count) only worksheets with valid names.
When the worksheet is already added and the name is invalid, it will be deleted (poorly handled, but it works.)
It is assumed that the list is contiguous (no empty cells).
The Code
Option Explicit
Sub SheetNameFromList()
Const wsName As String = "LocalList"
Const FirstCell As String = "B4"
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
Dim ListCount As Long
ListCount = WorksheetFunction.CountA(ws.Range(FirstCell, _
ws.Range(FirstCell).End(xlDown)))
Dim fRow As Long: fRow = ws.Range(FirstCell).Row
Dim fCol As Long: fCol = ws.Range(FirstCell).Column
Dim i As Long, wsCount As Long
Do While i < ListCount
If addSheetAfterLast(wb, ws.Cells(fRow + i, fCol).Value) = True Then
wsCount = wsCount + 1
End If
i = i + 1
Loop
ws.Activate
MsgBox "Created " & wsCount & " new worksheet(s).", vbInformation
End Sub
Function addSheetAfterLast(WorkbookObject As Workbook, _
SheetName As String) _
As Boolean
Dim ws As Worksheet
On Error Resume Next
Set ws = WorkbookObject.Worksheets(SheetName)
If Err.Number = 0 Then Exit Function
Err.Clear
WorkbookObject.Sheets.Add After:=WorkbookObject.Sheets(Sheets.count)
If Err.Number <> 0 Then Exit Function
Err.Clear
WorkbookObject.ActiveSheet.Name = SheetName
If Err.Number <> 0 Then
Application.DisplayAlerts = False
WorkbookObject.Sheets(WorkbookObject.Sheets.count).Delete
Application.DisplayAlerts = False
Exit Function
End If
addSheetAfterLast = True
End Function

Loop a range and skip rows in Excel Worksheet

I have an excel worksheet that accepts input from another excel file. This excel file has structured data in which I need to separate individually as sheets. I already have the following code to copy and format that data in a certain range but I need to loop this process for the whole worksheet until there's no more data.
The range currently I set is A2:P20 the next range is 4 rows below and that would be A25:P43.
Option Explicit
Public Sub CopySheetToClosedWorkbook()
Dim fileName
Dim closedBook As Workbook
Dim currentSheet As Worksheet
fileName = Application.GetOpenFilename("Excel Files (*.xls*),*xls*")
If fileName <> False Then
Application.ScreenUpdating = False
Set currentSheet = Application.ActiveSheet
Set closedBook = Workbooks.Open(fileName)
closedBook.Sheets(1).Range("A2:P20").Copy
ThisWorkbook.Worksheets("input").Range("A2").PasteSpecial xlPasteValues
closedBook.Application.CutCopyMode = False
closedBook.Close (True)
Application.ScreenUpdating = True
CopySheetAndRenameByCell2
End If
End Sub
You could do something based on the code below. I have set the last row as 1000, you will need to derrive this from your data.
Sub SplitRangeTest()
Dim lLastRow As Long
Dim lRow As Long
Dim lRangeSize As Long
Dim lSpacerSize As Long
lRangeSize = 19
lRow = 2
lSpacerSize = 4
lLastRow = 1000 ' Get the last populated row in the column of choice here
Do Until lRow > lLastRow
Debug.Print Range("A" & lRow).Resize(lRangeSize, 16).Address
lRow = lRow + lRangeSize + lSpacerSize
Loop
End Sub
Try this:
Public Sub CopySheetToClosedWorkbook()
Dim fileName As String
Dim closedBook As Workbook
Dim currentSheet As Worksheet
fileName = Application.GetOpenFilename("Excel Files (*.xls*),*xls*")
If fileName <> False Then
start_row = 2
rows_to_copy = 19
row_step = 23
Set currentSheet = Application.ActiveSheet
Set closedBook = Workbooks.Open(fileName)
last_row = Sheets(1).Cells(Sheets(1).Rows.Count, "A").End(xlUp).Row
Application.ScreenUpdating = False
For y = start_row To last_row Step row_step
ThisWorkbook.Worksheets("input").Rows(y).Resize(rows_to_copy, 16).Value = closedBook.Sheets(1).Rows(y).Resize(rows_to_copy, 16).Value
Next
Application.ScreenUpdating = True
End If
End Sub
it's worth mentioning here that you set currentSheet but don't actually use it. Also, you shouldn't really use ThisWorkbook like that. Maybe you should be using currentSheet instead (or at least, it's parent).

Excel VBA Save multiple sheets from a range to a single PDF

I'm currently working on workbook where in column A:A of worksheet("STAM-Filialen") nearly all the names of the other worksheets are. I want only those worksheets named in column("A:A") in a single PDF. The code I use know makes it a separate file for each worksheet. Is it possible to use a sort of a same code to save it as a single PDF?
Dim myCell As Range
Dim lastCell As Long
Dim PathName As String
lastCell = lastRow("STAM-Filialen")
PathName = Range("I10").Value
Worksheets("STAM-Filialen").Activate
For Each myCell In ThisWorkbook.Worksheets("STAM-Filialen").Range("A2:A" & lastCell).Cells
Dim wksName As String
wksName = myCell.Text
ThisWorkbook.Worksheets(wksName).Range("A1:P60").ExportAsFixedFormat Type:=xlTypePDF, Filename:=PathName & "DispoPlan.Filiaal " & wksName & ".PDF"
Next
I'd recommend moving all the values to a single sheet to print. Then delete this temporary sheet when done.
Here's an example of placing each range from each sheet side by side in a new sheet.
Option Explicit
Public Sub CreateSinglePDF()
Dim ws As Range: Set ws = ThisWorkbook.Sheets(1).Range("A1:A4")
Dim rangeDict As Object: Set rangeDict = CreateObject("Scripting.Dictionary")
Dim cell As Range
For Each cell In ws
If Not rangeDict.exists(cell.Value) And cell.Value <> "" Then
rangeDict.Add cell.Value, ThisWorkbook.Sheets(cell.Value).Range("A1:A5")
End If
Next
Dim printsheet As Worksheet
Set printsheet = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Worksheets(ThisWorkbook.Worksheets.Count))
Dim key As Variant
Dim i As Long: i = 1
For Each key In rangeDict
printsheet.Range(printsheet.Cells(1, i), printsheet.Cells(5, i)).Value = rangeDict(key).Value
i = i + 1
Next
printsheet.UsedRange.ExportAsFixedFormat Type:=xlTypePDF, Filename:="C:\users\ryan\desktop\ExampleFile.pdf"
printsheet.Delete
End Sub
To convert multiple sheets into single pdf document,
first select multiple sheets
and use the Activesheet.ExportAsFixedFormat statement.
The print range of the page can be set in Page Setup.
Code
Sub test()
Dim WB As Workbook
Dim Ws As Worksheet
Dim sht As Worksheet
Dim PathName As String
Dim vWs() as String '<~~ Variable change
Dim rngDB As Range, rng As Range
Dim n As Integer
Set WB = ThisWorkbook
Set Ws = WB.Worksheets("STAM-Filialen")
PathName = Range("I10").Value
With Ws
Set rngDB = .Range("a1", .Range("a" & Rows.Count).End(xlUp))
End With
For Each rng In rngDB
n = n + 1
ReDim Preserve vWs(1 To n)
vWs(n) = rng.text '<~~ text
Set sht = Sheets(rng.Value)
With sht.PageSetup
.PrintArea = "a1:p60"
End With
Next rng
Sheets(vWs).Select '<~~ multiple sheets select
ActiveSheet.ExportAsFixedFormat Type:=xlTypePDF, filename:=PathName & "DispoPlan.Filiaal.PDF"
End Sub
Worksheets("STAM-Filialen")
Specipic Sheets selected
Single pdf

Method "Range" of object _worksheet failed when searching for lastrow VBA Excel

So here is my second question to you experts. I am trying to find the last row of a column, but I keep on getting the "method range of object worksheet failed.
I have tried changing the syntax to various things but I still can't work it out.
I am new to VBA excel and I would really appreciate your help with this one.
Thank you. The offending line is marked with ********
Private Sub GetProductCode()
'Opens jobs file
'Trims down customer reference column to job number only, then searches job file to return product code
Dim sJobNumber As String
Dim sCustomerReference As String
Dim wsRevRownum As Long
Dim wbkJobs As Workbook
Dim wsJobs As Worksheet
Dim wsRev As Worksheet
Dim strPathFile As String
Dim strSearch As String
Dim fCell As Range
Dim fCellRowNum As String
Dim wsJobsLastrow As Long
Dim wsRevLastrow As Long
'On Error GoTo ErrHandler
Application.ScreenUpdating = False
'Change as desired
strPathFile = "\\ACHILLES\Company\Production_schedule\Jobs.xlsm"
strSearch = "Specific text"
Set wbkJobs = Workbooks.Open _
(Filename:=strPathFile, _
UpdateLinks:=0, _
ReadOnly:=True, _
AddToMRU:=False)
Set wsRev = ThisWorkbook.Sheets(3)
MsgBox wsRev.Name
Set wsJobs = wbkJobs.Sheets("Jobs")
'MsgBox wbkJobs.Name
wsRevRownum = 2
With wsRev
wsRevLastrow = wsRev.Range("E" & Rows.Count).End(xlUp).Row ********
End With
With wsJobs
wsJobsLastrow = wsJobs.Range("A" & Rows.Count).End(xlUp).Row
End With
'Loop through revenue file and search for entries in jobs file. If found then take the product code from the jobs file and populate revenue file
Do Until wsRev.Range("E" & wsRevRownum).Address = (wsRevLastrow + 1)
'initialise variables
sCustomerReference = ""
sJobNumber = ""
strSearch = ""
Try one of these if you are looking for the last row in a worksheet:
Sub GetLastRow()
'Different ways to find the last row number of a range
Dim ws As Worksheet
Dim LastRow As Long
Set ws = ThisWorkbook.Worksheets("Sheet1")
'Ctrl + Shift + End
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
'Using UsedRange
ws.UsedRange 'Refresh UsedRange
LastRow = ws.UsedRange.Rows(ws.UsedRange.Rows.Count).Row
'Using Table Range
LastRow = ws.ListObjects("Table1").Range.Rows.Count
'Using Named Range
LastRow = ws.Range("MyNamedRange").Rows.Count
'Ctrl + Shift + Down (Range should be first cell in data set)
LastRow = ws.Range("A1").CurrentRegion.Rows.Count
End Sub

VBA Code to Create Sheets based on the values in column A

I am looking for a code to create sheets with the name in column A. I have used this code but it is not fulfulling my requirement. The code is ;
Private Sub CommandButton1_Click()
Dim sheetCount As Integer
Dim sheetName As String
Dim workbookCount As Integer
With ActiveWorkbook
sheetCount = Sheets(1).Range("A2").End(xlDown).Row
For i = 2 To sheetCount Step 1
sheetName = .Sheets(1).Range("A" & i).Value
workbookCount = .Worksheets.Count
.Sheets.Add After:=Sheets(workbookCount)
.Sheets(i).Name = sheetName
'.Sheets(i).Range("A" & i, "F" & i).Value = .Sheets("sample").Range("A" & i, "F" & i).Value
Next
End With
Worksheets(1).Activate
End Sub
Upon running this code in first go, it creates sheets with the text present in column A. But the problem is when i entered new text in that column, it makes previous sheets as well. I am looking for a code which only create the sheets with the new text being entered in the column and donot make sheets which are already made. Kindly help me out on this as i tried too much but didnt find any code.
Thanks
This works for me, and is tested: Note, if you try to use a name like "History" that is reserved you will get an error. I am not aware of all the reserved names.
Private Sub CommandButton1_Click()
Dim lastRow As Long
Dim sheetName As String
Dim workbookCount As Long
Dim ws As Worksheet
Dim match As Boolean
lastRow = Sheets("Sheet1").Range("A2").End(xlDown).Row
For i = 2 To lastRow
match = False
sheetName = Sheets("Sheet1").Cells(i, 1).Text
For Each ws In ActiveWorkbook.Worksheets
If ws.Name = sheetName Then
match = True
End If
Next
If match = False Then
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = sheetName
End If
Next i
End Sub
Edit: Added Screen Shots
You can try thi function:
Function SheetExists(SheetName As String) As Boolean
Dim Test As Boolean
On Error Resume Next
Test = Sheets(SheetName).Range("A1").Select
If Test Then
SheetExists = True
Else
SheetExists = False
End If
End Function
Using the function this way:
Sub test()
If SheetExists("MySheet") Then
MsgBox "Sheet exists"
Else
MsgBox "Sheet is missing"
End If
End Sub
I usually have these two helper functions in my workbooks / personal workbook
Option Explicit
Function getSheetWithDefault(name As String, Optional wb As Excel.Workbook) As Excel.Worksheet
If wb Is Nothing Then
Set wb = ThisWorkbook
End If
If Not sheetExists(name, wb) Then
wb.Worksheets.Add(After:=wb.Worksheets(wb.Worksheets.Count)).name = name
End If
Set getSheetWithDefault = wb.Sheets(name)
End Function
Function sheetExists(name As String, Optional wb As Excel.Workbook) As Boolean
Dim sheet As Excel.Worksheet
If wb Is Nothing Then
Set wb = ThisWorkbook
End If
sheetExists = False
For Each sheet In wb.Worksheets
If sheet.name = name Then
sheetExists = True
Exit Function
End If
Next sheet
End Function
To create the worksheets you just iterate over the sheet names and use the getSheetwithDefault function
The following code demonstrate this:
sub createSheets()
dim cursor as Range: set cursor = Sheets("Sheet1").Range("A2")
while not isEmpty(cursor)
getSheetWithDefault(name:=cursor.value)
set cursor = cursor.offset(RowOffset:=1)
wend
end

Resources