Error when referring to many multiple named ranges in Range property - vba

I have around 236 named ranges (columns) for a large table of data. I get this error when trying to split up the long code-line of delimited named ranges:
Run-time error '1004' Application-defined or object-defined error
E.g.:
Worksheets("Sheet1").Range("foo1,foo2" _
& "foo3,foo4" _
& "..." _
& "foo235,foo236")
I am trying to filter and unfilter columns based on specific criteria (named ranges). Everything seems to work fine (for smaller strings that only span 1 line in length) until I have to split the code into multiple lines since it reaches the end of the window..
Code -
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$B$3" Then
Worksheets("Sheet1").Range("Fruit," _
& "Months,Colour").EntireColumn.Hidden = Target.Value = "CustomView"
End If
If Target.Address = "$B$3" Then
Worksheets("Sheet1").Range("Colour,Number" _
& "Months").EntireColumn.Hidden = Target.Value = "Custom2View"
End If
End Sub
This code doesn't seem to work. I think it has something to do with the quotes and how excel reads it but i haven't been able to find a fix yet.
New code being tested based on suggestions in comments results in an error Run-time error '1004' Application-defined or object-defined error
Private Sub Worksheet_Change(ByVal Target As Range)
Dim arr, i As Long, rng As Range
If Target.Address = "$B$3" Then
arr = Split("foo1,foo2,foo3,...,foo266,foo267", ",")
Set rng = Worksheets("Database").Range(arr(0))
For i = 1 To UBound(arr)
Set rng = Application.Union(rng, Worksheets("Database").Range(arr(i)))
Next i
rng.EntireColumn.Hidden = (Target.Value = "CustomView")
End If
End Sub

You can use Application.Union to build up a range and then hide/show that range in one shot.
EDIT: based on your second shared file I think you need something like this. Your previous code was not checking the value of the "view name" cell and was applying all of the views, leaving you with the last one...
Eg:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim arr, q As Long, rng As Range, sht As Worksheet
Set sht = Worksheets("Database")
If Target.Address = "$B$3" Then
'unhide all columns forst
sht.UsedRange.EntireColumn.Hidden = False
Select Case Target.Value
Case "CustomView"
arr = Split("A,B,C_,X,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM," & _
"AN,AO,AP,AQ,AR,AS,AT,AV,AW,AX,AY,AZ,BA,BB,BC,BD,BE,BF,BG," & _
"BH,BI,BJ,BK,BL,BM,BN,BO,BP,BQ,BR,BS,BT,BU,BV,BW,BX,BY,BZ,CA," & _
"CB,CC,CD,CE,CF,CG,CH,CI,CJ,CK,CL,CU,CV,CW,CX,CY,CZ,DA,DB,DC", ",")
Case "XX100View"
arr = Split("D,E,F,G,X,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO," & _
"AP,AQ,AR,AS,AT,AV,AW,AX,AY,AZ,BA,BB,BC,BD,BE,BF,BG,BH,BI,BJ," & _
"BK,BL,BM,BN,BO,BP,BQ,BR,BS,BT,BU,BV,BW,BX,BY,BZ,CA,CB,CC,CD,CE," & _
"CF,CG,CH,CI,CJ,CK,CL,CU,CV,CW,CX,CY,CZ,DA,DB,DC", ",")
Case "OtherView"
arr = Split("A,B,D,E,F,G,H,I,X,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM," & _
"AN,AO,AP,AQ,AR,AS,AT,AV,AW,AX,AY,AZ,BA,BB,BC,BD,BE,BF,BG,BH," & _
"BI,BJ,BK,BL,BM,BN,BO,BP,BQ,BR,BS,BT,BU,BV,BW,BX,BY,BZ,CA,CB," & _
"CC,CD,CE,CF,CG,CH,CI,CJ,CK,CL,CU,CV,CW,CX,CY,CZ,DA,DB,DC", ",")
End Select
If Not IsEmpty(arr) Then
Set rng = sht.Range(arr(0))
For q = 1 To UBound(arr)
Set rng = Application.Union(rng, sht.Range(arr(q)))
Next q
rng.EntireColumn.Hidden = True '<<edited
End If 'got a view
End If 'is view name cell
End Sub
PS - your range names don't need to include all of your data: a single cell would be fine, since you use EntireColumn to expand it to the entire sheet height anyway.

