Searching for several exact substrings in one String - vba

I have a problem I've been working on for a while but cannot seem to get there. I have a list of about 6000 material descriptions and I want to pull out a specific searched for word.
So if the description is 'Handschuhe-Wunder-20XV28', and the search word was 'Wunder', this material would then have a new column that said 'Wunder'. However, I might also want to search for the word 'Super', and I would like this to appear in the same column.
This search would only pick up the exact words, so if it was looking for 'Super', it wouldn't return a result if it found the word 'Superman'.
I had a formula which can do this:
=IF(AQ2=1,IF(SUM(IF(ISNUMBER(SEARCH(Search!A$2, K2)), IF(COUNT((IF(CODE(MID(K2, SEARCH(Search!A$2, K2)-1, 1))>122, 1, "")), (IF(CODE(MID(K2, SEARCH(Search!A$2, K2)-1, 1))<65, 1, "")), (IF(SEARCH(Search!A$2, K2)-1<1, 1, ""))), IF(COUNT((IF(CODE(MID(K2, SEARCH(Search!A$2, K2)+LEN(Search!A$2), 1))>122, 1, "")), (IF(CODE(MID(K2, SEARCH(Search!A$2, K2)+LEN(Search!A$2), 1))<65, 1, "")), (IF((SEARCH(Search!A$2, K2)+LEN(Search!A$2)+1)>LEN(K2), 1, ""))), 1, 0), 0), 0))>0,Search!A$2,0),"")
Where AQ2 contained the initial quick search:
=IF(IFERROR(SEARCH(Search!$A$2,'Raw Data Working'!K2),0)=0,"",1)
This was to improve efficiency as the first formula separates non-alphanumerical characters to find the exact word, after the second formula I posted works out whether it is worth search at all.
I tried recording this in visual basic, with the idea that there would be columns increasing as part of a for loop, with two columns for each search item. I would then somehow bring across the results of any searches into one column. However, when I record the long formula in VBA, despite including breaks, it still does not work.
I would be grateful for any help, suggestions or ideas. Quite simply, it is looking in one piece of text, to see whether the text includes either of several works. And they would have to be exact matches.
Thanks everyone!

