Openpxyl problem with sheet names with spaces - openpyxl

I have an issue where if a sheet name has a space in it, it yields an error when trying to get the worksheet
```wb = load_workbook(excel_file, read_only=True, data_only=True)
sheet_names = wb.get_sheet_names()```
sheet_names=
['TB Sep 19',
'1000000000 - 1999999999',
'2000000000 - 2999999999',
'30000000000 - 4999999999',
'5000000000-999999999',
'Transaction Pivots',
'TB Reconciliation']
worksheet = workbook.get_sheet_by_name('1000000000 - 1999999999') ok
worksheet = workbook.get_sheet_by_name('Transaction Pivots') Error:'Worksheet Transaction Pivots does not exist.'

It does work. Was just being a numpty

Related

Finding dates and storing ranges in variables

I'm trying to find "blocks" in the worksheet that have dates in the A-column. A block is seperated by lines as you can see from the picture. The whole file is full of these blocks but I only need the blocks that have dates in the A-column. Just to be clear I don't just need the rows with the dates but the full block that contains the date.
A block in my files for example is the Range A172:G192.
Picture of the file:
[![enter image description here][1]][1]
How should I continue after selecting the first block? I probably should use the Find function starting from row 184 or the row of ResultDown variable moving down the sheet on "A" Column. However the row needs to be dynamic, so I can use it for the next block. Also I have no idea how many blocks there will be, so I would like to hear from you how to solve this aswell.
I would like to save all the blocks as different variables and then hide all the blocks in the worksheet and then just unhide the blocks that I stored in the variables.
My biggest problem is the last row.
Result2 = Range(Cells(StartcellRow, 1), Cells(1000, 1)).Find(What:="**/**/****", After:=Range(Cells(StartcellRow, 1))).Select
I keep getting an runtime error 1004
Public Sub FB_MAKRO()
Dim FBwb As Workbook
Dim FBsht As Worksheet
Dim ACol As Range
'Set variables for workbook and sheet
Set FBwb = Workbooks.Open(Filename:="C:\Users\l000xxx\Desktop\Makrot\FORCED BALANCE MAKRO\FB HARJOITUS.xls")
Set FBsht = FBwb.Sheets("Forced Balance")
Set ACol = FBsht.Range("A1:A1000")
'I want ACol variable to be the entire A-Column. Any ideas?
'For some reason the range function is not working here?
'This locates the date in A'column, so I can select the correct block
Result = Range("A3:A1000").Find(What:="**/**/****", After:=Range("A3")).Address
'This is the top left corner of the Block1 selection
ResultUp = Range(Result).End(xlUp).Offset(-1, 0).Address
Range(ResultUp).End(xlDown).Select
Range(ActiveCell, ActiveCell).End(xlDown).Select
'The ResultsDownLastRow variable is for Block2 find function
'ResultDown is the bottom right corner of the Block1
ResultsDownLastRow = Range(ActiveCell, ActiveCell).End(xlDown).Address
ResultDown = Range(ActiveCell, ActiveCell).End(xlDown).Offset(-2, 6).Address
'First Block assigned. I plan to use this in the end when I hide everything and then unhide these blocks
Block1 = Range(ResultUp, ResultDown).Select
' NEXT BLOCK STARTS HERE
'StartCellRow is the cell that the find function should start looking for Block2
'Result2 is the find function for Block2
StartcellRow = Range(ResultsDownLastRow).Row
Result2 = Range(Cells(StartcellRow, 1), Cells(1000, 1)).Find(What:="**/**/****", After:=Range(Cells(StartcellRow, 1))).Select
End Sub
'This returns value 194
StartcellRow = Range(ResultsDownLastRow).Row MsgBox StartcellRow
'This should work but doesn't. I get a syntax error
Range(Cells(StartcellRow &","& 1),Cells(1000 & "," & 1)).Find(What:="**/**/****", After:=Range(Cells(StartcellRow& ","& 1)).Select
This doesn't work either
'StarcellRow gives out value of 194
StartcellRow = Range(ResultsDownLastRow).Row
Result2 = Range("A&:StartcellRow:A648").Find(What:="**/**/****", After:=Range("A&:StartcellRow")).Select
This doesn't give me a syntax error but it's not working
I would search for all currency header and store their rownumber into an array. For each rownumber in the array i would look into the cell below (rownumber + 1). when there is a date in the cell, i would set the range in the following way:
set rangeWithDate = Range(Cells(actualRowNumberInArray - 1, 1), Cells(nextRowNumberInArray - 2, 7))
Array:
Dim array1() As long
Redim array1(5)
For i = 1 To 5
array(i) = i
Next i
Redim array1(10) ' changes the "length" of the array, but deletes old entries
For i = 1 To 10
Feld1(i) = i
Next i
Redim Preserve array1(15) ' changes the "length" of the array without deleting old entries

How to get multiple results with one vlookup in VBA, Where vlookup is the part of the whole string (vlookup value)

I have 3 sheets, in sheet one I have a column "Register Codes" and I have extracted the unique codes in next column. Please check the below image.
Based on these unique codes, sub-codes are allocated in sheet 2. please check the below image.
Now what I am trying here is that in sheet 3 I need every "Register code" with the relevant "sub-code" which is allocated in sheet2 based on the "unique ID" given in Sheet1. please check the below image for expected output.
I have been using various combinations of formulas but could not get a proper solution. What is the best way to do it in VBA as I just started learning in this field.
Subject to a few conditions the following code will do what you want. Install it in a standard code module (by default "Module1", but you can name it as you like) in the workbook where you have your data.
Option Explicit
Enum Nws ' Worksheet navigation
NwsFirstDataRow = 2 ' presumed the same for all worksheets
NwsCode = 1 ' 1 = column A (change as required)
NwsSubCode ' No value means previous + 1
NwsNumer
End Enum
Sub NumerList()
' 05 Apr 2017
Dim Wb As Workbook ' all sheets are in the same workbook
Dim WsCodes As Worksheet ' Register codes
Dim WsNum As Worksheet ' Sub-code values
Dim WsOut As Worksheet ' Output worksheet
Dim RegName As String, RegCode As String
Dim Sp() As String
Dim Rs As Long ' Source row in WsNum
Dim Rt As Long ' Target row in WsOut
Dim R As Long, Rl As Long ' rows / Last row in WsCodes
Set Wb = ActiveWorkbook ' Make sure it is active!
Set WsCodes = Wb.Worksheets("Reg Codes") ' Change name to your liking
Set WsNum = Wb.Worksheets("Code Values") ' Change name to your liking
On Error Resume Next
Set WsOut = Wb.Worksheets("Output") ' Change name to your liking
If Err Then
Set WsOut = Wb.Worksheets.Add(After:=WsNum)
WsOut.Name = "Output" ' create the worksheet if it doesn't exist
End If
On Error GoTo 0
Rt = NwsFirstDataRow
With WsCodes
Rl = .Cells(.Rows.Count, NwsCode).End(xlUp).Row
For R = NwsFirstDataRow To Rl
RegName = .Cells(R, NwsCode).Value
Sp = Split(RegName, "-")
If UBound(Sp) > 1 Then ' must find at least 2 dashes
RegCode = Trim(Sp(1))
Else
RegCode = ""
End If
If Len(RegCode) Then
On Error Resume Next
Rs = WorksheetFunction.Match(RegCode, WsNum.Columns(NwsCode), 0)
If Err Then Rs = 0
On Error GoTo 0
If Rs Then
Do
WsOut.Cells(Rt, NwsCode).Value = RegName
WsOut.Cells(Rt, NwsSubCode).Value = WsNum.Cells(Rs, NwsSubCode).Value
WsOut.Cells(Rt, NwsNumer).Value = WsNum.Cells(Rs, NwsNumer).Value
Rt = Rt + 1
Rs = Rs + 1
Loop While WsNum.Cells(Rs, NwsCode).Value = RegCode
Else
RegCode = ""
End If
End If
If Len(RegCode) = 0 Then
WsOut.Cells(Rt, NwsCode).Value = RegName
WsOut.Cells(Rt, NwsSubCode).Value = "No sub-code found"
Rt = Rt + 1
End If
Next R
End With
End Sub
And here are the conditions.
All 3 sheets must be in the same workbook. If you have them in different workbooks the code must be adapted to handle more than one workbook.
The two worksheets with data must exist. They must be named as the code prescribes or the code must be modified to match the names they have. The same goes for the Output worksheet, but that sheet will be created by the code if it doesn't exist. You can change its name in the code.
The enumeration at the top of the code presumes that all 3 sheets are identically formatted with no data in row 1 (captions) and data in columns A, B and C. Changes aren't difficult but must be made if you want a different input or output. You can change the columns in the existing code by assigning other values to the columns in the enum, but the code requires the same arrangement in all sheets.
The extracted codes in the Codes sheet aren't used. The code does its own extraction. It will mark an error in the output list if a code can't be extracted or if it isn't found in the Sub-code list.
The sub-codes in the Numer sheet must be sorted like the picture you posted. The code will look for the first occurrence of "image" and find the subcodes in the following rows while the code is "image" in column A. It will not find further occurrences of "image" that might follow after an intermission.
The code doesn't do any colouring. Adding it wouldn't be difficult, but you would have to specify some rules, such as "20 different colours for the first 20 codes and then repeat the same sequence".
Other cell formatting could be added without much effort because each cell is already individually named. More properties can be added easily.

Group sheets with names in a string variable

I want to group a number of sheets that has sheet names with in a string variable, the group of sheets will be used to format each sheet within the group.
The string (let's call it, SheetsFormat) contains all sheet names that need to be formatted, each value within the string was added into the dynamic string when it was created.
The question is how to create a group of sheets that reference the SheetsFormat string?
Example of the string contents: "Mon Maint", "Mon Mgnt", "Tue Prod" etc.
The code I used was:
Dim ws as worksheet, ws_group As Worksheets
Set ws_group = Sheets(Array(SheetsFormat))
For Each ws In ws_group
'some staff
Next
Here's a sample procedure to get you going. You can build your array in any number of ways, but the end result will follow this patter--iterating through an array (or similar) to do the same operations on the set of worksheets.
Sub SampleDoSomethingToMultipleSheets()
Dim SheetsFormat(3) As String
SheetsFormat(0) = "Mon Maint"
SheetsFormat(1) = "Mon Mgnt"
SheetsFormat(2) = "Tue Prod"
Dim idx As Integer
For idx = 0 To UBound(SheetsFormat) - 1
With Sheets(SheetsFormat(idx))
' put your code here, e.g.
.Range("A1").Value = "StackOverflow is awesome"
End With
Next idx
End Sub
Here's an example that does what you want. It uses the Split function which splits a string into an array based on a separator.
Sub LoopThroughSheetsString()
Dim SheetsFormat As String
Dim ws As Excel.Worksheet
SheetsFormat = "Sheet1,Sheet2,Sheet3"
'loop through the array created by splitting SheetsFormat on commas
For Each ws In Sheets(Split(SheetsFormat, ","))
Debug.Print ws.Name
Next ws
End Sub
Note that it's one long string with double-quotes only at the beginning and end.

How do I stop old data from being updated by my macro?

I've been working on what is basically a register for sales. My spreadsheet records sales, profits etc and from that prints a receipt however, I'd like to keep my own record of this data to use for graphs etc. At the moment my macro moves up from a specified column, beginning from row 100000, then when it reaches a row with data, moves down one to the empty cell. In it it places the cell reference for the new data it has found. I'm new to VBA so I'm not sure if I'm taking the right approach, this is my code:
Sub DataLog()
'
' DataLog Macro
' Record sales in the data log.
'
'
Sheets("Data").Select
Dim rngd As Range
Set rngd = Range("A100000").End(xlUp).Offset(1, 0)
Dim coke As Range
Set coke = Range("C100000").End(xlUp).Offset(1, 0)
Dim creamsoda As Range
Set creamsoda = Range("D100000").End(xlUp).Offset(1, 0)
Dim lemonade As Range
Set lemonade = Range("E100000").End(xlUp).Offset(1, 0)
Dim pasito As Range
Set pasito = Range("F100000").End(xlUp).Offset(1, 0)
Dim pepsi As Range
Set pepsi = Range("G100000").End(xlUp).Offset(1, 0)
Dim pepsim As Range
Set pepsim = Range("H100000").End(xlUp).Offset(1, 0)
Dim solo As Range
Set solo = Range("I100000").End(xlUp).Offset(1, 0)
Dim sprite As Range
Set sprite = Range("J100000").End(xlUp).Offset(1, 0)
Dim sunkist As Range
Set sunkist = Range("K100000").End(xlUp).Offset(1, 0)
Dim water As Range
Set water = Range("L100000").End(xlUp).Offset(1, 0)
rngd.Value = "=Form!D1"
coke.Value = "=Form!E7"
creamsoda.Value = "=Form!E8"
lemonade.Value = "=Form!E9"
pasito.Value = "=Form!E10"
pepsi.Value = "=Form!E11"
pepsim.Value = "=Form!E12"
solo.Value = "=Form!E13"
sprite.Value = "=Form!E14"
sunkist.Value = "=Form!E15"
water.Value = "=Form!E16"
When I clock the macro button, the first time it works, as does it the second time. Although, when it does record the second set of data in the row below, it updates all the previous data in each cell. How can I stop any previously entered data from being updated so I can keep a weekly record of information? Any help or insight is greatly appreciated, thank you :)
EDIT:
I should add, I have tried locking the cells once data has been added but this only protected them from manual alteration, the macro still updated the data.
To overwrite the all formulas in your Data sheet with values, add these two lines to the bottom of your script:
Sheets("Data").Cells.Copy
Sheets("Data").Cells.PasteSpecial (xlPasteValues)
If there are formulas you want to keep, you'll simply need to define the Range of soda values you do want to overwrite and follow the same pattern:
Dim SodaData As Range
'ex below sets SodaData range from A3 to C3
Set SodaData = Sheets("Data").Range(Sheets("Data").Cells(3, 1), Sheets("Data").Cells(3, 3))
SodaData.Copy
SodaData.PasteSpecial (xlPasteValues)

Change code to autofit column width based on content of row

I have some code here which is working great but which I would like to fine-tune. Right now the macro copies three rows of values to a new workbook. These values are pasted without any format. Not all the content of the cells is visible because the column width is not large enough sometimes. My question is the following: how can i change the code provided below so as for the columns to become larger depending on the text in the first row pasted.
How do I autofit the columns depending on the content in the first row?
Code:
Public Sub pub_sub_ExportRows()
'#
'# declare private variables
'#
Dim pvt_xls_Current As Excel.Worksheet
Dim pvt_wbk_New As Excel.Workbook
Dim pvt_lng_SelectedSourceRow As Long
Dim pvt_flg_ValidRow As Boolean
Dim pvt_lng_RowNumber As Long
Dim pvt_lng_FirstColumn As Long
Dim pvt_lng_LastColumn As Long
Dim pvt_lng_ColumnNumber As Long
Dim pvt_lng_TargetColumn As Long
'#
'# record the current row based on the active cell
'#
Set pvt_xls_Current = ThisWorkbook.ActiveSheet
pvt_lng_SelectedSourceRow = ActiveCell.Row
'#
'# the maximum number of columns to be considered is not dependent on the columns defined in the
'# worksheet, but rather by a limit imposed by the user - i.e. column HG is the last column to be copied,
'# and column Q is the first column to be considered
'#
pvt_lng_FirstColumn = Columns("Q").Column
pvt_lng_LastColumn = Columns("HG").Column
pvt_lng_TargetColumn = 0
'#
'# check if the selected row is valid by examining the values in the columns on that
'# row - any non-blank value implies that the selected row is valid - when looking at
'# the values the search starts in column Q as requested by the user
'#
With pvt_xls_Current
pvt_flg_ValidRow = False
For pvt_lng_ColumnNumber = pvt_lng_FirstColumn To pvt_lng_LastColumn
If LenB(.Cells(pvt_lng_SelectedSourceRow, pvt_lng_ColumnNumber).Value) > 0 Then
pvt_flg_ValidRow = True
Exit For
End If
Next pvt_lng_ColumnNumber
End With
If Not pvt_flg_ValidRow Then
MsgBox "You must select a valid - i.e. non empty - row"
Exit Sub
End If
If pvt_lng_SelectSourceRow > 10000 Then
MsgBox "You may not select a row > 10000"
Exit Sub
End If
'#
'# create a new workbook to hold the copied values and copy & paste the information to the
'# newly created workbook
'#
Set pvt_wbk_New = Application.Workbooks.Add
With pvt_xls_Current
For pvt_lng_ColumnNumber = pvt_lng_FirstColumn To pvt_lng_LastColumn
If LenB(.Cells(pvt_lng_SelectedSourceRow, pvt_lng_ColumnNumber).Value) > 0 And _
InStr(1, "$AF,$BF,$CG,$DH,$ES,$FV,$HD,$HF", Split(Columns(pvt_lng_ColumnNumber).Address, ":")(0)) = 0 Then
pvt_lng_TargetColumn = pvt_lng_TargetColumn + 1
pvt_wbk_New.Worksheets("Sheet1").Cells(1, pvt_lng_TargetColumn).Value = .Cells(4, pvt_lng_ColumnNumber).Value
pvt_wbk_New.Worksheets("Sheet1").Cells(2, pvt_lng_TargetColumn).Value = .Cells(5, pvt_lng_ColumnNumber).Value
pvt_wbk_New.Worksheets("Sheet1").Cells(3, pvt_lng_TargetColumn).Value = .Cells(pvt_lng_SelectedSourceRow, pvt_lng_ColumnNumber).Value
End If
Next pvt_lng_ColumnNumber
End With
'#
'# activate the new workbook
'#
pvt_wbk_New.Activate
End Sub
This will autofit the entire row of the first cell being set to a value. It could obviously be simplified to just indicate row 1, but I tried to duplicate the code you used:
pvt_wbk_New.Worksheets("Sheet1").Cells(1,pvt_lng_TargetColumn).EntireRow.Columns.Autofit