I have a set of data (webmaster tools search queries) which is in excel with the following header:
Query | Impressions | Clicks | Date
Sample google spreadsheet here.
I want to add in an extra column called Category and categorize all the queries according to custom rules that will search for a string on column A.
Ex:
if A2 contains the string 'laptop' then write 'laptop' on the category next to it
So far I have tried a formula to do this but I'm not sure this is the easiest way. Also, if there are lots of categorization rules the formula gets really long and unmanageable.
=IF(ISNUMBER(SEARCH("laptop",A2)),"laptop",
IF(ISNUMBER(SEARCH("notebook",A2)),"laptop",
IF(ISNUMBER(SEARCH("iphone",A2)),"phone",
IF(ISNUMBER(SEARCH("galaxy s",A2)),"phone",
"other")))
Can you suggest a better way of doing this where I can have the rules in one sheet in this format:
Query_contains | Category_is
where Query_contains would be the string that needs to be matched in column A from the initial sheet the and Category would be the value that needs to be filled into column D.
Ok, I changed your sheet a bit....
Supposing all your data was in cells A1:C9, then you had the following table in cells F1:G5
Search_For: Category:
laptop Laptop
iphone Phone
galaxy Phone
notebook Laptop
Now, in cell D2, put in the following formula:
=IFERROR(INDEX(G$2:G$5,MATCH(TRUE,ISNUMBER(SEARCH(F$2:F$5,A2)),0)),"other")
And enter it as an array formula Meaning, once you enter it, hit CTRL+SHIFT+ENTER.
You can then drag the formula from cell D2 down and it should give you the desired results (and you can, of course, increase the list in columns F & G as necessary).
Hope this does the trick!!
This small macro assumes your data is in Sheet1 and your rules are in worksheet rules in columns A & B:
Sub catagorize()
Dim s1 As Worksheet, s2 As Worksheet
Dim N1 As Long, N2 As Long
Set s1 = Sheets("Sheet1")
Set s2 = Sheets("rules")
N1 = s1.Cells(Rows.Count, "A").End(xlUp).Row
N2 = s2.Cells(Rows.Count, "A").End(xlUp).Row
For i = 2 To N1
v = s1.Cells(i, 1).Value
For j = 1 To N2
If InStr(1, v, s2.Cells(j, 1).Value) > 0 Then
s1.Cells(i, "D").Value = s2.Cells(j, "B").Value
End If
Next j
Next i
End Sub
And for a 3rd option, you could use a custom formula.
I created a table just for categories on a separate sheet, then inserted the following code in a standard module.
Option Explicit
Function CategoryLookup(s_Query As String, Keyword_tbl As Range)
Dim rngKeywords As Range
Dim s_foundCategory As String
Dim b_CategoryExists As Boolean
b_CategoryExists = False
For Each rngKeywords In Keyword_tbl
If InStr(s_Query, rngKeywords.Value) <> 0 Then
s_foundCategory = rngKeywords.Offset(0, 1).Value
b_CategoryExists = True
Exit For
End If
Next rngKeywords
If b_CategoryExists = True Then
CategoryLookup = s_foundCategory
Else
CategoryLookup = "Other"
End If
End Function
Then in D2 (your category column) insert the following formula (which can then be dragged down)
=CategoryLookup(A2,categories!$A$2:$A$5)
Related
So I have this large excel file, over 7000 rows, and what I need to do is pick the content from a cell in a column, for example A2, and extract its content onto A3
The thing is that the content are abbreviatures, like PRD for period, or CLS for class, all abbreviature are separated by underscores with no particular order, so for example A2 can just say PRD but B2 would say PRD_CLS_PPRD_ADVAN and then back to CLS_ADV on C2
What I need is to extract the content from each cell an put it on another cell, the abbreviatures must be replaced by it's original word so instead of PRD it should say Period, or Class instead of CLS, when there's an underscore it should be replaced for a slash. So overall when B2 says PRD_CLS_PPRD_ADVAN then B3 should say Period/Class/Pre-Production/Advance
I've given it a lot of tries to solve this, using LEFT, RIGHT, EXTRACT, but to no avail, would appreciate any suggestion on how to solve this
So, slightly different approach using find():
Formula for C2:
IFERROR(IF(FIND($E$2,$A2,1)>0,VLOOKUP($E$2,$E$2:$F$5,2,0),"")&"/","")&IFERROR(IF(FIND($E$3,$A2,1)>0,VLOOKUP($E$3,$E$2:$F$5,2,0),"")&"/","")&IFERROR(IF(FIND($E$4,$A2,1)>0,VLOOKUP($E$4,$E$2:$F$5,2,0),"")&"/","")&IFERROR(IF(FIND($E$5,$A2,1)>0,VLOOKUP($E$5,$E$2:$F$5,2,0),""),"")
Let you think about the trailing "/"...
'something like that should get you started
Public Sub convertCell()
Dim rowmax As Integer
Dim i As Integer
Dim j As Integer
Dim StrArray() As String
Dim wrds As Integer
'replace sheet1 by the name of you sheets
rowmax = Worksheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
For i = 1 To rowmax
StrArray = Split(Worksheets("Sheet1").Cells(i, 1), "_")
wrds = UBound(StrArray)
For j = 0 To wrds
Worksheets("Sheet1").Cells(i, 2) = Worksheets("Sheet1").Cells(i, 2) & "/" & StrArray(j)
Next j
Next i
End Sub
So basic method using formulae:
Formulae in C2:
VLOOKUP(LEFT(A2,FIND("_",A2,1)-1),$E$2:$F$5,2,0)
Cells E2 to F5 hold the abbreviations, extend the range as needed.
So, update, been playing.
Just done the first two, use mid() for number 3 and right for number 4.
I'm creating a validation spreadsheet, where the user will input data before it gets imported into our company database.
I had it all finished, but then we realised there was one remaining hole, which I am struggling to plug.
For my test to see if I can get something to do what I want, the user enters data in columns A and B, starting at row 2. Column A is mandatory, column B is a mix - for majority of entries in A then B is optional, but for certain entries in A then B is required AND is required in a predefined format based on A.
eg.
user enters "12345678" in A2, and "12345678" is nothing special so an entry in B2 is optional
user enters "11111111" in A3, and "11111111" is special, so they do need to put an entry in B3, and it is required in the format ab12cde (??##???)
So far I have a small table in range N2:O6 to use for a Vlookup - N is the A entry (11111111, 22222222, etc) and B is the mask required (??##???, ##?#?, etc)
I know I will need to loop the code eventually, but I removed that as want to get 1 run-through done first.
Dim b As String
Dim suf As String
Dim zzz As Integer
Last_Row_Suf = Sheets("Sheet1").Cells(Rows.Count, "A").End(xlUp).Row
Debug.Print Last_Row_Suf
zzz = 2
If zzz <= Last_Row_Suf Then
suf = "test"
suf = Application.VLookup(Range("A" & zzz), Range("N2:O6"), 2, False)
b = Range("B" & zzz).Value
If suf Like b Then
Range("D" & zzz).Value = 1
Else
Range("D" & zzz).Value = 0
End If
zzz = zzz + 1
Else
End If
Debug.Print suf
Debug.Print b
The issue I am facing is that Like is returning False when I try to compare for example xy45trn and ab12cde
I also need to put in the earlier steps to check if A2 is in the mandatory table or not as if it isn't then I will just skip as I don't care what is in B2 then, but suf doesn't get updated when the Vlookup fails.
Probably lots of issues, but hopefully someone can point me in the right direction.
Thanks
Something like this::
Sub Tester()
Dim c As Range, m, v
Dim wsData As Worksheet, wsLookup As Worksheet
Set wsData = ThisWorkbook.Sheets("Sheet1")
Set wsLookup = ThisWorkbook.Sheets("Config")
'loop over the input in ColA
For Each c In wsData.Range(wsData.Range("A2"), _
wsData.Cells(Rows.Count, 1).End(xlUp)).Cells
'lookup table is on a separate sheet
m = Application.VLookup(c.Value, wsLookup.Range("A2:B20"), 2, False)
If Not IsError(m) Then
'got a hit from the lookup table
v = Trim(c.Offset(0, 1).Value)
'using Like
c.Offset(0, 3).Value = IIf(v Like m, "OK", "Error")
'using RegExp
'c.Offset(0, 3).Value = IIF(MatchesPattern(v, m), "OK", "Error")
End If
Next c
End Sub
If Like isn't meeting your needs then you can use a regexp object to perform the vbalidation: a bit more complex in terms of pattern, but much more powerful.
Function MatchesPattern(v, patt As String) As Boolean
Static reg As Object
If reg Is Nothing Then Set reg = CreateObject("VBScript.RegExp")
reg.Pattern = patt
MatchesPattern = reg.Test(v)
End Function
RegExp reference: https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/scripting-articles/ms974570(v=msdn.10)?redirectedfrom=MSDN
I have a spreadsheet in which there are multiple rows that have three columns (K, L M) that contain text (inserted manually from a dropdown). The inserted text includes a 'score'. For the row shown in the image that score is 3 + 2 + 2 = 7.
What I'd like to be able to do is to have that score automatically calculated and shown in column N. I'm happy to do the score extraction given the text, but I'm completey unfamiliar with Excel's object model, and how to write a VBA macro that can be triggered across all of the rows. I assume it would be passed a range somehow, or a string designating a range, but how to do that is beyond me just now. Perhaps I just need a formula? But one that calls a function to strip non-numerical data from the cell?
Any help appreciated.
Put this formula in N2 cell and drag it all the way down.
=LEFT(K2, FIND("-", K2) - 2) + LEFT(L2, FIND("-", L2) - 2) + LEFT(M2, FIND("-", M2) - 2)
For more information see reference. It sum all numbers, that are present before the hyphen (-) in a cell.
Try:
N2 = LEFT(TRIM(K2),1) + LEFT(TRIM(L2),1) + LEFT(TRIM(M2),1)
As I said in comments, this solution does not scale so well if it is more than three columns and / or the scores are more than single digit [0-9]
A VBA solution to do all of your rows and enter the values into Column N:
Sub foo()
Dim ws As Worksheet: Set ws = Sheets("Sheet1")
'declare and set your worksheet, amend as required
LastRow = ws.Cells(ws.Rows.Count, "K").End(xlUp).Row
'get the last row with data on Column A
For rownumber = 1 To LastRow 'loop through rows
For i = 11 To 13 'loop through columns
strValue = ws.Cells(rownumber, i).Value 'get text string from cell
pos = InStr(strValue, " -") 'find the dash - in cell
If pos > 0 Then 'if dash found
Value = Value + Val(Left(ws.Cells(rownumber, i).Value, pos - 1)) 'remove everything after number
End If
Next i
ws.Cells(rownumber, 14).Value = Value 'write value to column N
Value = 0
Next rownumber
End Sub
Without using VBA (I can do it in VBA, but just want to try whether a macro can do it as well, but I haven't figured it out yet),
I have two sheets. Sheet A includes a column of names such as its cell like:
Wright
Sheet B includes a column of names well, but with more letters like title in one cell such as:
Mr. Wright
Sheet A to B is in a relationship of one-to-many (Wright in Sheet A might have multiple rows with Mr.Wright in Sheet B).
If in Sheet B, how to write a macro with some function to achieve: to check whether 'Mr.Wright' has a substring in a cell in Sheet A.
(I think about it might be easier to start from Sheet A: might with regex, find all matches in Sheet B with INDEX or MATCH first. It's much better if it can be done from Sheet B in one shot)
Create a macro called sub_in_name.
Option Explicit
Sub sub_in_name()
Dim x, i As Long
Dim endofcells1, endofcellsmany As Long
endofcells1 = WorksheetFunction.CountA(Range("A:A"))
endofcellsmany = WorksheetFunction.CountA(Range("B:B"))
For x = 1 To endofcells1
For i = 1 To endofcellsmany
If (InStr(1, Cells(i, 2), Cells(x, 1), vbTextCompare)) Then
Cells(i, 2 + x).Value = "True"
Else
Cells(i, 2 + x).Value = "False"
End If
Next i
Next x
End Sub
Intr(start, SearchStr, SearchInStr, vbaoption) is the main function to make this work. Cells(i, 2 + x) is indexed based off the number of non-empty cells in column "A"
Make sure to clear the cells content for each trial; after column "B".
For example put in Column "A" & Column "B" and you will get columns "C:D"
Column "A" Column "B" Column "C" Column "D"
Wright Mr. Wright True False
Roger Wright Jr. True False
Wright the Ivth. True False
Sally False False
Roughly similar logic. Originally I wanted to use existing macro functions to do it. Finally it ended up like making a customized function like below:
Inspired by another thread in stack overflow. I made a code to do full checking like user3553260's. But I think a function is not a bad choice as well, considering if the efficiency is not the top one concern here.
Function LookupName(lookupValue As Variant, lookupRange As Range) As String
Dim r As Long
Dim c As Long
Dim s As String
s = "No"
For r = 1 To lookupRange.Rows.Count
For c = 1 To lookupRange.Columns.Count
If Not IsEmpty(lookupRange.Cells(r, c).Value) Then
If InStr(LCase(lookupValue), LCase(lookupRange.Cells(r, c).Value)) Then
s = "Yes"
Exit For
End If
End If
Next
Next
LookupName = s
End Function
I'm working on problem that necessitates the use of nested formulas in excel. For eg:
I have a column for errors and one for its analysis
Error Analysis
Enter a valid material number Invalid Material
Eg errors:
Enter a valid material number; The material number 1234 does not
exist.
PO number XYZ does not exist.
VIN number 123 does not exist.
Country of origin AB does not exist.
I have a compendium of such errors and their analyis in the next sheet, and I'm using VLOOKUP in conjuction with FIND to lookup the analysis for the known errors.
=VLOOKUP(LEFT(F2, FIND(" ", F2, FIND(" ", F2) + 1) - 1)&"*", 'Sheet2'!A:B, 2, 0)
What i'm trying to do here is extract the first two words from the error and append a * to it and use it in VLOOKUP.
It would be something like Vlookup "PO number *" in the other sheet and get the analysis for it. Asterisk is because I don 't get the same number daily. And I also know that the extracted first two words of the error will be unique. (I know that error with "Enter a" as the first two words will not appear again).
Now I get errors in the same column so I thought of making a button and writing a code which uses the above formula.
I tried to modify some code off the net, but I'm not getting anywhere with it. I'm totally new to VBA. It'd be great if you can provide a snippet for this. I'll try to replicate the procedure for other needs.
This code seems to be working for now
Sub PopulateAnalysis()
Dim an_row As Long
Dim an_clm As Long
Dim lft As String
Dim st_num As Integer
Dim fin As String
Dim searchStr As String
Dim soughtStr As String
Table1 = Sheet1.Range("F2:F6") 'ErrorColumn from Error table (How do I make the range dynamic??)
Table2 = Sheet5.Range("A1:B6")
an_row = Sheet1.Range("G2").Row ' Populate this column from the analysis table on sheet2
an_clm = Sheet1.Range("G2").Column
For Each cl In Table1
'How do I translate the above formula into VBA statements??
st_num = InStr(InStr(cl, " ") + 1, cl, " ")
lft = left(cl, st_num - 1)
fin = lft & "*"
Sheet1.Cells(an_row, an_clm) = Application.WorksheetFunction.VLookup(fin, Table2, 2, True)
an_row = an_row + 1
Next cl
MsgBox "Done"
End Sub
This should work. You don't need the debug lines of course ;)
Sub PopulateAnalysis()
Dim rngTableWithErrors As Range
Dim rngTableWithAnalysis As Range
Application.ScreenUpdating = False
'set the range for Table with error, Table1 on sheet 1
With Sheets(1) 'change to name of the sheet, more reliable than index num.
Set rngTableWithErrors = .Range("F2:F" & .Cells(.Rows.Count, 6).End(xlUp).Row)
Debug.Print rngTableWithErrors.Address
End With
'set the range for Table with Analysis, Table 2 on sheet 2
With Sheets(2) 'change to name of the sheet, more reliable than index num.
Set rngTableWithAnalysis = .Range("A1:B" & .Cells(.Rows.Count, 2).End(xlUp).Row)
Debug.Print rngTableWithAnalysis.Address
End With
'formula for cell G2
'=VLOOKUP(LEFT(F2;FIND(" ";F2;FIND(" ";F2)+1)- 1)&"*";Sheet2!A1:B23;2; 0)
rngTableWithErrors.Offset(0, 1).FormulaR1C1 = _
"=VLOOKUP(LEFT(R[0]C[-1],FIND("" "",R[0]C[-1],FIND("" "",R[0]C[-1])+1)-1)& ""*"",Sheet2!R1C1:R" & rngTableWithAnalysis.Rows.Count & "C2,2, 0)"
Application.ScreenUpdating = True
MsgBox "Done"
End Sub
Notes
You can notice, that we are setting the upper left cells of ranges manually. It's better practice to somehow find the upper left cells (using Find method is my favorite) and work from there. You never know, how the user will change the worksheet - i.e. add new rows, columns etc.