for each control variable must be variant or object - string

Really new to VBA here... I've looked around and tried to piece together some code to fulfil my need. Think it's almost there, but I'm getting errors that are likely easy to overcome and yet I don't know how.
The code looks at the current sheet (STOCK), and takes a 'target' text value from cell A2. It then searches a named range in another sheet 'Other'. If it determines one of the cells ('cand') in Other to be equal to the target value, then a value of "True" will be applied to column G in the STOCK sheet, on the same row of the original target.
Hopefully this makes sense. I've copied in the code which will maybe shed more light on things.
Dim target As String
Dim cand As String
Dim currentrow As Integer
Sub search_named_range()
' This range is hard coded; we can try A:A if hard code version works '
For Each target In Worksheets("STOCK").Range("A2:A1000")
' retrieve the row of the current range, for use when setting target values '
currentrow = Range(target).Row
' FOR loop to search range of part numbers in Mojave '
For Each cand In Worksheets("Other").Range("N9:N150")
If StrConv(cand.Value, 2) = StrConv(target, 2) Then
Worksheets("STOCK").Range("G" + currentrow) = "True"
GoTo FORend
End If
Next cand
' If part is not found, do nothing and return to find next target '
FORend: Next target
End Sub
Currently I'm getting the error 'For Each control variable must be Variant or Object', but can't find anywhere that explains why this is. I'm sure it's pretty obvious, but a steer would be really appreciated.
Thanks.

You can't use a String variable in a For Each. You're using tartget and cand as the control variables in your For Each loops but you have defined them as strings. They need to be an object, and specifically an object that is contained the collection of objects you're iterating. You're iterating over a range, which is a collection of ranges, so your control variables need to be Range objects.
Sub search_named_range()
Dim rCell As Range
Dim rCand As Range
For Each rCell In Worksheets("STOCK").Range("A2:A1000").Cells
For Each rCand In Worksheets("Other").Range("N9:N150").Cells
If StrComp(rCand.Value, rCell.Value, vbTextCompare) = 0 Then
rCell.Offset(0, 6).Value = "True"
Exit For 'exits the rCand For, but no the rCell one
End If
Next rCand
Next rCell
End Sub
Other changes that weren't correcting errors:
I'm not sure why you declared your variables outside the sub, but I put them inside.
You don't need to define .Cells at the end of the For Each line, but I like to. You could iterate over .Rows or .Columns or .Areas with a Range (although .Cells is the default).
There's nothing wrong with StrConvert, but you could also use LCase() or, as I do, StrComp.
Since I already have a reference to a cell on the current row (rCell), I use that and Offset to fill in a column I want.

Related

How to put all values in the first row into an array?