Based on what has been discussed, the following should meet your needs or at least be a lot closer to what you imagine.
First is a function that accepts all the characters you wish to delimit the string with:
Note: This function is actually kind of awesome.
Function MultiSplitX(ByVal SourceText As String, RemoveBlankItems As Boolean, ParamArray Delimiters()) As String()
Dim a As Integer, b As Integer, n As Integer
Dim i As Integer: i = 33
Dim u As Variant, v As Variant
Dim tempArr() As String, finalArr() As String, fDelimiters() As String
If InStr(TypeName(Delimiters(0)), "()") <> 0 And LBound(Delimiters) = UBound(Delimiters) Then
ReDim fDelimiters(LBound(Delimiters(0)) To UBound(Delimiters(0))) 'If passing array vs array items then
For a = LBound(Delimiters(0)) To UBound(Delimiters(0)) 'build that array
fDelimiters(a) = Delimiters(0)(a)
Next a
Else
fDelimiters = Delimiters(0)
End If
Do While InStr(SourceText, Chr(i)) <> 0 'Find an unused character
i = i + 1
Loop
For a = LBound(fDelimiters) To UBound(fDelimiters) 'Sort Delimiters by length
For b = a + 1 To UBound(fDelimiters)
If Len(fDelimiters(a)) < Len(fDelimiters(b)) Then
u = fDelimiters(b)
fDelimiters(b) = fDelimiters(a)
fDelimiters(a) = u
End If
Next b
Next a
For Each v In fDelimiters 'Replace Delimiters with a common character
SourceText = Replace(SourceText, v, Chr(i))
Next
tempArr() = Split(SourceText, Chr(i)) 'Remove empty array items
If RemoveBlankItems = True Then
ReDim finalArr(LBound(tempArr) To UBound(tempArr))
n = LBound(tempArr)
For i = LBound(tempArr) To UBound(tempArr)
If tempArr(i) <> "" Then
finalArr(n) = tempArr(i)
n = n + 1
End If
Next i
n = n - 1
ReDim Preserve finalArr(LBound(tempArr) To n)
MultiSplitX = finalArr
Else: MultiSplitX = tempArr
End If
Erase finalArr
Erase tempArr
End Function
Next is the routine that finds all applicable matches:
Sub SearchDynamicDelimit()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets(2)
Dim strTest As New Collection
Dim udRange As Range: Set udRange = ws.Range("D2", ws.Cells(ws.Rows.Count, "D").End(xlUp)) '<~~Change to your Search Range
Dim myCell, myMatch, myString, i, delimiter, d, s, t, u, c
Dim temp() As String, myDelimiter() As String, delNotInSearch() As String, delInSearch() As String, tempCell As String
Dim delimitInSearch As Boolean: delimitInSearch = False
Dim delString As String, searchString As String
For Each myMatch In udRange
If myMatch.Value <> "" Then strTest.Add myMatch.Value
searchString = searchString & CStr(myMatch.Value)
Debug.Print myMatch.Value & " " & myMatch.Address
Next myMatch
ws.Range("B2", ws.Cells(ws.Rows.Count, "B")).Clear '<~~Change to where you want the results to populate
delString = "_|-|.|/|<|>|;|:|[|]|\|{|}| |(|,|)" '<~~Change to the delimiters you want. Separate them with any unique character.
myDelimiter() = Split(delString, "|") '<~~Make sure the unique character you chose above is the same here.
ReDim delNotInSearch(LBound(myDelimiter) To UBound(myDelimiter))
ReDim delInSearch(LBound(myDelimiter) To UBound(myDelimiter))
t = LBound(myDelimiter)
u = LBound(myDelimiter)
For s = LBound(myDelimiter) To UBound(myDelimiter)
If InStr(searchString, myDelimiter(s)) = 0 Then
delNotInSearch(t) = myDelimiter(s)
Debug.Print "delNotInSearch(" & t & ") = " & delNotInSearch(t)
t = t + 1
Else
delInSearch(u) = myDelimiter(s)
Debug.Print "delInSearch(" & u & ") = " & delInSearch(u)
u = u + 1
End If
Next s
t = t - 1
u = u - 1
If t <> -1 Then ReDim Preserve delNotInSearch(LBound(myDelimiter) To t)
If u <> -1 Then ReDim Preserve delInSearch(LBound(myDelimiter) To u)
If delInSearch(LBound(delInSearch)) <> "" Then delimitInSearch = True
If strTest.Count > 0 Then
For Each myCell In ws.Range("A2", ws.Cells(ws.Rows.Count, "A").End(xlUp)) '<~~Change to range being searched
If myCell.Value = "" Then GoTo SkipBlanks
If delimitInSearch = True Then
temp() = MultiSplitX(myCell.Value, True, delNotInSearch())
For i = 0 To UBound(temp)
For Each myString In strTest
If StrComp(temp(i), myString, vbTextCompare) = 0 Then
If ws.Range("B" & myCell.Row).Value = "" Then 'If you only want it to show 1 search result, remove the IF statement entirely
ws.Range("B" & myCell.Row).Value = temp(i) 'And keep this line only. Change "B" to where you want the results to go
Else: ws.Range("B" & myCell.Row).Value = ws.Range("B" & myCell.Row).Value & ", " & temp(i)
End If
End If
Next myString
Next i
Erase temp
End If
temp() = MultiSplitX(myCell.Value, True, delInSearch())
For i = 0 To UBound(temp)
For Each myString In strTest
If StrComp(temp(i), myString, vbTextCompare) = 0 Then
If ws.Range("B" & myCell.Row).Value = "" Then 'If you only want it to show 1 search result, remove the IF statement entirely
ws.Range("B" & myCell.Row).Value = temp(i) 'And keep this line only. Change "B" to where you want the results to go
Else: ws.Range("B" & myCell.Row).Value = ws.Range("B" & myCell.Row).Value & ", " & temp(i)
End If
End If
Next myString
Next i
Erase temp
SkipBlanks:
Next myCell
Else: MsgBox "Nothing found to search...", Title:="No Search Item"
End If
End Sub
On my sample workbook, I yielded the following results using the routine in conjuction with the MultiSplit function:
Notice Sich.Okay was properly found, even though "." being used as a delimiter.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ORIGINAL ANSWER BELOW FOR THOSE IT MAY HELP
If you want a method using VBA you could try something like this:
Sub ColorMatchingString()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets(1)
Dim strTest As Collection: Set strTest = New Collection
Dim udRange As Range: Set udRange = ws.Range("E1:G1") 'Define Search Ranges
Dim myCell, myMatch, myString, i, nextRR As Long
Dim temp() As String, tempLength As Integer, stringLength As Integer
nextRR = 3
For Each myMatch In udRange 'Build the collection with Search Range Values
strTest.Add myMatch.Value
Next myMatch
If ws.Range("E1").Value <> "" Or ws.Range("F1").Value <> "" Or ws.Range("G1").Value <> "" Then
For Each myCell In ws.Range("A1:A50")
temp() = Split(myCell.Text, "-")
startLength = 0
stringLength = 0
For i = 0 To UBound(temp)
tempLength = Len(temp(i))
stringLength = stringLength + tempLength + 2
For Each myString In strTest
If StrComp(temp(i), myString, vbTextCompare) = 0 Then
ws.Range("H" & nextRR).Value = myCell.Text
ws.Range("I" & nextRR).Value = myCell.Address
startLength = stringLength - tempLength - 2
ws.Range("H" & nextRR).Characters(startLength, tempLength).Font.Color = vbRed
nextRR = nextRR + 1
End If
Next myString
Next i
Erase temp
Next myCell
Else: MsgBox "Nothing found to search...", Title:="No Search Item"
End If
End Sub
What this will do is find your search items and show them in a new column, as well as show you where the item was found.