Related

Adding custom formula to Range() cell

Sub test()
Application.DisplayAlerts = False
Application.ScreenUpdating = False 'keeping the screen clean
Sheets("Data").Select
'Here is where the error is triggered.
With ThisWorkbook.Worksheets("TestSheet")
'.Range("A2:A" & Lr).Formula = "=CusVlookup(Z2,Data'!A:B,2)" <- this doesnt work either
.Range("A2:A" & Lr) = "=CusVlookup(Z2,Data'!A:B,2)"
End With
End Sub
Function CusVlookup(lookupval, LookupRange As Range, indexcol As Long)
Dim x As Range
Dim Result As String
Result = ""
For Each x In LookupRange
If x = lookupval Then
Result = Result & "," & x.Offset(0, indexcol - 1)
End If
Next x
CusVlookup = Result
End Function
I've tried using the regular Vlookup and works just fine but if I try to use this custom function it triggers the error. By the way I need to get the multiple matches "vlookups" into one cell separated by comma.(I added the goal just in case you know a better/faster way to do the same.)
whats wrong with the code?
Error 1004 - Application-defined or object-defined error

VBA Code for search box that filters table

I've designed a search box that filters my table when text is entered into said search box. The problem is that it is soooo slow, it's almost not even worth having it in my workbook right now.
Can anyone think of any way to revise/improve upon this code?
Here is my code currently:
Private Sub TextBox1_Change()
Dim searchArea As Range, searchRow As Range, searchCell As Range
Dim searchString As String
Dim lastRow As Integer
Application.ScreenUpdating = False
searchString = "*" & LCase(TextBox1.Value) & "*"
Rows.Hidden = False
lastRow = ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row
Set searchArea = Me.Range("f3:f791", "f3" & lastRow)
searchArea.EntireRow.Hidden = True
For Each searchRow In searchArea.Rows
For Each searchCell In searchRow.Cells
If LCase(searchCell) Like searchString Then
searchRow.Hidden = False
Exit For
End If
Next searchCell
Next searchRow
Application.Goto Range("Z1"), True
ActiveWindow.ScrollColumn = 1
Application.ScreenUpdating = True
End Sub
Edited my code to this:
Private Sub TextBox1_Change()
ActiveSheet.ListObjects("states").Range.AutoFilter Field:=1, _
Criteria1:="*" & [G1] & "*", Operator:=xlFilterValues
End Sub
However, this is not working. There are text and numbers in Field 1, and this only is filtering text, not the numbers...
This is definitely redundantly redundant, because your iteration is over a single column:
For Each searchRow In searchArea.Rows
For Each searchCell In searchRow.Cells '### searchRow ONLY HAS ONE CELL! This second/inner loop is totally unnecessary
If LCase(searchCell) Like searchString Then
searchRow.Hidden = False
Exit For
End If
Next searchCell
Next searchRow
Rewrite as:
For Each searchCell in searchArea.Cells '## Assumes searchArea is single column
searchCell.EntireRow.Hidden = Not (LCase(searchCell) Like searchString)
Next
That alone should improve performance, but I think AutoFilter is a better method, and you should be able to derive the basic code for that from the Macro Recorder.
This would look something like:
searchArea.AutoFilter Field:=1, Criteria1:="=" & searchString, _
Operator:=xlAnd, Criteria2:="<>"
This should filter to display only non-blank rows which contain your searchString
#Yowe3k's points about the range assigment should also be noted, and you may use the AfterUpdate event of the TextBox instead of the Change event.
UPDATE This might work to handle your mixed cases of numeric/text values. There might be a better way to do this but I don't see an obvious solution. The AutoFilter is meant to work with either text or numbers, but not both. So this attempts to convert numeric values to string representations. You may need to make changes elsewhere if the numeric values are referenced in formula, etc.
Dim arr, v
Dim tbl As ListObject
Set tbl = ActiveSheet.ListObjects(1)
' ## Disable filter if it's on already
If tbl.Range.AutoFilter Then tbl.Range.AutoFilter
arr = tbl.DataBodyRange.Columns(1).Value
' ## Convert your range of mixed numeric/string to string
For v = LBound(arr, 1) To UBound(arr, 1)
If IsNumeric(arr(v, 1)) Then
arr(v, 1) = "'" & CStr(arr(v, 1))
End If
Next
' ## Put the string data back out to the worksheet
tbl.DataBodyRange.Columns(1).Value = arr
tbl.Range.AutoFilter Field:=1, _
Criteria1:="*" & CStr([G1]) & "*", Operator:=xlFilterValues