I am trying to set my array equal to all column headers of tables in a worksheet.
If I were doing this in Excel, I would click on A1 and do Control+Shift+Right Arrow. I've found some 15 year old code attempting to simulate this, shown below, but it doesn't recognize the last Column.
Sub Ls_List_Click()
'variables used in for each loops
Dim Column_Array() As Variant
Dim EndRange As Range
ThisWorkbook.Worksheets(Me.Ls_List.Value).Select
Range("A1", Range("A1").End(xlRight)).Select
EndRange = Range("A1").End(xlRight).Offset(2, 0)
Range(EndRange, EndRange.End(xlRight)).Select
EndRange = EndRange.End(xlRight).Offset(2, 0)
Column_Array() = EndRange
'... couple of for each loops
End Sub
I get error 1004 on the following line:
Range("A1", Range("A1").End(xlRight)).Select
If you replace xlRight with xlToRight you should be good
I believe you're looking for something more like:
Sub Ls_List_Click()
'variables used in for each loops
Dim Column_Array() As Variant
Column_Array() = Range(Cells(1,1),Cells(1, Cells(1, 1).End(xlToRight).Column)).value
'... couple of for each loops
End Sub
There didn't appear to be rhyme or reason for the .select components beyond stepping through to see where things were (nice for testing... i guess?).
Just one line of code gets your array generated for all the contiguous headers in your first row.
Edit:
Checking back, because EndRange is used for looping, you may want to write:
Sub Ls_List_Click()
'variables used in for each loops
Dim Column_Array() As Variant, EndRange as Long 'reference to column number, not a range, so watch referencing
EndRange = Cells(1, 1).End(xlToRight).Column
Column_Array() = Range(Cells(1,1),Cells(1, EndRange)).value
'... couple of for each loops
End Sub
Two main issues with your code:
You misspelled xlRight - the correct value is xlToRight. That's why it is recommended to use Option Explicit, so such errors are detected at compile time.
You shouldn't use Select - what for? If you want to reference particular cells/range, just use Cells(row, column) (row and column are integers) or Range("A1:B3") (just example).
Having said that, to get values into an array, you should use:
firstRowArray = Range(Cells(1, 1), Cells(1, 1).End(xlToRight)).Value2
Expalnation:
I used Range overload, accepting two cells as top-left and bottom-right cells to define the range. Top left is Cell(1, 1), i.e. A1 and bottom left is Cells(1, 1).End(xlToRight), which you are already familiar with :)
Then I use Value2 property, which return array of values (you could use Value but it's slower).
Note that, the result will be two-diensional array with number of rows and number of columns same as in the range.

Unable to read object properties of Set Range variables

I am developing a workbook with vba; in it are worksheets that can contain a varying amount of named "CheckCells" which act as "boolean" dropdowns ("Yes"/"No") to manipulate other cells within the same worksheet.
I'm trying to develop a Public Function macro which can be called within a formula of a cell. Its purpose is to "collapse" all of these CheckCells into a single Long-type output -- the output will equal the column index from which I read off of special "archive" worksheets within the workbook; the "archive" sheets will act as permanent "memory" for certain values, as they are critical to the workbook function but would be lost upon workbook open/close.
The following is a shortened version of the code I need help with. As indicated by commented Debug.Print lines, it appears that any way I try to Dim a Range-type variable, then Set it to equal a range of cells, most attempts at reading using that variable's object properties throw errors results such as "Cannot get property...", "Object-defined error...", or "Application-defined error...". "VersionCheckCell" is indeed a named cell of the ActiveSheet here.
Public Function CollapseRun() As Long
Dim ActSht As Worksheet
Set ActSht = ThisWorkbook.ActiveSheet
'Variables to get the range of cells to loop through when finding VersionCount.
Dim intVisRows As Long: intVisRows = 1
Dim intVisCols As Long: intVisCols = 1
'[Code to evaluate inVisCols and intVisRows omitted to shorten post...]
Dim EndCell As Range
Set EndCell = ActSht.Cells(intVisRows, intVisCols)
'Debug.Print [VersionCheckCell].Name.Name 'This works. If explicitly giving valid ranges, _
I can read its properties like normal.
'Debug.Print EndCell.Name.Name 'This fails. When I define a _
Range variable, then Set it as some cells, I can't read its object properties.
Dim VisibleRange As Range
Set VisibleRange = ActSht.Range(ActSht.Cells(1, 1), EndCell) 'Cell Range to
loop through.
Dim intCzCells As Integer: intCzCells = 0
Dim Cell As Range
Dim arrCz() As Variant
'This is the Application-defined error-throwing loop.
For Each Cell In VisibleRange
If InStr(Cell.Name.Name, "CheckCell") <> 0 Then 'Checks cell's name for "CheckCell".
intCzCells = intCzCells + 1
arrCz(intCzCells) = Cell.Address 'Storing addresses to later check values.
End If
Next Cell
'[More code after this loop to determine CollapseRun value...]
I did research before asking and found many other questions with labels such as "Application-defined error when doing ...", etc. The problems/solutions of many of these stemmed from the QA improperly Setting the variable which was throwing errors when being read. I've stared and tinkered with this code for several hours and cannot figure out how I might be improperly setting the VisibleRange or the EndCell variables, so I'm inclined to believe something else is wrong, but I'm hoping someone here can nudge me in the right direction.
I'm on Windows 10 using Excel 2016.
(Edit: Insert missing portion of code where intCzCells was initialized as 0.
(Edit 2: Fixed unnecessary range evaluation as pointed out in a comment.)
The main issue, as pointed out by John Coleman in comments, was that .Name.Name would throw errors if the range wasn't a named cell.
One way to resolve this issue is to use workaround error handling within the loop (I also fixed the code to actually add the addresses to the array properly).
For Each Cell In VisibleRange
On Error GoTo NoName
If InStr(Cell.Name.Name, "CheckCell") <> 0 Then
intCzCells = intCzCells + 1
ReDim Preserve arrCz(intCzCells - 1) 'Array indexed from 0.
arrCz(intCzCells - 1) = Cell.Address
End If
NoName:
Resume NextIteration
NextIteration:
Next Cell

Union function in VBA

I am trying to filter my data, and add the desired data to the Range. I have set my desired range to null and initiated a search in a particular column for a particular keyword, which if found adds the entire row in my Range.
Column 'E' is been searched for a Value "Desired Value", if found the entire row is added to the PTRange, which is initially set to Nothing(is Empty).
Dim rng As Range
Dim PTRange As Range
Dim count As Long
count = 1
Set PTRange = Nothing
Set rng = Range("E2:E20")
For Each cell In rng
If cell.Value = ("Desired Value") Then
PTRange = Application.Union(Range(PTRange), Range(Rows(count)))
End If
count = count + 1
Next cell
But the compiler shows that there is something wrong in the code where Union method is used.
UPDATE :
Initialization of PTRange and the following change
PTRange=Union(PTRange,Rows(count))
worked for me. Thanks
It is hard to get you to your desired goal as I'm a little unclear what that is but the below can serve as both assistance on your code with a possible solution following.
Firstly I would recommend you always have Option Explicit turned on especially if calling out to Stack Overflow for assistance.
Option Explicit forces you to declare all variables, in this instance it would be of help as you cell as a variable which can appear ambiguous. When it is not declared, it starts its life as a variant and is defined to a specific format on first use, in this case the compiler declared cell as a Range.
It also helps encourage better coding and to understand what is happening in the code more. To turn it on, type Option Explicit at the very top of the code pane (before any procedures or variable declarations), also while in the VBE (Visual Basic Editor) click 'Tools' > 'Options' and ensure 'Require Variable Declaration' is ticked.
Below are some comments within the code you have, these should help you understand the issues you are facing.
Dim rng As Range
Dim PTRange As Range
Dim count As Long
count = 1
'If the range is nothing then it can not be used, it must be started as
'something
Set PTRange = Nothing
Set rng = Range("E2:E20")
'This is where declaring 'cell' would help understanding, it will become a
'Range at the VBE discretion\decision based on what you are trying to do
'with it. Even when looking at a single cell, you do so through a Range,
'it is a reference to one or more cells in a worksheet
For Each cell In rng '
If cell.Value = ("Desired Value") Then
'This will not work for a number of reasons
'O- As BrakNicku stated, there needs to be 'Set' at the front
'O- This is not remembering the values into the 'PTRange' but
' adding a reference to where those values are. I.e. You are
' not taking note of everybody in the house, but just remembering
' their house address
'O- You do not need to put 'Range()' around 'PTRange' as it is
' already a range
'O- 'Rows(count)' is not valid syntax and does not pass a range into
' the union
'O- 'Union' will put together 2 or more references to ranges, it
' will not put their values in any specific place on a worksheet
PTRange = Application.Union(Range(PTRange), Range(Rows(count)))
End If
count = count + 1
Next cell
I hope the above has been informative and below is an example to a potential solution:-
Option Explicit
Public Sub Sample()
Dim LngRow As Long
Dim Rng As Excel.Range
Dim Rng_Cl As Excel.Range
Dim Rng_PT As Excel.Range
LngRow = 21
'Initialise my Range
Set Rng_PT = Range("E21") '" & LngRow)
'Get the search range
Set Rng = Range("E2:E20")
'Loop through all the cells
For Each Rng_Cl In Rng
If Rng_Cl.Value = ("Duck") Then
'Adds just 5 column and not the whole row
Range(Rng_Cl.Address, ActiveSheet.Cells(Rng_Cl.Row, Rng_Cl.Column + 5).Address).Copy Range("E" & LngRow)
LngRow = LngRow + 1
End If
Next
'It is good practice to remove references once you are done with them
'This can help with memory consumption/speed and also more serious
'memory leaks (and actual stack overflows!) on larger macros
Set Rng = Nothing
Set Rng_PT = Nothing
End Sub

Excel VBA Hardcode every Named Range in formulas

I currently copy a worksheet that contains workbook and worksheet-specific named ranges.
The worksheet-specific references need to be the way they are, the workbook ones need to be hardcoded.
I am looking for a way to fixate every named range that contains an external reference.
My current code looks like this, but it's not really what I am after:
Sub HardcodeValuesInExternalNamedRanges(wb As Workbook, ws As Worksheet)
Dim namCur As Name
For Each namCur In wb.Names
If (InStr(1, namCur.RefersTo, ThisWorkbook.Name) > 0) Then
namCur.RefersTo = "=" & ws.Evaluate(namCur.RefersTo)
End If
Next namCur
End Sub
I could not find a way to fixate the values properly. The way it is done now, does the job to some degree but I would prefer if the values were properly converted within the cell and not just on a named range basis.
Another option would be to loop through all cells and see if it contains a named range, but I think this would be too time consuming.
Is there a way to do it more efficiently?
Rather than loop through the cells, how about looping a .FindNext like this?
Do
rng.Value = rng.Value 'Hardcodes the value
Set rng = .FindNext(What:=ThisWorkbook.Name Lookin:=xlFormulas) 'finds the next Value to be hardcoded
Loop While Not Rng Is Nothing

If value is in array then

What I'm trying to achieve is a loop that will check if a cell (which will be two letters of the alphabet) is found in a array, if this is the case other stuff will happen.
So far my code looks like:
Sub Mortgagee()
Dim Symbol As Variant
Dim i As Long
Symbol = Range("C1:C11").Value
For i = LBound(Symbol, 1) To UBound(Symbol, 1)
If Symbol.contains("A1") Then
Range("G1").Copy
Range("A1").Select
ActiveSheet.Paste
End If
Next i
End Sub
In your code above, Symbol is only taking the value of the first cell in the range - in this case, it is just taking the value of whatever is in cell C1.
I'm going to assume that you what you are trying to do is check if the value of a cell exists in an array - not if the cell itself is (which would mean you had an array of cell, or Range, objects).
Sub Mortgagee()
Dim i as Long
Dim arrSymbol(1 to 11) as String
For i = 1 to UBound(arrSymbol)
If arrSymbol(i) = "value to match" Then
'Do work here
End If
Next i
End Sub
If you provide more information about the problem, specifically what value(s) you are checking for and also where Codes came from and what it is then I can help you out some more.

Resources