You could still keep a formula based approach, using an array formula. So, using the list being in e1:e4 and search criteria 1 in I1 and search criteria 2 in J1, I used the following
=INDEX($E$1:$E$4,SMALL(IF((NOT(ISERROR(SEARCH($I$1 & " ",$E$1:$E$4))))+(NOT(ISERROR(SEARCH($J$1 & " ",$E$1:$E$4)))),ROW($E$1:$E$4)),ROWS($E$1:$E1)))
and dragged down
Results can be seen in column G
Based on the comments, I've done the below array formula, this time, I've my data in A1:A5 and my search terms in D1 and D2.
=IFERROR(INDEX($A$1:$A$5 & " (" & $D$1 &")",SMALL(IF(NOT(ISERROR(SEARCH($D$1,$A$1:$A$5))),ROW($A$1:$A$5)),ROWS($B$1:$B1))),IFERROR(INDEX($A$1:$A$5 & " (" & $D$2 &")",SMALL(IF(NOT(ISERROR(SEARCH($D$2,$A$1:$A$5))),ROW($A$1:$A$5)),ROWS($B$1:$B1)-SUM(IF(NOT(ISERROR(SEARCH($D$1,$A$1:$A$5))),1,0)))),"<>"))
This looks as follows

This present two solutions:
1. Method to work with long formulas in VBA
2. VBA code to classify all Materials Description at once.
1. Method to work with long formulas in VBA
This formula is too long for VBA.
=IF(AQ2=1,IF(SUM(IF(ISNUMBER(SEARCH(Search!A$2, K2)), IF(COUNT((IF(CODE(MID(K2, SEARCH(Search!A$2, K2)-1, 1))>122, 1, "")), (IF(CODE(MID(K2, SEARCH(Search!A$2, K2)-1, 1))<65, 1, "")), (IF(SEARCH(Search!A$2, K2)-1<1, 1, ""))), IF(COUNT((IF(CODE(MID(K2, SEARCH(Search!A$2, K2)+LEN(Search!A$2), 1))>122, 1, "")), (IF(CODE(MID(K2, SEARCH(Search!A$2, K2)+LEN(Search!A$2), 1))<65, 1, "")), (IF((SEARCH(Search!A$2, K2)+LEN(Search!A$2)+1)>LEN(K2), 1, ""))), 1, 0), 0), 0))>0,Search!A$2,0),"")
To write long Formulas with VBA, we need to slice it down in several parts using variables.
So let’s first see the formula broken down by excel functions:
=IF(AQ2=1,
IF(
SUM(
IF(
ISNUMBER(SEARCH(Search!A$2, K2)),
IF(
COUNT(
(IF(CODE(MID(K2, SEARCH(Search!A$2, K2)-1, 1))>122, 1, "")),
(IF(CODE(MID(K2, SEARCH(Search!A$2, K2)-1, 1))<65, 1, "")),
(IF(SEARCH(Search!A$2, K2)-1<1, 1, ""))),
IF(
COUNT(
(IF(CODE(MID(K2, SEARCH(Search!A$2, K2)+LEN(Search!A$2), 1))>122, 1, "")),
(IF(CODE(MID(K2, SEARCH(Search!A$2, K2)+LEN(Search!A$2), 1))<65, 1, "")),
(IF((SEARCH(Search!A$2, K2)+LEN(Search!A$2)+1)>LEN(K2), 1, ""))
), 1, 0),
0),
0)
)>0,
Search!A$2,0)
,"")
This formula has basically 4 parts:
• It checks what you call the quick search in cell AQ2 - Part 1
=IF(AQ2=1,
IF(
SUM(
IF(
ISNUMBER(SEARCH(Search!A$2, K2)),
• Validates the character immediately before and after the word found:
Part 2 - before:
IF(
COUNT(
(IF(CODE(MID(K2, SEARCH(Search!A$2, K2)-1, 1))>122, 1, "")),
(IF(CODE(MID(K2, SEARCH(Search!A$2, K2)-1, 1))<65, 1, "")),
(IF(SEARCH(Search!A$2, K2)-1<1, 1, ""))),
Part 3 - after:
IF(
COUNT(
(IF(CODE(MID(K2, SEARCH(Search!A$2, K2)+LEN(Search!A$2), 1))>122, 1, "")),
(IF(CODE(MID(K2, SEARCH(Search!A$2, K2)+LEN(Search!A$2), 1))<65, 1, "")),
(IF((SEARCH(Search!A$2, K2)+LEN(Search!A$2)+1)>LEN(K2), 1, ""))
), 1, 0),
0),
0)
)>0,
• Then returns the result - Part 4
Search!A$2,0)
,"")
Following the same logic I have modified your formula:
=IF(
IF(OR(
IFERROR(CODE(TRIM(MID($K2,IFERROR(SEARCH(Search!A$2,$K2),0)+
IF(IFERROR(SEARCH(Search!A$2,$K2),0)=1,0,-1),1))),0)
<65,
IFERROR(CODE(TRIM(MID($K2,IFERROR(SEARCH(Search!A$2,$K2),0)+
IF(IFERROR(SEARCH(Search!A$2,$K2),0)=1,0,-1),1))),0)
>122),0,1)
+
IF(OR(
IFERROR(CODE(TRIM(MID($K2,IFERROR(SEARCH(Search!A$2,$K2),0)+
LEN(Search!A$2),1))),0)
<65,
IFERROR(CODE(TRIM(MID($K2,IFERROR(SEARCH(Search!A$2,$K2),0)+
LEN(Search!A$2),1))),0)
>122),0,1)
<>0,"",Search!A$2)
Now we can clearly see the fundamental parts of the formula:
• Getting the character immediately before:
IFERROR(CODE(TRIM(MID($K2,IFERROR(SEARCH(Search!A$2,$K2),0)+
IF(IFERROR(SEARCH(Search!A$2,$K2),0)=1,0,-1),1))),0)
• Getting the character immediately after:
IFERROR(CODE(TRIM(MID($K2,IFERROR(SEARCH(Search!A$2,$K2),0)+
LEN(Search!A$2),1))),0)
Now we use variables to define the formula, but first in order to make the formula flexible so it can be applied with any reference let’s modify the “hard coded” references with strings that can be replaced with the references obtained at run time:
Substitute $K2 with #Cll and Search!A$2 with #Srch
The final code is something like this:
Sub Vba_Long_Formula()
Dim sCll As String, sSrch As String
sCll = "$K2"
sSrch = "Search!A$2"
Dim sFmlIni As String, sFmlEnd As String 'Formulas for the before and after characters
'Chr(10) is used to ease reading by breaking the formula by line
sFmlIni = "IFERROR(CODE(TRIM(MID(#Cll,IFERROR(SEARCH(#Srch,#Cll),0)+" & Chr(10) & _
"IF(IFERROR(SEARCH(#Srch,#Cll),0)=1,0,-1),1))),0)"
sFmlEnd = "IFERROR(CODE(TRIM(MID(#Cll,IFERROR(SEARCH(#Srch,#Cll),0)+" & Chr(10) & _
"LEN(#Srch),1))),0)"
Dim sFml1 As String 'Formula to be applied
sFml1 = "=IF(" & Chr(10) & _
"IF(OR(" & Chr(10) & sFmlIni & "<65," & Chr(10) & sFmlIni & ">122),0,1)+" & Chr(10) & _
"IF(OR(" & Chr(10) & sFmlEnd & "<65," & Chr(10) & sFmlEnd & ">122),0,1)<>0,"""",#Srch)"
sFml1 = Replace(Replace(sFml1, "#Cll", sCll), "#Srch", sSrch)
ThisWorkbook.Sheets("Raw Data Working").Range("AR2:AR4").Formula = sFml1
End Sub
Hope the above solves the problem with the long formula in VBA.
2. VBA code to classify all Materials Description at once.
However, if you are using VBA then it’s more efficient to run the entire process using VBA to classify all the Material Descriptions with the corresponding Brands.
This code assumes the following (change as required):
List of Brands starts at 'Search'!A2
List of Materials Descriptions starts at 'Raw Data Working'!K2
Output of Brands in column 'Raw Data Working'!AP
Option Compare Text ‘Must have this at the top of the module
Option Explicit
Sub Brand_Classification()
Dim aBrands As Variant, rMaterials As Range, rResults As Range
Dim rFound As Range, blFound As Boolean, sFound As String
Dim sMaterial As String
Dim lLastRow As Long
Dim vItm As Variant
Dim iAsc As Integer, bPos As Byte
Dim b As Byte
Rem Set Array with Brands
With ThisWorkbook.Sheets("Search") 'Change as needed
lLastRow = .Columns("A:A").Cells(1 + .UsedRange.SpecialCells(xlLastCell).Row).End(xlUp).Row 'Change as needed
aBrands = .Range("A2:A" & lLastRow).Value2 'Change as needed
End With
With ThisWorkbook.Sheets("Raw Data Working") 'Change as needed
Rem Set Materials Description Range
lLastRow = .Columns("K:K").Cells(1 + .UsedRange.SpecialCells(xlLastCell).Row).End(xlUp).Row 'Change as needed
Set rMaterials = .Range("K2:K" & lLastRow) 'Change as needed
Rem Set Brand Results Range
Set rResults = .Range("AP2:AP" & lLastRow) 'Change as needed
Rem Clearing prior results
'rResults.ClearContents '}Choose one of
rResults.Value = Chr(39) '}these options
End With
Rem Search for Brands in Materials Description
For Each vItm In aBrands
If vItm <> Empty Then
With rMaterials
Set rFound = .Cells.Find(What:=vItm, After:=.Cells(.Cells.Count), _
LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
Rem Validate Value Found
If Not rFound Is Nothing Then
sFound = rFound.Address
Do
Rem Process Value Found
blFound = True
sMaterial = rFound.Value
For b = 1 To 2
Select Case b
Case 1
Rem Get Character Before Value
bPos = InStr(sMaterial, vItm)
bPos = -1 + bPos
Case 2
Rem Get Character After Value
bPos = InStr(sMaterial, vItm) + Len(vItm)
End Select
Rem Get Character
Select Case bPos
Case 0, Is > Len(sMaterial)
Case Else
Rem Validate Character
On Error Resume Next
iAsc = Asc(Mid(sMaterial, bPos, 1))
On Error GoTo 0
Select Case iAsc
Case 65 To 90, 97 To 122
blFound = False
End Select: End Select: Next
Rem Write Results
If blFound Then
With rResults.Cells(1 - rMaterials.Row + rFound.Row)
If .Value = Empty Then
.Value = vItm
Else
.Value = .Value & ", " & vItm
End If: End With: End If
Rem Search Next
Set rFound = .FindNext(After:=rFound)
If rFound.Address = sFound Then Exit Do
Loop: End If: End With: End If: Next
End Sub
Suggest to read the following pages to gain a deeper understanding of the resources used:
Do...Loop Statement,
For Each...Next Statement,
On Error Statement,
Option keyword,
Range Object (Excel),
Select Case Statement,
Variables & Constants,
With Statement,

Related

VBA Highlight multiple keywords with wildcard from text strings

Any help here would be appreciated please.
The included VBA code almost meets the intended purpose, however, I need a solution that enables the use of wildcards and highlights all parameters contained between "##", "%%" or potentially other special characters (special characters included).
For instance, lets say in the cell range B2:B10 we would find something like:
Checked at ##date1## and ##hour1##
But I want to be able to do a search and highlight using # * # or % * % within a selected determined cell range with the end result (bold being color):
Checked at ##date1## and ##hour1##
Sub HighlightStrings()
Application.ScreenUpdating = False
Dim Rng As Range
Dim cFnd As String
Dim xTmp As String
Dim x As Long
Dim m As Long
Dim y As Long
Dim xFNum As Integer
Dim xArrFnd As Variant
Dim xStr As String
cFnd = InputBox("Please enter the text, separate them by comma:")
If Len(cFnd) < 1 Then Exit Sub
xArrFnd = Split(cFnd, ",")
For Each Rng In Selection
With Rng
For xFNum = 0 To UBound(xArrFnd)
xStr = xArrFnd(xFNum)
y = Len(xStr)
m = UBound(Split(Rng.Value, xStr))
If m > 0 Then
xTmp = ""
For x = 0 To m - 1
xTmp = xTmp & Split(Rng.Value, xStr)(x)
.Characters(Start:=Len(xTmp) + 1, Length:=y).Font.ColorIndex = 3
xTmp = xTmp & xStr
Next
End If
Next xFNum
End With
Next Rng
Application.ScreenUpdating = True
End Sub
Thank you
Okay This seems to work for me. There is a limitation we can work on if required: the phrase to highlight must be padded with spaces on both sides.
Option Explicit
Option Base 0
Sub testreplace()
Dim I As Integer 'Iteration
Dim FlagNum As Integer 'Flag Number
Dim RG As Range 'Whole range
Dim CL As Range 'Each Cell
Dim FlagChar As String 'Flag characters
Dim ArrFlag 'Flag Char Array
Dim TextTemp As String 'Cell Contents
Set RG = Selection
FlagChar = "##"
FlagChar = InputBox("Enter 'Flag Characters' separated by a comma." & vbCrLf & vbCrLf & _
"Example:" & vbCrLf & vbCrLf & _
"##,%%,&&" & vbCrLf & _
"$$,XX", "Flag Characters to Highlight", "##,%%")
ArrFlag = Split(FlagChar, ",")
For Each CL In RG.Cells
TextTemp = CL.Value
For FlagNum = 0 To UBound(ArrFlag)
For I = 1 To Len(TextTemp)
'Debug.Print "<<" & Mid(TextTemp, I, Len(ArrFlag(Flagnum)) + 1) & _
"=" & " " & ArrFlag(Flagnum) & ">>"
If Mid(TextTemp, I, Len(ArrFlag(FlagNum)) + 1) = " " & ArrFlag(FlagNum) Then
CL.Characters(I + 1, InStr(I, TextTemp, ArrFlag(FlagNum) & " ") + _
Len(ArrFlag(FlagNum)) - I).Font.ColorIndex = 3
End If
Next I
Next FlagNum
Next CL
End Sub
Here's an example of it working:

Format pasted rows within userforum-textbox into concatenation or borderline?

I get a mismatch error in this line :
row_str = Join(cell_rng, Chr(10))
Thank you. I am intermediate.
I attached a piece of the code below:
Dim last_row As String
Dim last_col As String
Dim office_str As String
Dim lookupVal As String
Dim i As Long
Dim seperate_cells, cell_rng As Range
Dim r As Range
Dim row_str As String
With Contacts
For i = 2 To last_row
Set cell_rng = Rows(i & ":" & i + 1)
For Each r In cell_rng.Rows
seperate_cells = cellsSeparator(r.SpecialCells(xlCellTypeConstants))
If row_str = "" Then
row_str = Join(cell_rng, Chr(10))
Else
row_str = row_str & vbLf & Join(cell_rng, Chr(10))
End If
Next
Debug.Print row_str
Client_Finder.result.Text = Client_Finder.result.Text & vbLf & row_str
Next i
End With
````
Please try the next way. It will place the values of the necessary specific row in the text box, each value separated by " | ":
Sub testSeparatorsBetweenRowCells()
'your existing code...
Dim arr, rngR As Range
For i = 2 To last_row
lookupVal = cells(i, office_str)
' Compare ComboBox with the range from the spreadsheet
If lookupVal = Office_Code Then
Set rngR = rows(i & ":" & i).SpecialCells(xlCellTypeConstants) 'Set a range which will return all cells value in the row, except the empty ones
arr = arrCells(rngR) 'call a function able to make an array from the range set in the above line
Client_Finder.result.Text = Client_Finder.result.Text & vbLf & Join(arr, " | ") 'add the text obtained by joining the array to the next line of existing text
End If
Next i
End Sub
Function arrCells(rng As Range) As Variant
Dim arr, Ar As Range, i As Long, C As Range
ReDim arr(rng.cells.count - 1) 'ReDim the array to be filled as the range cells number.
'- 1, because the array is 0 based...
For Each Ar In rng.Areas 'iterate between the range areas
For Each C In Ar.cells 'iterate between cells of each area
arr(i) = C.value: i = i + 1 'put each cell value in the array
Next
Next
arrCells = arr 'make the function returning the arr
End Function
If the text in the text box still goes on the next line, try making the text box property WordWrap False. If you cannot see all the text, make the textbox wider or decrease its font size.
Please, test it and send some feedback.
Edited:
Please, try understanding the next piece of code, able to deal with copying more rows at once:
Sub testCopyingMoreRows()
Dim sh As Worksheet, i As Long, rng As Range, r As Range, arr, strRow As String
Set sh = ActiveSheet
i = 9
Set rng = sh.rows(i & ":" & i + 1)
'you ca select cells, rows (even not consecutive) and use:
'Set rng = Selection.EntireRow 'just uncomment this code line...
'extract rows and paste their contents (exept the empty cells) in Imediate Window
For Each r In rng.rows
arr = arrCells(r.SpecialCells(xlCellTypeConstants))
If strRow = "" Then
strRow = Join(arr, " | ")
Else
strRow = strRow & vbLf & Join(arr, " | ")
End If
Next
Debug.Print strRow
'instead returning in Imediate Window, you can do it in your text box (uncomment the next line):
'Client_Finder.result.Text = Client_Finder.result.Text & vbLf & strRow
End Sub
The code uses the same function arrCells...

VBA Chart range substract

I found this code, which add's one extra column to the chart each time it runs.
Meaning first time it runs it shows week 1-7, secound time 1-8, next 1-9 and I would like it to show 2-7, 3-8, 4-9 ect.
Sub ChartRangeAdd()
On Error Resume Next
Dim oCht As Chart, aFormulaOld As Variant, aFormulaNew As Variant
Dim i As Long, s As Long
Dim oRng As Range, sTmp As String, sBase As String
Set oCht = ActiveSheet.ChartObjects(1).Chart
oCht.Select
For s = 1 To oCht.SeriesCollection.count
sTmp = oCht.SeriesCollection(s).Formula
sBase = Split(sTmp, "(")(0) & "(<FORMULA>)" ' "=SERIES(" & "<FORMULA>)"
sTmp = Split(sTmp, "(")(1) ' "..., ..., ...)"
aFormulaOld = Split(Left(sTmp, Len(sTmp) - 1), ",") ' "..., ..., ..."
aFormulaNew = Array()
ReDim aFormulaNew(UBound(aFormulaOld))
' Process all series in the formula
For i = 0 To UBound(aFormulaOld)
Set oRng = Range(aFormulaOld(i))
' Attempt to put the value into Range, keep the same if it's not valid Range
If Err.Number = 0 Then
Set oRng = oRng.Worksheet.Range(oRng, oRng.Offset(0, 1))
aFormulaNew(i) = oRng.Worksheet.Name & "!" & oRng.Address
Else
aFormulaNew(i) = aFormulaOld(i)
Err.Clear
End If
Next i
sTmp = Replace(sBase, "<FORMULA>", Join(aFormulaNew, ","))
Debug.Print "Series(" & s & ") from """ & oCht.SeriesCollection(s).Formula & """ to """ & sTmp & """"
oCht.SeriesCollection(s).Formula = sTmp
sTmp = ""
Next s
Set oCht = Nothing
End Sub
I want to do the opposite of this code, so instead of adding a column one column should be substracted. How can the code be modifued to do this?
(LINK: VBA: Modify chart data range)
Thank you!
Try changing the line
Set oRng = oRng.Worksheet.Range(oRng, oRng.Offset(0, 1))
to
Set oRng = oRng.Worksheet.Range(oRng.Offset(0, 1), oRng.Offset(0, 1))

Excel VBA - Randomly select 3 rows per username

I have a large list of tickets with a total of 6 different user names. What I need the code to do is randomly select 3 rows of data per user (18 total) and hide the rest of the rows, as I only need to see the selected rows.
The code will be something like the below, but I am not sure how to write the "random" part.
LastRow = Cells(Rows.Count, "A").End(xlUp).Row
With Range("A2:F" & LastRow)
*Select 3 random rows for user A*
*Select 3 random rows for user B*
*The same for C-F*
*Hide all other rows*
End With
Found this to be an interesting challenge. Something like this should work for you. Commented code for clarity.
Sub tgr()
'Adjust these parameters as necessary
Const sDataSheet As String = "Sheet1"
Const sUserCol As String = "A"
Const lHeaderRow As Long = 1
Const lShowRowsPerUser As Long = 3
Const bSortDataByUser As Boolean = False
'Declare variables
Dim ws As Worksheet
Dim rData As Range
Dim rShow As Range
Dim aData() As Variant
Dim aUserRows() As Variant
Dim lTotalUnqUsers As Long
Dim lMaxUserRows As Long
Dim i As Long, j As Long, k As Long
Dim lRandIndex As Long
'Test if sDataSheet name provided exists in ActiveWorkbook
On Error Resume Next
Set ws = ActiveWorkbook.Sheets(sDataSheet)
On Error GoTo 0
If ws Is Nothing Then
MsgBox "No sheet named [" & sDataSheet & "] found in " & ActiveWorkbook.Name & Chr(10) & _
"Correct sDataSheet in code and try again."
Exit Sub
End If
ws.Cells.EntireRow.Hidden = False 'Reset rows to show all data
'Work with the data range set by parameters
With ws.Range(sUserCol & lHeaderRow + 1, ws.Cells(ws.Rows.Count, sUserCol).End(xlUp))
'Verify data exists in specified location
If .Row < lHeaderRow + 1 Then
MsgBox "No data found in [" & sDataSheet & "]" & Chr(10) & _
"Verify column containing users is Column [" & sUserCol & "] or correct sUserCol in code." & Chr(10) & _
"Verify header row is Row [" & lHeaderRow & "] or correct lHeaderRow in code." & Chr(10) & _
"Once corrections have been made and data is available, try again."
Exit Sub
End If
lTotalUnqUsers = Evaluate("SUMPRODUCT((" & .Address(external:=True) & "<>"""")/COUNTIF(" & .Address(external:=True) & "," & .Address(external:=True) & "&""""))") 'Get total unique users
lMaxUserRows = Evaluate("max(countif(" & .Address(external:=True) & "," & .Address(external:=True) & "))") 'Get max rows per user
If bSortDataByUser Then .Sort .Cells, xlAscending, Header:=xlNo 'If bSortByUser is set to True, then sort the data
Set rData = .Cells 'Store the data in a range object for later use
aData = .Value 'Load the data into an array to speed operations
ReDim aUserRows(1 To lTotalUnqUsers, 1 To 3, 1 To lMaxUserRows) 'Ready the results array that random rows will be selected from
End With
'Load all available rows into the results array, grouped by the user
For i = LBound(aData, 1) To UBound(aData, 1)
For j = LBound(aUserRows, 1) To UBound(aUserRows, 1)
If IsEmpty(aUserRows(j, 1, 1)) Or aUserRows(j, 1, 1) = aData(i, 1) Then 'Find correct user
If IsEmpty(aUserRows(j, 1, 1)) Then aUserRows(j, 1, 1) = aData(i, 1) 'If user isn't in results array yet, add it
k = aUserRows(j, 2, 1) + 1 'Increment row counter for this user
aUserRows(j, 2, 1) = k
aUserRows(j, 3, k) = i + lHeaderRow 'Load this row into this user's group of rows
Exit For
End If
Next j
Next i
'Select random rows up to lShowRowsPerUser for each user from the grouped results array
For j = LBound(aUserRows, 1) To UBound(aUserRows, 1)
Do
Randomize
lRandIndex = Int(Rnd() * aUserRows(j, 2, 1)) + 1
If Not rShow Is Nothing Then
Set rShow = Union(rShow, ws.Cells(aUserRows(j, 3, lRandIndex), sUserCol))
Else
Set rShow = ws.Cells(aUserRows(j, 3, lRandIndex), sUserCol)
End If
Loop While rShow.Cells.Count < j * Application.Min(lShowRowsPerUser, aUserRows(j, 2, 1))
Next j
rData.EntireRow.Hidden = True 'Hide all relevant rows
rShow.EntireRow.Hidden = False 'Only show the rows that have been randomly selected
End Sub

Get the value between the parentheses, multiple matches in one string

My spreadsheet has a column with value like this string:
some text (text1) some test (text2) (text1)
How do I get all values between parentheses? The result I am looking for is:
text1, text2
Even if text1, text2... testn is present in the cell multiple times, I need it in the result only once.
I found a function GetParen here: Get the value between the brackets
It is helpful, but it gives the fist available value in the parentheses and ignores the rest.
It seems unwieldy to have one User Defined Function for individual entries and another for a collective result of all entries.
Paste the following into a standard module code sheet.
Function getBracketedText(str As String, _
Optional pos As Integer = 0, _
Optional delim As String = ", ", _
Optional dupes As Boolean = False)
Dim tmp As String, txt As String, a As Long, b As Long, p As Long, arr() As Variant
tmp = str
ReDim arr(1 To 1)
For b = 1 To (Len(tmp) - Len(Replace(tmp, Chr(40), vbNullString)))
p = InStr(p + 1, tmp, Chr(40))
txt = Trim(Mid(tmp, p + 1, InStr(p + 1, tmp, Chr(41)) - (p + 1)))
If UBound(Filter(arr, txt, True)) < 0 Or dupes Then '<~~ check for duplicates within the array
a = a + 1
ReDim Preserve arr(1 To a)
arr(UBound(arr)) = txt
End If
Next b
If CBool(pos) Then
getBracketedText = arr(pos)
Else
getBracketedText = Join(arr, delim)
End If
End Function
Use like any other native worksheet function. There are optional parameters to retrieve an individual element or a collection as well as changing the default <comma><space> delimiter.
    
This code works for me:
Sub takingTheText()
Dim iniP 'first parenthesis
Dim endP 'last parentehis
Dim myText 'the text
Dim txtLen
Dim i
Dim tmp
Dim j
myText = Range("A1").Value
txtLen = Len(myText)
j = 0
Do 'Loop in the text
i = i + 1 'a counter
iniP = InStr(1, myText, "(", 1) 'found the first occurence of the (
endP = InStr(1, myText, ")", 1) 'same as above
tmp = tmp & Right(Left(myText, i), 1) 'take the text garbage text
If i = iniP Then 'here comes the work
j = j + 1 'here take the cell index
myText = Replace(myText, tmp, "") 'remove the garbage text in front the first (
tmp = Left(myText, endP - iniP - 1) 'reuse the var to store the usefull text
Cells(1, 2).Value = Cells(1, 2).Value & Chr(10) & tmp 'store in the cell B1
'If you want to stored in separated cells use the below code
'Cells(j, 2).Value = tmp
myText = Replace(myText, tmp & ")", "", 1, 1) ' remove the garbage text from the main text
tmp = Empty 'empty the var
i = 0 'reset the main counter
End If
Loop While endP <> 0
End Sub
Result:
Please check and tellme if is ok.
Edit#1
Cells(1, 2).Value = Cells(1, 2).Value & Chr(10) & tmp this code store the text in separated lines inside the same cell, may be you want to use spaces between the resulting text because of chr(10) (also you can use chr(13)), then you can use Cells(1, 2).Value = Cells(1, 2).Value & " " & tmp, or use any other character instead the string inside the & symbols