VBA excel - return the last matching value in a column using VBA

Basically, I have a rather large (and growing) sheet of position details and I'm looking to build in a sub routine that, once a position number is entered into the relevant cell, will auto-populate the corresponding cells in the row. VLOOKUP would do the trick nicely except, when a position has multiple lines, it returns the earliest set of details--I need it to return the latest.
I can produce the answer I need using a LOOKUP function , but I can't seem to translate the function across to VBA.
Example lookup function:
LOOKUP(D17,1/($D$2:$D$10=D17),E2:E10)
This is what I have so far
Sub Worksheet_Change(ByVal Target As Excel.Range)
If Target.Column = 4 Then
actionrow = Target.Row
resulte = Application.WorksheetFunction.Lookup(2, 1 / Range("D2:D10") = Target.Value, Range("E2:E10"))
If Target.Value <> "" Then
Range("E" & actionrow).formula = resulte
End If
End If
End Sub
I think that looking at column D for a matching value with the Range.Find method would do. Start at the Target cell and use the SearchDirection:=xlPrevious option. Something will always be found. If the row it is found is not the same row as Target then use the value in column E to populate the cell right of Target.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Columns(4), Target) Is Nothing Then
On Error GoTo bm_Safe_Exit
Application.EnableEvents = True
Dim trgt As Range, lastrw As Long
For Each trgt In Intersect(Columns(4), Target)
lastrw = Columns(4).Find(what:=trgt.Value, after:=trgt, _
lookat:=xlWhole, SearchDirection:=xlPrevious).Row
Debug.Print lastrw
If lastrw <> trgt.Row Then
trgt.Offset(0, 1) = Cells(lastrw, trgt.Column + 1).Value
End If
Next trgt
End If
bm_Safe_Exit:
Application.EnableEvents = True
End Sub
This should survive pasting multiple values into column D.
You can use .Find function with parameter SearchDirection:=xlPrevious
For case where you are searching word "AC" in a row 4:
Set FindCell = sh_wb_SF.Range("4:4").Find(What:="AC", LookIn:=xlValues, SearchDirection:=xlPrevious)
If FindCell Is Nothing Then
MsgBox ("Ooooooopppps")
End If

How do I get a cell's position within a range?

