How to categorise a column in excel based on another column containing value in string with delimiter ; where string can have two separate words - vba

This question is a follow up to a previous query: How to categorise a column in excel based on another column that contains value in string separated by semicolons
I have the following spreadsheet (please click on link below for image):
Raw data and expected output
My question is I want to categorise the raw data so the output is as pictured in B16:B34 and C16:C34. I am trying to categorise people by their interests when their interests are in a column containing strings separated by semicolon with multiple words. The Name can come up multiple times according to their interests where in this case Movie, Action Movie, Music, Rock Music, Jazz Music and Radio.
I have tried the answer provided by #Glitch_doctor:
{=IFERROR(INDEX($B$1:$B$11,SMALL(IF(ISNUMBER(SEARCH(B$15,$C$1:$C$11)),ROW($C$1:$C$11)-ROW(INDEX($C$1:$C$11,1,1))+1),ROW(1:1))),"")}
The issue with the answer is that when a person's interest is actually Action Movie they show up in the Movie categorisation. I am trying to match when their interest is Action Movie they only show up in Action Movie after categorisation. Multiple words doesn't seem to work with SEARCH function.
I tried to replace the SEARCH function with a VBA code:
Function ProjectSearch(ByVal strProj As String, ByVal strVal As String, _
Optional ByVal delimiter As String = ";") As Boolean
Dim i As Long
Dim strSplit() As String
strSplit = Split(strProj, delimiter)
ProjectSearch = False
For i = LBound(strSplit) To UBound(strSplit)
If strSplit(i) = strVal Then
ProjectSearch = True
Exit Function
End If
Next i
End Function
Since I am a newbie it doesn't seem to work. My question is there a way where I can do what I want without VBA? If I do need to use VBA what do I need to do?
Thanks kindly in advance.

Okay, that was much simpler than I was expecting it to be:
=IFERROR(INDEX($C$1:$C$11,SMALL(IF(ISNUMBER(IF(SEARCH(E$15,$D$1:$D$11)=1,SEARCH(E$15,$D$1:$D$11),SEARCH(CONCATENATE("; ",E$15),$D$1:$D$11))),ROW($D$1:$D$11)-ROW(INDEX($D$1:$D$11,1,1))+1),ROW(1:1))),"")
I only changed the ISNUMBER(SEARCH(B$15,$C$1:$C$11)) part to ISNUMBER(IF(SEARCH(E$15,$D$1:$D$11)=1,SEARCH(E$15,$D$1:$D$11),SEARCH(CONCATENATE("; ",E$15),$D$1:$D$11)))
The addition IF statement is saying that if the search finds the single word as the first character in the cell to accept that search into the array, otherwise search beginning with "; " instead.

Related

In Excel VBA, how do I refer to a named column that contains a single quote

