I have a code where there could be an instance of "Apple - Total", but it is very rare, while there will always be an instance of "Apple". How could I create a code to check whether the string exists in a row? The problem is at the moment is that the code bugs out if it is not there. If there is an instance of "Apple - Total" it should take priority over just "Apple". Something like the Try function in R would work.
If WorksheetFunction.Match(Apple & "-Total", Sheets("SOFP").Range("2:2"), 0) > 0 Then
letr = WorksheetFunction.Match(Fund & "-Total", Sheets("SOFP").Range("2:2"), 0)
letr = Split(Cells(, letr).Address, "$")(1)
cur = Sheets("SOFP").Offset(1, 0).Value
ElseIf WorksheetFunction.Match(Apple , Sheets("SOFP").Range("2:2"), 0) > 0 Then
letr = WorksheetFunction.Match(Fund, Sheets("SOFP").Range("2:2"), 0)
letr = Split(Cells(, letr).Address, "$")(1)
cur = Trim(Sheets("SOFP").Offset(1, 0).Value)
End If
Since:
it's always better :
avoid On Error Resume Next approach
which is quite dangerous and should be limited to a very few cases (like to check for any collection element)
use Match() function of the Application object instead of WorksheetFunction object
because it traps the error into its return value and hence doesn't stop code execution upon possible Match() failure
assuming:
you want to store into cur the value in the row beneath the proper column
"Apple" and "Fund" are two String literals and not String variables
a first approach, following more closely yours, could be the following:
Option Explicit
Sub main()
Dim letr As Variant
Dim cur As Double
With Sheets("SOFP").Range("2:2") '<-- reference your worksheet row 2
If Not IsError(Application.Match("Apple-Total", .Cells, 0)) Then '<-- if there's "Apple-Total"...
letr = Application.Match("Fund-Total", .Cells, 0) '<-- ...then try finding "Fund-Total"
ElseIf Not IsError(Application.Match("Apple", .Cells, 0)) Then '<-- otherwise if there's "Apple"...
letr = Application.Match("Fund", .Cells, 0) '<-- ...then try finding "Fund"
End If
If Not IsError(letr) Then '<-- if the "proper Fund" has been succesfully found...
letr = Split(Cells(, letr).Address, "$")(1) '<-- ...then get "proper Fund" column
cur = Trim(.Range(letr & "2").Value) '<-- and return the value in the 3rd row (i.e. with a row index of 2 with reference to row "2")
End If
End With
End Sub
But you may want to consider the following "Find()" approach:
Option Explicit
Sub main2()
Dim f As Range
Dim cur As Double
With Sheets("SOFP").Range("2:2") '<-- reference your worksheet row 2
If Not .Find(what:="Apple-Total", LookIn:=xlValues, lookAt:=xlWhole, MatchCase:=False) Is Nothing Then '<-- if "Apple-Total" has been found ...
Set f = .Find(what:="Fund-Total", LookIn:=xlValues, lookAt:=xlWhole, MatchCase:=False) '<-- ...then try finding "Fund-Total"
ElseIf Not .Find(what:="Apple", LookIn:=xlValues, lookAt:=xlWhole, MatchCase:=False) Is Nothing Then '<-- otherwise, if "Apple" has been found ...
Set f = .Find(what:="Fund", LookIn:=xlValues, lookAt:=xlWhole, MatchCase:=False) '<-- ...then try finding "Fund"
End If
If Not f Is Nothing Then cur = Trim(f.Offset(1).Value) '<-- if the "proper Fund" has been succesfully found then store the value in row 3 of its corresponding column
End With
End Sub
which I think is much neater
You could also use:
If iserror(application.match)... and handle it that way
On error goto TryApple
' try for total and goto eHandle if found
TryApple:
On error goto eHandle
' try for Apple
eHandle:
The first try for total is like the Try, TryApple is like catch, and eHandle is default
Related
I'm stuck inserting a for loop inside a For function. Essentially, the script is supposed to search for the strings in the array and then paste it in D38 and paste the value of the next string into the next cell by increments of 3 (As seen by the For x = 38 To 100 Step 3). The issue I have is that the Next x is skipping the next value of the string and the loop gets stuck. I tried inserting the For x = 38 To 100 Step 3 inside the Else statement of the first for loop but it doesn't work. What else can I do to fix this? Thank you!
strings = Array("String 1", "String 2")
For Each strng In strings
strSearch = strng
For x = 38 To 100 Step 3
Worksheets("Paste Results Here").Activate
With ActiveSheet.Columns("D:D")
Set rFind = .Find(strSearch, LookIn:=xlValues, LookAt:=xlPart, SearchDirection:=xlNext, MatchCase:=True)
If rFind Is Nothing Then
MsgBox ("There is no " & strSearch)
Exit Sub
Else
rFind.Activate
ActiveCell.Offset(0, 5).Resize(3, 1).Copy
Worksheets("Data").Activate
Set rng = Worksheets("Data").Cells(x, 4)
rng.Activate
ActiveCell.PasteSpecial xlValues
End If
End With
Next x
Next strng
I believe this is what you're trying to do, but let me know if it isn't. Get rid of the inner loop all together and only increment x by 3 when the string is found.
strings = Array("String 1", "String 2")
x = 38
For Each strng In strings
strSearch = strng
Worksheets("Paste Results Here").Activate
With ActiveSheet.Columns("D:D")
Set rFind = .Find(strSearch, LookIn:=xlValues, LookAt:=xlPart, SearchDirection:=xlNext, MatchCase:=True)
If rFind Is Nothing Then
MsgBox ("There is no " & strSearch)
Exit Sub
Else
rFind.Offset(0, 5).Resize(3, 1).Copy
Worksheets("Data").Activate
Set rng = Worksheets("Data").Cells(x, 4)
rng.PasteSpecial xlValues
x = x + 3
End If
End With
Next strng
Exit Sub bails out of the procedure scope entirely.
You don't want that - you want to bail out of the inner loop, but keep iterating to perform the next search.
Exit For will do that.
If rFind Is Nothing Then
MsgBox ("There is no " & strSearch)
Exit For
Not clear what your conditions are, but it sounds like you also want to Exit For when you do have a match. Now, if you bail out of a loop whether or not you've found what you're looking for, reconsider whether you need an inner loop at all.
Or, search for each search-string for each value of x instead of searching for each value of x for every search-string, as Scott suggested ("flip the loops"):
For x = 38 To 100 Step 3
For Each strng In strings
Set rFind = Range.Find...
If Not rFind Is Nothing Then
...paste special...
Else
...msgbox
End If
Next
Next
Use Exit For to exit a loop body, and Exit Sub to exit the procedure completely.
I wrote this quick For Loop as part of my macro to find cells in utwbk, based on a list of values in dewbk. The loop seems to go fine for a few cells and then crashes. The problem is, it crashes for different values each time but the values DO exist in utwbk. Here is my code:
Dim utpath As String
Dim utwbk As Workbook
Dim ogfund As String
Dim ogcell As Range
Dim newfund As String
Dim newcell As Range
Dim t As Long
For t = 2 To tempfundlastrow
If dewbk.Sheets("Macro").Cells(t, 1).Value <> "" Then
Set ogcell = dewbk.Sheets("Macro").Cells(t, 1)
ogfund = Trim(ogcell.Value)
With utwbk.Sheets("Report").Range(Cells(1, 1), Range("AAA1").SpecialCells(xlCellTypeLastCell))
Set newcell = .Find(ogfund, LookIn:=xlValues, lookat:=xlWhole)
End With
newfund = newcell.Value
newcell.Offset(2, 0).Value = ogcell.Offset(0, 8).Value
newcell.Offset(3, 0).Value = ogcell.Offset(0, 9).Value
newcell.Offset(4, 0).Value = ogcell.Offset(0, 11).Value
newcell.Offset(6, 0).Value = ogcell.Offset(0, 10).Value
Else
'nothing
End If
Next t
The code crashes with run-time error 91: 'Object variable of with block variable not set' on this line:
newfund = newcell.Value
In the previous line where I define newcell, ogfund has a value and I can find that value in utwbk so really not sure what's going on. I am assuming my syntax for the .Find is incorrect but I do not know how to rectify this. As usual, any help is greatly appreciated!
After the line
Set newcell = .Find(ogfund, LookIn:=xlValues, lookat:=xlWhole)
Type this
If newcell Is Nothing Then
MsgBox "Not found"
Exit Sub
End If
If you see the message box that means .Find couldn't find the search text and since it couldn't find, the newcell.Value will break the code as newcell is Nothing
BTW if you look for a word BLAH and in your cell you have BLAH with leading and trailing spaces then your .Find will not find the word because you are using lookat:=xlWhole. Maybe you want lookat:=xlPart
It's a common mistake. In
utwbk.Sheets("Report").Range(Cells(1, 1), Range("AAA1").SpecialCells(xlCellTypeLastCell))
Cells(1, 1) and Range("AAA1") refer to ranges in the currently active sheet
What you probably want is more like
With utwbk.Sheets("Report")
With .Range(.Cells(1, 1), .Range("AAA1").SpecialCells(xlCellTypeLastCell))
Set newcell = .Find(ogfund, LookIn:=xlValues, lookat:=xlWhole)
End With
End With
or just
With utwbk.Sheets("Report").UsedRange
Set newcell = .Find(ogfund, LookIn:=xlValues, lookat:=xlWhole)
End With
For context:
I would like for the program to look through column B, identify the first "< / >" (which is purely stylistic and can be changed if necessary - it's only used to break up the data) as the start of a week at cell B9 and the next "< / >" (end of the week) at B16. So the range I'm interested in is B10-B15. It would then sum those numbers from J10 to J15 (Earned column) and paste that sum in L16 (Week Total column). The same could then be done with 'Hours' and 'Week Hours'. For the following week (and thereafter) the 'end of the week' "< / >" becomes the start of the week, and the program continues until B200.
I don't have any experience with VBA and so made the following incomplete attempt (based on what I had found online) but felt too out of my depth not to ask for help.
Sub Work()
Dim rng As Range
Dim rngFound As Range
Set rng = Range("B:B")
Set rngFound = rng.Find("</>")
If rngFound Is "</>" Then
If Cell = "</>" Then
End If
End Sub
Thank you for any help and please let me know if I can be clearer or elaborate on something.
The following code will loop through 200 lines, looking for your symbol. When found, it will sum the numbers in column J for rows between the current row and the last symbol.
I've included two lines that will update the formula. To me, the 2nd one is easier to understand.
Sub Work()
Dim row As Integer
row = 4
Dim topRowToAdd As Integer 'Remember which row is the
'top of the next sum
topRowToAdd = 4
While row <= 200
If Cells(row, 2) = "</>" Then
'Cells(row, 10).FormulaR1C1 = "=SUM(R[" & -(row - topRowToAdd) & "]C[0]:R[" & -1 & "]C[0])"
Cells(row, 10).Value = "=SUM(J" & topRowToAdd & ":J" & row - 1 & ")"
topRowToAdd = row + 1
End If
row = row + 1
Wend
End Sub
Sub Work()
Dim rng As Range, rngFound As Range
Set rng = Range("B:B")
Set rngFound = rng.Find("</>")
If rngFound.Value2 = "</>" Then
'whatever you want to do
End If
End Sub
So at a second glance it looks like this. If you'd like to make it structured you'd need to use a countifs function first.
Sub Work()
Dim rng As Range, rngFound(1) As Range
Set rng = Range("B1:B200")
On Error GoTo Err 'it is quite possible it will run into an error if there are no matches and I'm too lazy for structured solution
Set rngFound(0) = rng.Find(What:="</>", LookAt:=xlWhole, SearchDirection:=xlNext) 'finds the first
Set rngFound(1) = rng.Find(What:="</>", LookAt:=xlWhole, SearchDirection:=xlNext, After:=rngFound(0)) 'finds the first after the first (i.e. the second)
Set rngFound(0) = rngFound(0).Offset(1, 8) '8 is the difference between B and J, row shifts as per description, I advise you to make it a variable
Set rngFound(1) = rngFound(1).Offset(-1, 8)
If rngFound(1).Row > rngFound(0).Row Then 'if it's not higher, then it recurred and found the first range again
rngFound(1).Offset(1, 2).Formula = "=SUM(" & Range(rngFound(0), rngFound(1)).Address & ")" 'L column will have the sum as a formula
Else
MsgBox "There is a single match in " & rng.Address(False, False)
End If
If False Then
Err:
MsgBox "There are no matches in " & rng.Address(False, False)
End If
End Sub
Now for the grand finale:
Sub Work()
Dim rng As Range, rngFound() As Range, rngdiff(1) As Long, rngcount As Long
Set rng = Range("B1:B200")
rngcount = rng.Cells.Count
ReDim rngFound(rngcount)
rngdiff(0) = Range("J1").Column - rng.Column ' the range that needs to be summed is in column J
rngdiff(1) = Range("L1").Column - rng.Column ' the range containing the formula is in column L
On Error GoTo Err 'it is quite possible it will run into an error if there are no matches and I'm too lazy for structured solution
Set rngFound(0) = rng.Find(What:="</>", LookAt:=xlWhole, SearchDirection:=xlNext) 'finds the first
'loop starts
For i = 1 To rngcount
Set rngFound(i) = rng.Find(What:="</>", LookAt:=xlWhole, SearchDirection:=xlNext, After:=rngFound(i - 1)) 'finds the next
If rngFound(i).Row > rngFound(i - 1).Row Then 'if it's not higher, then it recurred and found the first range again
rngFound(i).Offset(0, rngdiff(1)).Formula = "=SUM(" & Range(rngFound(i - 1).Offset(1, rngdiff(0)), rngFound(i).Offset(-1, rngdiff(0))).Address & ")" 'L column will have the sum as a formula
Else
Exit Sub 'if it recurred the deed is done
End If
Next i
If False Then
Err:
MsgBox "There are no matches in " & rng.Address(False, False)
End If
End Sub
I am trying to find the matching cell value in a named range in my excel workbook (.vba). I know the value that I am looking for, and also know the name of the range, and when I run the code the first time it runs with no problem, but on the second run with a new range name, I get an error.
I have tried a couple different ways to search the named range, and both ways result with the same error. The error is: "Method 'Range' of object '_Global' failed".
My initial code that I tried is:
'march through the list of racks
For i = iFirstRackRow To iLastRackRow
iCurrRackSize = Sheets("PLC I-O").Cells(i, 6).value
iHardwareIndexEnd = iHardwareIndex + iCurrRackSize - 1
rngCardsName = Trim(Sheets("PLC I-O").Cells(i, 2).value & "Cards")
'march through the rack hardware
For j = iHardwareIndex To iHardwareIndexEnd
modCardSize = 0
'march through each card in the rack
For Each zCell In Range(rngCardsName)
If zCell = Sheets("PLC I-O").Cells(j, 2) Then
modCardSize = Sheets("Links").Cells(zCell.Row, zCell.Column + 1).value
Exit For
End If
Next zCell
If modCardSize <> 0 Then
'io module matched
NumRows = NumRows + modCardSize
Else
'processor or adapter module found
NumRows = NumRows + 1
End If
Next
iHardwareIndex = iHardwareIndex + iCurrRackSize
Next
Or I have also tried:
Dim rngFoundCell As Range
With Range(rngCardsName)
Set rngFoundCell = .Find(What:=Sheets("PLC I-O").Cells(j, 2).value, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not rngFoundCell Is Nothing Then
'cell match was found
rngrow = rngFoundCell.Row
rngcol = rngFoundCell.Column
modCardSize = Sheets("Links").Cells(rngrow, rngcol + 1).value
Else
'cell match was not found
End If
End With
I am not sure what I am doing wrong here. Please help.
I think that the problem is the After:= parameter of your .Find . Try to change the parameter with the value After:=.Cells(1).
Thanks everyone! I found this issue. It was the Trim instruction. Since there were multiple spaces in the Sheets("PLC I-O").Cells(i,2).value, I needed to use a custom function to make sure all spaces were removed.
I am trying to figure out a way to run a Vlookup on a Cell in my "System File" by checking a table in a "New Data" File. HOWEVER, if there is an #N/A error, I want the cells' values to be unchanged. I've come up with the following, however, I keep getting a "Next without For" error. Is it possible to escape a nested For Next loop?
The tl;dr semantic version:
For i 1 to 10
For j 1 to 3
Something with .Cells(i,j)
Set range X = .Find(thing
If X = Nothing Then
Next j *** <THIS IS WHERE MY ERROR IS THROWN
Else
-Do Something with X-
End if
Next j
Next i
My more or less actual code is as follows:
Sub Thing()
Dim SysWS As Worksheet
Dim NewDataWS As Worksheet
Dim NDSKUs As Range ' This is set to the first column of the NewDataWS
Dim NDMonthsRow As Range ' This is set to the first row of the NewDataWS
Dim SKU2look4 As String, Month2look4 As String
Dim ifoundtheSKU As Range 'the result of finding SKU2look4 inside of NDSKUs range
Dim ifoundtheDate As Range 'the result of finding Month2look4 inside of NDMonthsRow range
Dim i As Integer, j As Integer
Dim workzone As Range 'The Cell being evaluated
For i = 2 To SysWS.UsedRange.Columns.Count
For j = 2 To SysWS.UsedRange.Rows.Count
Set workzone = SysWS.Cells(j, i)
SKU2look4 = SysWS.Cells(j, 1) 'SKUs are along the left column
Month2look4 = SysWS.Cells(1, i) 'Dates are along the top row
'1-Find the right Date Column for extraction
Set ifoundtheDate = NDMonthsRow.Find(What:=Month2look4, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If ifoundtheDate Is Nothing Then
Debug.Print (Month2look4 & " -Date NOT Found in New Date File")
******Next j******
Else
Debug.Print ("ifoundtheDate:" & ifoundtheDate.Address)
End If
'2-Find the row
Set ifoundtheSKU = NDSKUs.Find(What:=SKU2look4, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If ifoundtheSKU Is Nothing Then
Debug.Print (SKU2look4 & " Not Found in the New Data File")
*********Next j******
Else
Debug.Print ("ifoundtheSKU:" & ifoundtheSKU.Address)
End If
'Set the "workzone" cell's value to that of the found row offset by the found column
workzone = ifoundtheSKU.Offset(, (ifoundtheDate.Column - 1))
Next j
Next i
Of course the ***s are not actually in there. Any thoughts on how I can accomplish this?
Thanks in advance
For i = 1 to 10
For j = 1 to 3
Something with .Cells(i,j)
Set rngX = .Find(thing)
If Not rngX Is Nothing Then
Set rngY = .Find(thingelse)
If Not rngY Is Nothing Then
'something with rngX and rngY
End If
End if
Next j
Next i
Use
For i=1 to 10
For j=1 to 3
Something with .Cells(i,j)
Set range X = .Find(thing
If X = Nothing Then
Goto Nextj *** <THIS IS WHERE MY ERROR IS THROWN
Else
-Do Something with X-
End if
NextJ:
Next j
Next i
Exit For terminates the current for loop early (the inner one in your case).