Named range to hide/show columns - excel

I have a string that can take a couple of values (e.g. Madison, Chicago,...) and a couple of ranges named (Madison_Range, Chicago_Range,...)
Depending on the input of the user (more specifically, he drops an 'x' in a certain cell), the string takes on a certain value and I want the corresponding (hidden) range to be shown.
However,
Dim X as string
Dim Combination as string
x = Range("ACity:ZCity").Find("x").Name.Name
Combination= x & "_Range"
Range("Combination").EntireRow.Hidden
Does not do the trick, probably because vba looks for the named range "combination". Any ideas how to procede? I know that there is possibly a more elegant case, but this is a quick fix solution as part of a wide problem.

Dim X As String
Dim target As Range
X = Range(Range("Acity"),Range("zcity")).Find("x").Name.Name
'this is potentially dodgy - you assume that the find will find an x - if it doesn't it will error. Also you need to refer to each name with a separate Range function to turn it into a cell reference and then an external range function to turn those two cell references into a full range.
Set target = Range(X & "_Range") 'point to the range
'to hide
'target.EntireColumn.Hidden = True
'to unhide
target.EntireColumn.Hidden = False

Related

Referencing a range that is input into the function is causing the code to stop

this is both my first post on stack overflow and my first vba project so please forgive and correct any mistakes.
I am trying to create a function that looks at two ranges and pastes only non-null duplicate values into a third range.
My function takes in the two ranges that are to be compared as input (rangeOne and rangeTwo). It also takes in a third range, which is the first cell in the paste range (strtCell). I am trying to make it so that the duplicate values are pasted along a column for now, but so that they start at the cell that the user inputs.
The problem is that whenever I reference strtCell, the code just stops running. I have tested various breakpoints and using strtCell in many different contexts. However, whenever it is used at all in the code, the code stops executing at that line without any error message. If I comment out or delete the lines referencing it, the cell I enter the function in is set to "Success" as expected.
Also as an aside, whenever I type Option Explicit on the top line, it disappears from the workspace but still seems to be in effect. I am confused as to why it is disappearing.
Here is the relevant code [I put the bracketed text for comments I added in the post]:
(Option Explicit)
Public Function CopyDuplicates(rangeOne As Range, strtCell As Range, rangeTwo As Range)
'Declare necessary things
Dim arrayOne(), arrayTwo(), element, element2, element3, element4
Dim cell As Range
Dim pairs As New Collection
pairs.Add ""
Dim flag As Boolean
Dim numberOfDuplicateValues As Integer, i As Integer
numberOfDuplicateValues = 0
i = 0
'Import the Ranges
'Set array lengths to sizes of ranges
ReDim arrayOne(rangeOne.Count - 1)
ReDim arrayTwo(rangeTwo.Count - 1)
'Set array values to values of ranges
'Range 1
For Each cell In rangeOne
arrayOne(i) = cell.Value
i = i + 1
Next cell
'Range 2
[Exact same process as Range 1]
'Check for Duplicates
[This is just some code that adds all the duplicate values into pairs.
I have used a breakpoint to verify that pairs is being updated as intended.]
'Print duplicates
For Each element4 In pairs
strtCell.Value = CStr(element4)
strtCell = strtCell.Offset(1, 0)
Next element4
CopyDuplicates = "Success"
End Function
I'll also just add that strtCell is meant to be a single-cell range, but I have tested it as both a single and multi cell range, with the same weird stopping result in both cases.
Thanks for any help, this problem has had me stumped for hours!

How can I create a formula using VBA to reference a dynamically offset range of cells?