I am investigating a bug in an Excel spreadsheet where the following formula is inserted into every cell in a column.
=AGGREGATE( 3, 5, InputData[#[Foo]:[Bar]]) > 0
The VBA is as follows:
Let AddColumn.DataBodyRange.formula = "=AGGREGATE( 3, 5, [#[Foo]:[Bar]]) > 0"
this will evaluate to FALSE if all of the cells on the current row between columns Foo and Bar are empty, otherwise it evaluates to TRUE
The problem I'm seeing is that the names Foo and Bar are variable and not under my control and the formula fails with Run-time error 1004 if a name contains a single quote:
Let AddColumn.DataBodyRange.formula = "=AGGREGATE( 3, 5, [#[Foo's name]:[Bar]]) > 0"
Is there a way I can escape the name in such a way that single quotes won't create the run-time error? Adding double quotes around the name gives me the same error.
Are there likely to be further problems if the names contain other characters that have special meaning in Excel?
I could also refer to the columns by address instead of name. Would that work with the current row '#' notation?
Excel version:14.0.7188.5002
I hear you when you say the naming convention is "not under my control". This really puts you in a bind when anything can be pumped into your code.
Sadly, the only solution is to scrub the input when they finally hand it over to you. This involves you having to make your own vba function that takes in a string and returns a string that has special characters removed (or replaced with something else).
In your case, you are going to have to scrub the data in possibly two places.
First, you will need to change all the column names so they don't have special characters in them. You'll need to access each name and send it through the 'scrub' function and then replace the name with the scrubbed name.
Second, when someone inputs a column name for your AGGREGATE, you'll need to capture that input into a string variable and then pass that through the 'scrub' function. Then you'll need to validate that the input they gave you matches up with a valid column name. If it's not valid, send them an error message asking them to enter a valid name or to cancel out.
After you have valid values for foo and bar, you can add them to your AGGREGATE function and let it execute.
If you can't scrub/change your column names, then you'll have to make a list of scrubbed column names and associate them with the column address. Then when you get your input, scrub it, and then match to the list to grab the correct address. Then you can use hard addresses instead of variable naming schemes.
It's a lot of work. But it's necessary when you have naming conventions that are not under your control.
The other answers and comments put me on the right track:
Function escapedColumnName(columnName As String) As String
columnName = Replace(columnName, "'", "''")
columnName = Replace(columnName, "#", "'#")
columnName = Replace(columnName, "[", "'[")
columnName = Replace(columnName, "]", "']")
escapedColumnName = columnName
End Function

Excel VBA Macro to change empty fields in a column to 'checked' (String) based on various search criterias

Hi I am really new to VBA Macros and would appreciate any help on this matter.
I need to write a macro to change the value of a column to 'checked' based on different search criterias. My requirement is that one of the columns values must be automatically marked as checked based on search criterias which will search for existence of serial nos contained in a string in one column inside string values of other columns.
Please help me in creating a macro to accomplish this task.
I am sorry to say that I cannot share the code or attach a screenshot to further explain the scenario because of confidentiality reasons.
Detailed explaination
Column to be modified : Comments (initially blank)
Columns used for search criteria : Can be any column in the existing table.
Search criteria:
A substring from the reference column (basically a number with 6 digits will be selected as the search key)
Note : There is no specific format followed by this column (and we can do nothing about that) eg: In one column the entry will be 35567890-DEF-GHJ while in another it will be like Ref:35567890-- and in another column field it will be like CEK 35567890.
The substring must be checked not only for the entries in the same coulmn but with the contents of the entire table. Basically it is like the find function in the excel.
If a match is found I need to add up the values in the debit and credit entries and see whether the result is 0. If the result is zero, I need to enter 'checked' in the comments field that allows string values.
This is not one complete macro but still does the work.
Macros where used only for extracting the product code. For the remaining processes I used existing functions as in, performed a comparing function on the net amount and then used a simple if to mark the column as check.
Macro used for extracting product code.
Public Function getProductCode(source As String) As String
Dim rExp As Object, allMatches As Object, match As Object
Dim result As String: result = ""
Set rExp = CreateObject("vbscript.regexp")
With rExp
.Global = True
.MultiLine = False
.Pattern = "(?:^|\D)(\d{7,8})(?!\d)"
End With
Set allMatches = rExp.Execute(source)
For Each match In allMatches
result = result + match.SubMatches.Item(0)
Next
getProductCode = result
End Function
A new column 'Reference' was then created on which the macro was used passing the specific column and the extracted product code is stored here.
After this a function was used to find duplicate entries and sum up the total number of products sold. The formula I used is as follows:
=ROUND(SUMIFS([Total],[Reference],[#Reference]),2)=0
Finally I used a simple if to mark the column as checked if the previously generated result (in column status) is true.
=IF([#Column1]=TRUE, "Checked", "")
This is how I got it done.
There might be better way to do it. But if anyone else is facing the same problem this will help.

Word Macro for separating a comma-separated list to columns

I have a very large set of data that represents cartesian coordinates in the form x0,y0,z0,x1,y1,z1...xn,yn,zn. I need to create a new line at the end of each xyz coordinate. I have been trying to record a macro that moves a certain number of spaces from the beginning of each line, then creates a new line. This, of course, will not work since the number of digits in each xyz coordinate differs.
How can I create a macro to do this in Microsoft Word?
Try this:
Public Sub test()
Dim s As String
Dim v As Variant
Dim t As String
Dim I As Long
s = "x0,y0,z0,x1,y1,z1,xn,yn,zn"
v = Split(s, ",")
t = ""
For I = LBound(v) To UBound(v)
t = t + v(I)
If I Mod 3 = 2 Then
t = t + vbCr
Else
t = t + ","
End If
Next I
t = Left(t, Len(t) - 1)
Debug.Print t
End Sub
The Split function splits a string along the delimiter you specify (comma in your case), returning the results in a 0-based array. Then in the For loop we stitch the pieces back together, using a carriage return (vbCR) every third element and a comma otherwise.
The final (optional) step is to remove the trailing carriage return.
Hope that helps
The question placed before us was most clearly asked
“Please produce a macro sufficient to the task
I have Cartesian coordinates, a single line of these
Array them in many lines, triplets if you please!”
Instinctively we start to code, a solution for this quest
Often without asking, “Is this way truly best?”
But then another scheme arises from the mind
That most venerated duo: Word Replace and Find
Provide the two textboxes each an encantation
Check the Wildcard option and prepare for Amazation!
Forgive me!
In Word open Find/Replace
Click the More button and check the Use wildcards box
For Find what enter ([!,]{1,},[!,]{1,},[!,]{1,}),
For Replace with enter \1^p
Use Find Next, Replace and Replace All as usual
How it works
With wildcards, [!,]{1,} finds one or more chars that are NOT commas. This idiom is repeated 3 times with 2 commas separating the 3 instances. This will match 3 comma-delimited coordinates. The whole expression is then wrapped in parentheses to created an auto-numbered group (in this case Group #1). Creating a group allows us to save text that matches the pattern and use it in the Replace box. Outside of the parentheses is one more comma, which separates one triplet of coordinates from the next.
In the Replace box \1 retrieves auto-numbered group 1, which is our coordinate triplet. Following that is ^p which is a new paragraph in Word.
Hope that helps!

Count a specific word using a Function ans a Sub

In VB.net, I want to make a counting program using a Function and a Sub.
There is a textbox to input a date and a button to exercise the programme in Form1.
I have a txt file which was extracted from MS-Excel with sequential date of time at its column A.
And from that txt file, I want to count the number of date(Actually string) such as "18-Jun-12".
The answer showing the count should be in the format of msgbox in the Sub.
I really have no idea how to link a Function and a Sub using variable, because I am just beginner.
Any help will be gratefully accepted.
If the fields are delimited by comma you must be careful since the field itself could contain a comma. Then you cannot differentiate between the value and the delimiter. You either could enclose the fields with quotes to mask them. But then you should use an available CSV parser anyway.
If the values never contain comma and you want a simple solution use File.ReadLines or File.ReadAllLines to read the lines and String.Split to get all fields per line.
Here's a simple approach using a little bit of LINQ to count all lines which contain the searched date (as string):
Dim linesWithThatDate = From line in File.ReadLines("Path to File")
Where line.Split(","c)(0).Trim() = "18-Jun-12"
Dim count = linesWithThatDate.Count()
As an aside, if the user must enter a date you could use a DateTimePicker control instead. Then you should also use Date.Parse(line.Split(","c)(0).Trim()) or Date.TryParse to get a real date.

How do I find specific substring in a string

My full question title was too long, but it should be asked here:
How do I find all instances of a specific substring in a string accounting for spaces and special characters potentially being on either side of the substring
What I mean is this. I am writing a SQL code formatting assistance program in VB.Net. This program will help when I am following up on truly porrly writen SQL. A for instance is (and please ignore the syntax failure here, I am not good at writting bad code in SQL):
if exists(
select *
from dbo.table
where field1 = (if exists (select field1
from dbo.table1
where field2 = '123')
select field1 from table2)
My program is still in the early stages. I have already identified most of the keywords, and written the code that will put them in the proper case format. So in the bad code example from above all of the selects will be Select. To do this I have created a list of key words in array form, and use this array in the following function:
Private Function FindAndReplace(ByVal findWhat As String, _
ByVal replaceWith As String, ByVal focusLine As String) As String
focusLine = Microsoft.VisualBasic.Strings.Replace(focusLine, findWhat, _
replaceWith, 1, -1, Constants.vbTextCompare)
Return focusLine
End Function
The good news is this works really well with words like Select. Words like If, Go, On, and End are a bit more challenging. If I have the word Send, it will replace it with the word SEnd because End is a keyword. On many of these instances I can account for this by putting the smaller words before the larger words. I have added Send as a keyword because of the number of times that word appears in user messages on our systems.
I cannot seem to account for words like On, If, or Go. I considered searching for " Go ", " On ", ")Go ", " On(", etc. but there are times when Go is going to be the first word on the line...or the only.
What I need is a VB.Net means of searching a string for all of the instances of a given substring (such as If). I was thinking I would check if it was the first word in the string, or seeing if it is surrounded by any combination of spaces or special characters (or not surrounded by other letters and underscores, etc.). I would update those that met my requirements, and leave the others alone.
I am drawing a blank on how to do this, and I could really use some assistance.
I am writing a SQL code formatting assistance program
I'd recommend starting with an existing SQL parser.
Pete Sestoft's excellent Programming Language Concepts book introduces parsing fundamentals including writing Lexer and Parser specifications for Micro-SQL in Chapter 3.
The open source Irony project includes an SQL grammar sample.
Use your favourite search engine to find others.
What I need is a VB.Net means of searching a string for all of the instances of a given substring
There are a number of ways of achieving this:
Split the string into words and then search those words for instances.
Use a state machine to iterate over the string and check words after white space.
With option 2 you can handle quoted strings and maintain an index for each word, here's a short example in F#: http://fssnip.net/f6