How would I go about getting the relative position of a cell within a range? Finding the position of a cell in a worksheet is trivial, using the Row- and Column-properties, but I am unsure of how to do the same within a range.
I considered using the position of the top-left cell in the range I want to find the position of a cell in, and just deduct it (-1) from the position of the cell in the worksheet, but it gets a little bit cumbersome. Is there a more elegant way to go about this?
My best attempt, including a test, so far is this:
Option Explicit
Sub test()
Dim r As Range: Set r = Sheet1.Range("B2:E10")
Dim c As Range: Set c = Sheet1.Range("C2")
Debug.Print "Column in sheet: " & c.Column
Debug.Print "Row in sheet: " & c.Row
Debug.Print "Column in range: " & column_in_range(r, c)
Debug.Print "Row in range: " & row_in_range(r, c)
End Sub
Function column_in_range(r As Range, c As Range) As Long
column_in_range = c.Column - (r.Cells(1, 1).Column - 1)
End Function
Function row_in_range(r As Range, c As Range) As Long
row_in_range = c.Row - (r.Cells(1, 1).Row - 1)
End Function
This gives the desired output:
Column in sheet: 3
Row in sheet: 2
Column in range: 2
Row in range: 1
But I wonder if there are any native functions I can use instead?
updated using variant provided by lori_m
But I wonder if there are any native functions ...
use this
Sub test()
Dim r As Range, c As Range
With Sheet1
Set r = .[B2:E10]
Set c = .[C2]
End With
If Not Intersect(r, c) Is Nothing Then
Debug.Print "Column in sheet: " & c.Column
Debug.Print "Row in sheet: " & c.Row
Debug.Print "Column in range: " & Range(r(1), c).Columns.Count
Debug.Print "Row in range: " & Range(r(1), c).Rows.Count
End If
End Sub
output
Column in sheet: 3
Row in sheet: 2
Column in range: 2
Row in range: 1
There is no native way to do it. I also do what you have mentioned in the code above. However I put some extra checks.
Sub test1()
Dim r As Range: Set r = Sheet1.Range("B2:E10")
Dim c As Range: Set c = Sheet2.Range("C2") '<~~ Changed Sheet1 to sheet2
Dim rng As Range
On Error Resume Next
Set rng = Intersect(c, r)
On Error GoTo 0
'~~> Check if the range is in main range
If Not rng Is Nothing Then
'
'~~> Rest of your code
'
Else
MsgBox c.Address & " in " & c.Parent.Name & _
" is not a part of " & _
r.Address & " in " & r.Parent.Name
End If
End Sub
In my opinion there is almost native way to check it but result is a string required some additional manipulation. All you need to use is a proper construction of .Address property (according to MSDN). Some examples:
Dim r As Range: Set r = Sheet1.Range("B2:E10")
Dim c As Range: Set c = Sheet1.Range("c2")
Debug.Print c.Address(False, False, xlR1C1, , r.Cells(0, 0))
'>>result: R[1]C[2]
'-----------------------------------------------------
Set c = Sheet1.Range("e2")
Debug.Print c.Address(False, False, xlR1C1, , r.Cells(0, 0))
'>>result: R[1]C[4]
'-----------------------------------------------------
Set c = Sheet1.Range("e5")
Debug.Print c.Address(False, False, xlR1C1, , r.Cells(0, 0))
'>>result: R[4]C[4]
'-----------------------------------------------------
Take a look on MSDN to see more.
You can use something like :
MsgBox ActiveCell.Address(RowAbsolute:=True, _
ColumnAbsolute:=True, _
ReferenceStyle:=xlR1C1, _
External:=False, _
RelativeTo:=Range("B2"))
'Or shorter version :
MsgBox ActiveCell.Address(, , xlR1C1, False, Range("B2"))
But you'll have both information about row and column in the range, but not separately.
So you'll still need to extract these values from the answer (look like : R18C20) in two functions, so almost the same issue...
I'm not totally sure if this is what you are after.
But here it goes:
Sub ts2()
Dim test As Range
Set test = Range("B2:E10")
Dim topcorner As Range
Dim testcell As Range
Set topcorner = Cells(test.Row, test.Column)
Set testcell = Range("D7")
rel_row = testcell.Row - topcorner.Row
rel_col = testcell.Column - topcorner.Column
End Sub
By this, you will find the relative position.
But maybe you were looking for some built in function ?
If this was not the thing you were after, please edit your post...

Create named range of all cells that contain constants or formulas?

I am trying to create a named range that refers to all cells containing formulas or constants. But I get an error message on the row that starts with Set r = Union(...
How can I get this to work?
Dim r As Range
Set r = Union(Sheet1.Cells.SpecialCells(xlCellTypeConstants), Sheet1.Cells.SpecialCells(xlCellTypeFormulas), _
Sheet22.Cells.SpecialCells(xlCellTypeConstants), Sheet22.Cells.SpecialCells(xlCellTypeFormulas))
Union only works with Ranges that are on the same sheets. You can build a collection of the addresses like this though
Sub Main()
Dim arr As Variant
arr = Array( _
GetAddresses(Sheet1, xlCellTypeConstants), _
GetAddresses(Sheet1, xlCellTypeFormulas), _
GetAddresses(Sheet2, xlCellTypeConstants), _
GetAddresses(Sheet2, xlCellTypeFormulas) _
)
Dim r As Variant
For Each r In arr
If Len(r) > 0 Then Debug.Print r
Next
End Sub
Function GetAddresses(sh As Worksheet, cellType As XlCellType) As String
On Error Resume Next
GetAddresses = sh.Name & "!" & sh.Cells.SpecialCells(cellType).Address
On Error GoTo 0
End Function
If you need to handle your errors differently, have a look at this answer