This is a very strangely specific need, and the last thing I need to complete my suite of new macros.
Note: The '---' at the top of the sheet is there to represent several months of the same report going back in time
As you can see in the image linked above, I have two highlighted sections. I need to make column G the sum of E and F from the previous report's numbers. Because there is a new set of data added every day, I can't reference specific cells and it must be dynamic. The larger problem here is that my number of customers will change every so often. It will only go up and it will always be in the same order; even if a lose a customer they stay on the report in the same spot.
My only theories on how to get this done are:
Find the second to last instance of customer A and define a rng based on the offset cells to the right. My problem with this is that—to my understanding—even filling that formula all the way down will just give me the one value.
Adding =SUM((INDIRECT(ADDRESS(ROW()-5,COLUMN()-2))):(INDIRECT(ADDRESS(ROW()-5,COLUMN()-1)))) to the blank cells. My problem with this is that the -5 in the offset is able to change, and even defining it by the number of blank cells will cause a mistake the first time a new customer comes on.
Any insight would be very much appreciated. And please let me know if you have any clarifying questions; I'm happy to answer/edit the original post as needed.
It can probably be optimised further by actually pre-calculating the range, but the naive version would be:
=SUMIFS([Outstanding Mail],[Date],LOOKUP([#Date]-1,[Date]),[Customer],[#Customer])
+SUMIFS([Outstanding Faxes],[Date],LOOKUP([#Date]-1,[Date]),[Customer],[#Customer])
Which relies on the fact that your dates are sorted, and that LOOKUP returns the last value that is not greater than the supplied value, so the [#Date]-1 makes it look up the biggest date that is less than the provided date. Will not work on an unsorted range.
#Gserg got an answer ahead of me, and his solution is one good elegant line, although i think it goes on the assumption there will be items every day there (if I`m not wrong?), and your screenshot suggest they may not be consecutive days all the time.
If you are still looking at a VBA solution as well, I would do something like this:
Option Explicit
Sub addOffsetFormula()
'Declare and set your workbook
Dim wb As Workbook: Set wb = ActiveWorkbook
'Declare and set your spreadsheet
Dim shData As Worksheet: Set shData = wb.Worksheets("Data")
'Set your last row/column for a dynamic aproach
Dim lRow As Long: lRow = shData.Cells(1, 1).End(xlDown).Row
Dim lCol As Long: lCol = shData.Cells(1, shData.Columns.Count).End(xlToLeft).Column
'Declare some further variables to help
Dim R As Long, X As Long
Dim sumFormula As String
'Declare and set your array to hold your data - much faster to iterate through the array than spreadsheet itself
Dim tblData(): tblData = shData.Range(shData.Cells(1, 1), shData.Cells(lRow, lCol))
For R = LBound(tblData) + 1 To UBound(tblData) 'Iterate through your data
For X = LBound(tblData) + 1 To UBound(tblData) 'Iterate through the same data again
If tblData(R, 4) = tblData(X, 4) And X > R Then 'Check for match with the next client found (assuming clients are unique)
'Set your formula to a variable, helps with debugging
sumFormula = "=SUM(R[-" & X - R & "]C[-2]+R[-" & X - R & "]C[-1])"
'Assign the formula to the respective cell _
If the spreadsheet is massive, you might need to add some optimisation _
(ie: assign everything to an array first, then dump into the spreadsheet)
shData.Cells(X, 7).FormulaR1C1 = sumFormula
End If
Next X
Next R
End Sub
Note: It won't add anything to the first few lines or new clients, as there is nothing to match against previously, but i expect that should work the same with any formula too.

Searching a Range in Excel VBA

I have a string array, and in each slot of the array is a 'range of columns' in a string format e.g "B:J" "k:W" "AC:AG"
The method is being passed a string which is a column name e.g "C"
I need to search see if "C" is inside "B:J".
So basically it needs to check to see if "C" is in "BCDEFGHIJ" which it is and if it is break from a loop
But if I input "A" it should then go to the next slot in the array.
Thank
This function will return the position in the array where the single column is included in the range. It uses the Intersect property to determine if the two ranges intersect.
Function ArrayPos(sColLetter As String, vaRanges As Variant) As Long
Dim i As Long
Dim sh As Worksheet
Dim lReturn As Long
Set sh = Sheet1
For i = LBound(vaRanges) To UBound(vaRanges)
If Not Intersect(sh.Columns(sColLetter), sh.Columns(vaRanges(i))) Is Nothing Then
lReturn = i
Exit For
End If
Next i
ArrayPos = lReturn
End Function
It's used like this, from the Immediate Window for example
?arraypos("M",array("B:J","K:W"))
1
You would want to add some error checking to make sure the arguments can be converted to ranges, but I'll leave that to you. You could do this without Range objects by splitting the range string on colon and comparing the ASCII values of the letters.
You might take a look at
How to convert a column number (eg. 127) into an excel column (eg. AA)
sheet.Application.ActiveCell.Column //for current selected column
int col=sheet.Range("A").Column; // for any choosen column
returns current column number, just apply the same for your range B:J, check if J (10) > C (3) > B ( 2)
Then you can say that it is inside that range.
In case of need the other way around this function with the link I gave you.
ConvertToAlphaColumnReferenceFromInteger(sheet.Application.ActiveCell.Column)
Done for C#/.NET

Transform a worksheet to an array containing only strings

I need to extract the data from an excel worksheet to an array that will be used in an application that uses VBScript as scripting language (Quick Test Professional). We can use the following code for that:
' ws must be an object of type Worksheet
Public Function GetArrayFromWorksheet(byref ws)
GetArrayFromWorksheet = ws.UsedRange.Value
End Function
myArray = GetArrayFromWorksheet(myWorksheet)
MsgBox "The value of cell C2 = " & myArray(2, 3)
All nice and well, but unfortunately the array that gets returned does not only contain the literal text strings, but also primitives of type date, integer, double etc. It happened multiple times that that data got transformed.
[edit] Example: when entering =NOW() in a cell and set the cell formatting to hh:mm makes the displayed value 17:45, the above method retuns a variable of type double and a value like 41194.7400990741
The following solution worked better: I can get the literal text from a cell by using the .Text property, but they only work on one cell and not on a range of cells. I cannot do this at once for an array as I could with the .Value property, so I have to fill the array one cell at a time:
Public Function GetArrayFromWorksheet_2(byref ws)
Dim range, myArr(), row, col
Set range = ws.UsedRange
' build a new array with the row / column count as upperbound
ReDim myArr(range.rows.count, range.columns.count)
For row = 1 to range.rows.count
For col = 1 to range.columns.count
myArr(row, col) = range.cells(row, col).text
Next
Next
GetArrayFromWorksheet_2 = myArr
End Function
But ouch... a nested for loop. And yes, on big worksheets there is a significant performance drop noticable.
Does somebody know a better way to do this?
As we covered in the comments, in order to avoid the issue you will need to loop through the array at some point. However, I am posting this because it may give you a significant speed boost depending on the type of data on your worksheet. With 200 cells half being numeric, this was about 38% faster. With 600 cells with the same ratio the improvement was 41%.
By looping through the array itself, and only retrieving the .Text for values interpreted as doubles (numeric), you can see speed improvement if there is a significant amount of non-double data. This will not check .Text for cells with Text, dates formatted as dates, or blank cells.
Public Function GetArrayFromWorksheet_3(ByRef ws)
Dim range, myArr, row, col
Set range = ws.UsedRange
'Copy the values of the range to temporary array
myArr = range
'Confirm that an array was returned.
'Value will not be an array if the used range is only 1 cells
If IsArray(myArr) Then
For row = 1 To range.Rows.Count
For col = 1 To range.Columns.Count
'Make sure array value is not empty and is numeric
If Not IsEmpty(myArr(row, col)) And _
IsNumeric(myArr(row, col)) Then
'Replace numeric value with a string of the text.
myArr(row, col) = range.Cells(row, col).Text
End If
Next
Next
Else
'Change myArr into an array so you still return an array.
Dim tempArr(1 To 1, 1 To 1)
tempArr(1, 1) = myArr
myArr = tempArr
End If
GetArrayFromWorksheet_3 = myArr
End Function
Copy your worksheet into a new worksheet.
Copy Paste values to remove formulas
Do a text to columns for each column, turning each column into Text
Load your array as you were initially doing
Delete the new worksheet
You cant do this quickly and easily without looping through the worksheet.
If you use the technique above with 2 lines of code it must a variant type array.
I've included a real example from my code that does it in 6 lines because I like to A) work with the worksheet object and B) keep a variable handy with the original last row.
Dim wsKeyword As Worksheet
Set wsKeyword = Sheets("Keywords")
Dim iLastKeywordRow As Long
iLastKeywordRow = wsKeyword.Range("A" & wsKeyword.Rows.Count).End(xlUp).Row
Dim strKeywordsArray As Variant
strKeywordsArray = wsKeyword.Range("A1:N" & iLastKeywordRow).Value
Note your array MUST be a variant to be used this way.
The reason that Variants work like this is that when you create an array of variants, each 'cell' in the array is set to a variant type. Each cell then get's it's variant type set to whatever kind of value is assigned to it. So a variant being assigned a string gets set to variant.string and can now only be used as a string. In your original example it looks like you had time values which were kind of stored as variant.time instead of variant.string.
There are two ways you can approach your original problem
1) loop through and do the process with more control, like the double nested for loop. explained in another answer which gives you complete control
2) store all the data in the array as is and then either re-format it into a second array, or format it as desired text as you use it (both should be faster)

Trouble Figuring out How to replace Cells in a Range with Specific Text - Excel VBScript

I must be having a brain fog at this point because I am certain this is easy to do, and in fact I have managed to create other functions that are a bit more complicated for this project.
Anyway, what I am trying to do. I have a sheet (inventory-data) and in column 1, it lists a company name, which is a same for all the rows. i.e. each of the 1900 or so rows have companyname in the first cell.
Now, while the data will always be the same at each application, the number of rows will change.
So, I need a function that will first determine what the last row of data is in the range, and then change all of the cells in column one of each record to name_company. The company names will always be the same so I can staticly assign them. Here is what I have that does not work.
I was able to get it to work another way, but it would replace text all the way down to the very last row of the worksheet, way beyond where the data stops.
Thanks!
Sub changeCompany() 'Changes company name as pulled from Agemni into proper ETA format
Dim myCell As Range
Dim RngToChange As Range 'The range of cells that need to be changed
Dim LastRow As Long 'Declare variable to help determine the last row on a variable length worksheet
Dim i As Integer
With Worksheets("inventory-data") 'set the range to change
Set RngToChange = .Columns(1)
End With
LastRow = Worksheets("inventory-data").UsedRange.Rows.Count 'Have Excel determine what the last row is.
For i = LastRow To 1 Step -1
RngToChange.Cells.Value = "name_company"
Next i
End Sub
I've always had more success with [SomeCellOrRange].CurrentRegion.Rows.Count e.g:
Range("A1").CurrentRegion.Rows.Count
UsedRange looks for any use of cells, not limited to a continuous tabular block. It also sometimes needs you to re-save the workbook before it will properly shrink after you have eliminated some rows.

Resources