I am trying to search an entire row for a string that contains "PROFILE". It will always be capitalized, but the format will be, for example "[9]PROFILE001".
Some extra information: I have used the Find command to locate the row that I am searching in for the string. It has its own variable that I am trying to incorporate into the range I am using.
I have searched multiple partial string articles on here, and have not been able to implement it into my code. I have tried using the Like command and the IntStr command to no avail. I believe my issue may be with how im referencing the search range or how i am searching.
Here is a snippet of my current code:
'finding item name row
Set FindRow3 = Range("A1:A100").Find("Item Name", LookIn:=xlValues)
itemnamerow = FindRow3.Row
'The section above is working as intended
'searching for the word profile, the section below is the one I am having issues with
Range("B8:Z100").Style = "Normal"
If ActiveSheet.Range("B" & itemnamerow & ":Z" & itemnamerow) Like "*PROFILE" Then
Range("C1").Value = "it worked"
End If
I am currently experiencing a run time error 13, type mismatch in the "If ActiveSheet..." line. I have not been able to get the correct index to make this correct.
I am trying to use that if the partial string is found, I want do something.
TIA
You need to use the Find method, with MatchCase and LookIn arguments set. And probably LookAt for to ensure it checks actual values and not formula syntax.
Dim profileFound as Range
Set profileFound = ActiveSheet.Range("B" & itemnamerow & ":Z" & itemnamerow).Find("PROFILE",lookIn:=xlValues,MatchCase:=True,lookAt:=xlPart)
If Not profileFound is nothing Then
debug.print profileFound.Value
Range("C1").Value = "it worked"
else
debug.print "no profile found"
End If
The reason your original code is failing is because Excel will not allow you to evaluate a multi-cell range against a single value. You could loop through each cell in the range and check each cell individually, but since Find is available, that is superfluous.
You don't provide enough parameters for the Range.Find operation. Switch to the worksheet's Match to locate Item name then again as a wildcard search to locate profile.
dim m as variant, n as variant
m = application.match("Item Name", range("A1:A100"), 0)
If not iserror(m) then
n = application.match("*profile*", cells(m, "B").resize(1, 25), 0)
If not iserror(n) then
Range("C1").Value = "it worked " & cells(m, n+1).value
end if
end if
Related
I am new at using VBA and I am trying to do something that seems "simple." I have my VBA code generate a string (CP20210100001) and I want my for loop to check if that string has already been used in that column. If already used, generate the next in the serial until the next unique value in the serial has been generated.
My boss wants to paste a different ID occasionally in the column and this disturbs the code. My code looks at the last row and adds one to the String + serial. This will result in duplicates.
I figured out through much googling to get the code to check the current value for duplicates but I can't figure out how to get it to check for future IDs in the series until it comes across a unique value.
Below you can see my column. I had 10 successful submission and then my boss pasted 3 rows. With my VBA the next generated ID would be CP20210200004 but last part of the code found it as duplicate so it added 1 and inputted CP20210200005. Ideally the VBA should for loop until the next in the serial shows up. In this case CP20210200011. This way no matter how many times my boss disrupts my table, my ID sequence stays in tact.
**Reference ID**
CP20210100000
CP20210200001
CP20210200002
CP20210200003
CP20210200004
CP20210200005
CP20210200006
CP20210200007
CP20210200008
CP20210200009
CP20210200010
JS20210200001
JS20210200002
JS20210200003
CP20210200005
Below is the the VBA
#Timestamp is part of the String + Serial Combo
Timestamp = Format(Year(Date)) + Format(Month(Date), "00")
#I found this online. Essentially if A2 is blank then input CP + Timestamp + 00001 (CP20210100001)
#It looks at the last row to find the old value (OVAL) and generate the new value (NVAL)
If Sheets(ws_output).Range("A2") = "" Then
Sheets(ws_output).Range("A2").Value = "CP" & Timestamp + 1
Else
lstrow = Sheets(ws_output).Cells(Rows.Count, "A").End(xlUp).Row
Oval = Sheets(ws_output).Range("A" & lstrow)
NVAL = "CP" & Timestamp & Format(Right(Oval, 4) + 1, "00000")
#Here I am trying to see if NVAL is a duplicate value. If so add one to the serial.
Count = Application.WorksheetFunction.Countif(Sheets(ws_output).Range("A2:A100000"), NVAL)
Dim Cell As Range
For Each Cell In Sheets(ws_output).Range("A2:A100000")
If Count > 1 Then
NXVAL = NVAL
Else
NXVAL = "CP" & Timestamp & Format(Right(NVAL, 4) + 1, "00000")
End If
Next
Please please please help.
EDIT
I Should clarify that all of this is triggered on a form. The module is connected to a submit button. Once the button is pressed all the values in the form write to a separate sheet. Reference ID is the only part that isn't on the form. Essentially once the button is pressed, it triggers the query to write the next available reference ID. The next line in the query is
Sheets("Sheet2").Cells(next_row, 1).Value = NXVAL
I need the new Reference ID to equal a variable.
Your code seems to give you much grief and little comfort. The reason is that you didn't take a strictly logical approach. The tasks are ...
Find the last used number. I suggest to use VBA's own Find function.
Insert the next number. It consists of prefix, Date and serial number.
So, you arrive at code like this:-
Sub STO_66112119()
' 168
Const NumClm As Long = 1 ' 1 = column A
Dim Prefix As String
Dim LastNumber As Long
Dim Fnd As Range ' search result
Prefix = "JS" ' you could get this from an InputBox to
' enable numbering for other prefixes
With Columns(NumClm)
On Error Resume Next ' if column A is blank
Set Fnd = .Find(What:=Prefix, _
After:=.Cells(1, 1), _
LookIn:=xlValues, _
Lookat:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False)
End With
LastNumber = Val(Right(Fnd.Value, 5))
On Error GoTo 0
Cells(Rows.Count, NumClm).End(xlUp).Offset(1).Value = Prefix & Format(Date, "yyyymm") _
& Format(LastNumber + 1, "00000")
End Sub
You need to spend a moment on preparation, however.
Define the column to work in. I put this in the Const NumClm. It's at the top of the code so as to make maintenance easier (won't need to dig in the code to make a change).
My code shows Prefix = "JS". You want to change this to "CP". I inserted "JS" to show that you could use any prefix.
The above code will continue counting up in a new month and even a new year. If you want to start each year with a new series just change the way you handle the found previous. The Find function will return the cell where the prefix was last used. You might further examine that cell's value.
I've created a VBA Macro to look at a string of input text in the cell E3, split it into individual characters and then VLookup those characters against a table of individual character pixel widths, which is added to the code using a named range, "pw_Table".
The pixel-widths for each letter are then summed and displayed in a cell below the text input box - "Cells(4,5)". Hitting return is meant to show the combined pixel-width total for the complete string.
The problem is that it is not being case sensitive and is using the same VLookup value for both upper and lower case characters.
All the manuals I've seen say VBA is case sensitive on VLookup, and all I can find are ways to get around this.
For my issue, however, the VLookup must be case sensitive to make sure I get the correct pixel width for each letter, for example, "c" is 9 pixels wide, "C" is 13.
I have tried reordering the upper and lower case characters in the table to see if that made a difference, but it only uses the first values it encounters for each letter of the alphabet, whether they be upper- or lower-case.
I thought that I might use INDEX, MATCH, and EXACT, but couldn't see how to implement that in VBA.
This is the Macro code ...
Private Sub ReadCharacter()
cell_value = ThisWorkbook.Sheets("Pixel-widths").Range("E3")
Character_Value = 0
For rep = 1 To Len(cell_value)
Character = Mid(cell_value, rep, 1)
On Error GoTo MyErrorHandler
Character_Value = Application.WorksheetFunction.VLookup(Character, [pw_Table], 2, 0)
Pixel_Width = Pixel_Width + Character_Value
MyErrorHandler:
Character_Value = 10
Resume Next
Next rep
Cells(4, 5) = Pixel_Width
End Sub
I had some issues with numbers, with VBA reporting Run-time Error 1004, but I bodged this by adding an error trap because all the numerals from 0-9 are 10 pixels wide.
I simply can't see why VBA is breaking its own rules.
Vlookup isnt case sensitive.
ive found this function that "simulates" a vlookup case sensitive.
Function CaseVLook(FindValue, TableArray As Range, Optional ColumnID As Integer = 1) As Variant
Dim xCell As Range
Application.Volatile
CaseVLook = "Not Found"
For Each xCell In TableArray.Columns(1).Cells
If xCell = FindValue Then
CaseVLook = xCell.Offset(0, ColumnID - 1)
Exit For
End If
Next
End Function
to use it just call it CaseVLook(F1,A1:C7,3)
more information in here
https://www.extendoffice.com/documents/excel/3449-excel-vlookup-case-sensitive-insensitive.html
good luck
Here's another way...
Character_Value = Evaluate("INDEX(" & Range("pw_Table").Address(, , , True) & _
",MATCH(TRUE,EXACT(INDEX(" & Range("pw_Table").Address(, , , True) & ",0,1),""" & Character & """),0),2)")
Hope this helps!
I need to find and replace hundreds of misnamed field names (cell names) in a large excel financial model. I'm trying to build this macro subroutine to find a given field name and replace it with the correct field name.
Sub FindReplaceFieldName()
Dim orgFieldName As String
Dim replFieldName As String
orgFieldName = "CAN"
replFieldName = "Canada"
Application.Goto Reference:=orgFieldName
With ActiveWorkbook.names(orgFieldName)
.Name = replFieldName
.RefersToR1C1 = "=Sheet1!(" & activeCell.row & ";" & activeCell.Column &")".Comment = ""
End With
ActiveWorkbook.Save
End Sub
The field name is found and replaced, but a Runtime error 1004 is thrown here
"=Sheet1!(" & activeCell.row & ";" & activeCell.Column &
")"
"The formula you typed contains an error." and so on.
I'm not familiar with VBA syntax, so a 2nd pair of experienced eyes would be helpful.
SOLVED: The correct syntax should be
.RefersToR1C1 = "=Sheet1!R" & activeCell.row & "C" & activeCell.Column & ""
Forgive me if wrong, but are you not approaching this sideways? You want to change the existing names not amend the locations; Ergo, use a mapping to rename the existing.
For example, use a dictionary to rename (you could use other structures); I wanted to leverage the .Exists of a dictionary so only attempted valid substitutions. You could even loop a range in the sheet to populate your dictionary. Or read the range straight into an array and dump the array into the dictionary as key/values.
Code:
Option Explicit
Public Sub RenameNamedRanges()
Dim currName As Name
Dim replaceDict As Object
Set replaceDict = CreateObject("Scripting.Dictionary")
replaceDict.Add "CAN", "Canada"
replaceDict.Add "FR", "France"
replaceDict.Add "DE", "Deutschland"
For Each currName In ThisWorkbook.Names
If replaceDict.Exists(currName.Name) Then
currName.Name = replaceDict(currName.Name)
End If
Next currName
End Sub
Before:
After:
To troubleshoot issues like this, where you're building a string to be used elsewhere, troubleshoot by looking at the problem string just before the error is caused.
In this case, you could add a line just before the line where you get the error:
Debug.Print "=Sheet1!(" & activeCell.row & ";" & activeCell.Column & ")"
...then, when you run your code and get the error, go to the Immediate Window (Ctrl+G) and see what Excel thinks you mean.
Are you able to see your error now?
That being said, you must have posted your code incorrectly, since I can't get it to run at all (to get an Error 1004) since this line is wonky:
.RefersToR1C1 = "=Sheet1!(" & activeCell.row & ";" & activeCell.Column &")".Comment = ""
If I replace the row and column numbers you're trying to insert with 1234 then it would read:
.RefersToR1C1 = "=Sheet1!(1234;1234)".Comment = ""
I can't give an absolute solution without knowing more about what you're trying to do, but obviously that is an invalid command (and likely not what you intended).
Note that ActiveCell.Row and ActiveCell.Column both return numbers, and that Sheet1!(1,1) is not how we refer to a cell in Excel.
I want to be able to compare 2 cells in VBA. The issue i'm having is that it's not making an exact match.
Dim WeekNum2 As Integer
WeekNum2 = Cells(1, 12).Value
Range("F2:F60").Find(WeekNum2).Activate
U = ActiveCell.Row
Range(Cells(U, 7), Cells(U, 7)).Value = GreenCountP4
Range(Cells(U, 8), Cells(U, 8)).Value = YellowCountP4
Range(Cells(U, 9), Cells(U, 9)).Value = RedCountP4
Above is the section of code that isn't working ideally. The value in Cells(1,12) is generated using the =WEEKNUM(K1) formula so depending on the date in K1 it returns a value from 1-52. I want it to take this value and then find the equivalent value in the range F2:F60.
In F2:F60 i have values going in order from "w46"-"w52" and then "w1"-"w52". The issue is that if the value in Cells(1, 12) is 5 for example, it will select the first row that has a 5 in it in the range (w50 in this case).
Is it possible to compare just the numbers in the cell (so not include the "w" with it still being present). If not, how do i make it so it picks up the exact values (So if the value in Cells(1, 12) is 5, then it goes to right 5 instead of the first 5 in the range)
use xlWhole value for lookat argument of Find() method:
Range("F2:F60").Find("W" & WeekNum2, lookat:=xlWhole, LookIn:=xlValues).Activate
U = ActiveCell.Row
furthermore you can avoid Select and directly go like follows:
U = Range("F2:F60").Find("W" & WeekNum2, lookat:=xlWhole, LookIn:=xlValues).Row
I suggest you use an optional argument to the find method, which allows you to insist that the entire cell is matched, and not just some subset of the text inside. You want to use the LookAt argument, and set it to xlWhole. Now nothing will be found for "5", because no cell will match "5" entirely. Cells containing "w5" aren't a match with LookAt:=xlWhole. I know, the naming of arguments is not at all intuitive...
Furthermore, life would be much easier if you simply used a string "w5" in your search. An alternative would be to strip the w's out of the cells in the find (search) range. This can be done with the split method.
You can "cheat" a little, make the value of 5 as "W5" and then find a match for that in your range. Just add another variable, let's call it WeekNum2String and add the "W" as a prefix. Now you can search your range with Range("F2:F60").Find(WeekNum2String).
Dim WeekNum2String As String
WeekNum2String = "W" & Cells(1, 12).Value
Note: you should use the Find method by defining a Range, then setting it to the Find result (and not with Activate) .This method will allow you to trap errors if there is no Find.
Like this:
Dim FindRng As Range
Set FindRng = Range("F2:F60").Find(WeekNum2String)
If Not FindRng Is Nothing Then
U = FindRng.Row
Else
MsgBox "Week number " & Cells(1, 12).Value & " not found in Range"
End If
I am a quite new to Excel VBA, and I come from a more... traditional programming background (Java, C). I am having issues with passing a Range Object as a parameter in my User-defined function (see below). The idea of my function is to take several parameters to complete a VLOOKUP of a filtered range.
I may have several syntax issues (I am unsure of my return type and my usage of VLOOKUP), and I would appreciate some guidance on this. See results, more information in my code:
Public Function GETVALUE(screen As String, strEvent As String, dataRange As Range, strDate As String) As String
'ASSUMPTION: dataRange has three columns; first column contains lookup values; Second
' column contains dates for filtering; Third column contains return values
Dim result As String
'remove irrelevant dates in dataRange; apply filter
'ASSUMPTION: This process should return a Range that is removes all Rows that does
'not have strDate in the second column
Dim newRange As Range
'RESULT: Returns #VALUE!. I know this is not the typical := syntax I see in many
'examples but this one apparently compiles, so I use it. I comment this line out
'and try to make the other lines below work with dummy parameters or fixed ranges
newRange = dataRange.AutoFilter(2, strDate)
'Now I try to use the newly filtered, "newRange" and use that in my VLOOKUP
'and return it.
result = [VLOOKUP("*" & screen & "/" & strEvent & "*", newRange, 3, False)]
'I can see an Error 2029 here on Result
GETVALUE = result
'RESULT: Returns #VALUE!
End Function
VLOOKUP ignores any filtering of your data. In other words VLOOKUP will also look in the hidden rows.
I would suggest two alternative approaches:
Copy the visible cells of the filtered range to a new sheet and perform the lookup there:
Set newRange = dataRange.AutoFilter(2, strDate).SpecialCells(xlCellTypeVisible)
set ws = worksheets.Add
ws.Range("A1").Resize(newRange.Rows.Count,newRange.Columns.Count).Value = newRange.Value
etc.
Note that this can not be done in a UDF, you would have to do it in a a Sub.
Store the values in dataRange in a variant array and loop to search for the required value:
Dim arr() as Variant
arr = dataRange.Value
For i = LBound(arr,1) to UBound(arr,1)
If (arr(i,2) = strDate) And (arr(i,1) LIKE "*" & screen & "/" & strEvent & "*"( Then
GETVALUE = arr(i,3)
Exit Function
End If
Next
This I think causes your problem:
result = [VLOOKUP("*" & screen & "/" & strEvent & "*", newRange, 3, False)]
Replace it with this instead:
result = Evaluate("VLOOKUP(*" & screen & "/" & strEvent _
& "*, " & newRange.Address & ", 3, False)")
[] which is shortcut for Evaluate doesn't work on variables.
If it is a direct VLOOKUP like below:
result = [VLOOKUP(D1,Sheet1!$A:$C,3,FALSE)]
it will work. But if you are working with variables as in your example, you have to explicitly state it.
And take note that Evaluate accepts Name argument in a form of string.
So you simply have to concatenate all your strings and then explicitly use Evaluate.
Edit1: Additional Inputs
This will not work as well: newRange = dataRange.AutoFilter(2, strDate).
To pass Objects to a Variable you need to use Set like this.
Set newrange = dataRange.AutoFilter(2, strDate)
On the other hand, AutoFilter method although returning a Range Object fails.
I'm not entirely sure if this can't really be done.
Moving forward, to make your code work, I guess you have to write it this way:
Edit2: Function procedures only returns values, not execute methods
Public Function GETVALUE(screen As String, strEvent As String, rng As Range)
GETVALUE = Evaluate("VLOOKUP(*" & screen & "/" & strEvent & "*, " _
& rng.Address & ", 3, False)")
End Function
To get what you want, use above function in a Sub Procedure.
Sub Test()
Dim dataRange As Range, strDate As String, myresult As String
Set dataRange = Sheet2.Range("A2:E65") 'Assuming Sheet2 as property name.
strDate = "WhateverDateString"
dataRange.AutoFilter 2, strDate
myresult = GETVALUE("String1", "String2", dataRange)
End Sub
Btw, for a faster and less complex way of doing this, try what Portland posted.
Basically you must write :
Getvalue = Application.VLookup( StringVar, RangeVar, ColumnNumberVar)
Vlookup needs your data to be previously ordered in alphabetical order, or it doesn't work.
Excel Developers's approach is a good one too, using a VBA Array.
I can also point the VBA functions FIND, and MATCH, wich will get you the row of searched data, and then you can pull what you need from the 3rd column of that row.
Wichever is faster depends of the size of your range.