Array splitting and extracting - arrays

I'm attempting to go through each character in a cell to determine whether or not a word is underlined and italicized but so far the loop runs and freezes. How can I copy and move the word that is italicized and underlined? This is what I have so far. I asked a new question because I wasn't clear enough in this one. It can be accessed at Array split and extract vba excel .
For Each j In ActiveSheet.Range("C1:C105")
v = Trim(j.Value)
If Len(v) > 0 Then
v = Replace(v, vbLf, " ")
Do While InStr(v, " ") > 0
v = Replace(v, " ", " ")
Loop
arr = Split(v, " ")
For Z = LBound(arr) To UBound(arr)
e = arr(Z)
For i = 1 To Len(v)
If j.Characters(i, 1).Font.Italic = True And j.Characters(i, 1).Font.Underline = True Then
j.Value.Copy
End If
Next i
Next Z
End If
Next j​

The following piece of code will Debug.Print all the words that are underlined and formatted italic in any of the given cells:
Option Explicit
Public Sub tmpSO()
Dim i As Long
Dim j As Range
Dim StartPoint As Long
Dim InItalicUnderlinedWord As Boolean
For Each j In ThisWorkbook.Worksheets(1).Range("C1:C105")
If Len(j.Value2) > 0 Then
For i = 1 To Len(j.Value2)
If j.Characters(i, 1).Font.Italic And j.Characters(i, 1).Font.Underline Then
If InItalicUnderlinedWord = False Then
StartPoint = i
InItalicUnderlinedWord = True
End If
Else
If InItalicUnderlinedWord = True Then
Debug.Print Mid(j.Value2, StartPoint, i - StartPoint)
InItalicUnderlinedWord = False
End If
End If
If InItalicUnderlinedWord = True And i = Len(j.Value2) Then
Debug.Print Mid(j.Value2, StartPoint, i - StartPoint + 1)
InItalicUnderlinedWord = False
End If
Next i
End If
Next j
End Sub
Debug.Print will output the italic and underlined word into the immediate window of the VBE. If you want these words anywhere else then you'll have to adjust the code in two (!) places:
Once in the section which starts with InItalicUnderlinedWord for any find anywhere within a cell and
in the section which starts with If InItalicUnderlinedWord = True And i = Len(j.Value2) Then for any occurrences where the last character in a cell is also underlined and italic.
Let me know if you have any questions or problems.

something like this, only does 1 cell, so you'll need to add it to your loop
Sub test()
Dim r As Range
Dim v As Variant
Dim i As Integer
Dim f As Integer
Set r = Range("h2")
v = Split(r.Value, Chr(32))
For i = 0 To UBound(v) - 1
f = InStr(1, r, v(i)) ' equiv Application.WorksheetFunction.Search(v(i), r)
If r.Characters(f, 1).Font.Italic Then
Debug.Print v(i) & " is italic"
End If
Next i
End Sub

A slightly simpler implementation involves copying the entire cell values first, and then manipulating the copied range. Call this in a loop, and provide it the two arguments: rngToCopy = the cell being copied and rngToPaste the destination cell (qualified to specific workbook/worksheet):
For each cl in Range("C1:C105")
Call CopyItalicUnderlined(cl, __Some Place Else__)
Next
Here's the procedure
Sub CopyItalicUnderlined(rngToCopy, rngToPaste)
rngToCopy.Copy rngToPaste
Dim i
For i = Len(rngToCopy.Value2) To 1 Step -1
With rngToPaste.Characters(i, 1)
If Not .Font.Italic And Not .Font.Underline Then
.Text = vbNullString
End If
End With
Next
End Sub

Related

Increment different counters depending on array index value

I have a vast list of data in a worksheet (called MainDump). I have a procedure set up to assess this list and return certain values using the following setup:
Dim ws1 As Worksheet
Set ws1 = Worksheets("DashBoard")
Dim ws2 As Worksheet
Set ws2 = Worksheets("MainDump")
Dim cntr As Long
On Error GoTo ErrorHandler 'Got A lot of divide by zero errors if searchstring wasn't found
With Application.WorksheetFunction
ws1.Range("O4").Value = .CountIf(ws2.Range("E:E"), "*" & "CEOD" & "*")
ws1.Range("L4").Value = .CountIfs(ws2.Range("E:E"), "*" & "CEOD" & "*", ws2.Range("A:A"), "Yes") / ws1.Range("O4").Value
ws1.Range("M4").Value = .CountIfs(ws2.Range("E:E"), "*" & "CEOD" & "*", ws2.Range("B:B"), "Yes") / ws1.Range("O4").Value
ws1.Range("N4").Value = .CountIfs(ws2.Range("E:E"), "*" & "CEOD" & "*", ws2.Range("C:C"), "SA Present, WBDA Present") / ws1.Range("O4").Value
End With
cntr = cntr + 1
'^This proces is then copied and thus repeated a total of 76 times, as I want to check
'for 76 different values in ws2.Range("E:E"), resulting in a massive code
ErrorHandler:
If Err.Number = 6 Then
If ws1.Range("O" & cntr).Value = 0 Then
ws1.Range("L" & cntr).Value = "div. by zero"
ws1.Range("M" & cntr).Value = "div. by zero"
ws1.Range("N" & cntr).Value = "div. by zero"
End If
End If
Resume Next
I wrote this when I was a lot less experienced in VBA. Needless to say this code takes a lot of time to complete (Maindump counts about 98000 rows).
So I wanted to try do this work via an array.
My approach would be to define a counter for each string I want to check in the array indexes and then looping through the array and increment the corresponding counters when a string is found in the Array. My question is if there is a way to write that loop in the following form:
Dim LastRow1 As long
Dim DataArray() As Variant
Dim SearchString1, SearchString2, .... SearchString76 As String
Dim SearchString1Cntr, SearchString2Cntr, .... SearchString76Cntr As long
With ws2
LastRow1 = .Cells.Find(What:="*", After:=.Range("A1"), LookAt:=xlPart, LookIn:=xlFormulas, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False).Row 'Gets the total row amount in the sheet
DataArray = .Range("A3:E" & LastRow1) 'puts selected range in Array
End With
For LastRow1 = Lbound(DataArray, 1) to Ubound(DataArray, 1)
'Start a For Each loop to check for all 76 strings
If Instr(1, DataArray(LastRow1, 5), SearchString > 0 Then 'SearchString is found so then
SearchStringCntr1 = SearchStringcntr1 + 1
'Where SearchStrinCntr1 is the counter related to the string checked for in the loop,
'so it switches when the SearchString changes
End If
'Next SearchString to check
Next LastRow1
So I want to try and use a flexible If statement in a For Next loop which checks the Array index for each SearchString and then increments the corresponding SearchStringCntr if the SearchString is found in the index, before looping to the next index. Is this possible? I would like to prevent making 76 different If/ElseIf statements for each SearchString + StringCntr and then use a counter to loop through them every time the code loops through the For LastRow1 / Next LastRow1 loop. Would love to hear your input.
Maybe this will help (might need some adjustments).
Create named range "Strings" somewhere in your workbook where you'll store all your strings that you're looking for
Option Explicit
Sub StringsCompare()
Dim LastRow1 As Long
Dim DataArray() As Variant, StringArray() As Variant
Dim Ws2 As Worksheet
Dim CompareStringsNo As Long, StringCounter As Long
Dim i As Long, j As Long
Dim aCell As Range
Dim SourceStr As String, SearchStr As String
Set Ws2 = ThisWorkbook.Sheets("Sheet1")
StringCounter = 1
With Ws2
'fill array with your strings to compare
CompareStringsNo = .Range("Strings").Rows.Count
ReDim StringArray(1 To CompareStringsNo, 1 To 2)
For Each aCell In .Range("Strings")
StringArray(StringCounter, 1) = aCell.Value
StringCounter = StringCounter + 1
Next aCell
'fill data array
LastRow1 = .Cells.Find(What:="*", After:=.Range("A1"), LookAt:=xlPart, LookIn:=xlFormulas, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False).Row 'Gets the total row amount in the sheet
DataArray = .Range("A1:E" & LastRow1)
End With
'search data array
For i = LBound(DataArray, 1) To UBound(DataArray, 1)
SourceStr = DataArray(i, 5)
'search array with your strings
For j = LBound(StringArray) To UBound(StringArray)
SearchStr = StringArray(j, 1)
If InStr(1, SourceStr, SearchStr) > 0 Then
'if match is found increase counter in array
StringArray(j, 2) = StringArray(j, 2) + 1
'you can add exit for here if you want only first match
End If
Next j
Next i
For i = LBound(StringArray) To UBound(StringArray)
Debug.Print StringArray(i, 1) & " - " & StringArray(i, 2)
Next i
End Sub
I think the main task is being over-complicated.
To check how many times a string occurs within an array you could use a function like this:
Function OccurWithinArray(theArray As Variant, stringToCount As String) As Long
Dim strArr As String
strArr = Join(theArray, " ")
OccurWithinArray = (Len(strArr) - Len(Replace(strArr, stringToCount, _
vbNullString, , , vbTextCompare))) / Len(stringToCount)
End Function
...and a demonstration:
Sub Demo()
Dim test(1 To 3) As String
test(1) = "I work at the Dog Pound."
test(2) = "I eat dogfish regularly."
test(3) = "Steroidogenesis is a thing."
Debug.Print OccurWithinArray(test, "dog")
End Sub
How it works:
Join joins all the elements of the array into one big string.
Len returns the length of the text.
Replace temporarily replaces the removes all occurrences of the search term.
Len returns the "modified" length of the text.
The difference between the two Len's, divided by the length of the string being searched for, is the number aof occurrences of the string within the entire array.
This returns 3 since the search is case-insensitive.
To make the search case-sensitive, remove the word vbTextCompare (in which case this example would return 2.)

array references in a vba loop?

OK... second attempt at posting this. Trying to make my question make sense please be patient with me, I am new at all of this.
The code below words PERFECTLY. It reads from the Range in column A and give me a string of all the cells where the searched numbers occur.
My only issue is its obviously not condensed at all. I have tried loops and arrays for the references but i can't seem to get it work. i would prefer something along the lines of reference(1-10) in stead of ref1, ref2, ref3. i had them all ref(i) with a loop around the whole macro but i ended up getting no results and an endless loop. any help to make this more streamlined would be appreciated. i have the full code if that will help anyone. I only put three iterations of the code it simply continues on with more references. up to 9
Option Explicit
Sub FindAircraftHourly()
Dim Findarray() As Integer 'the array to put numbers in
Dim count1, count2, count3, count4, count5, count6, count7, count8, count9 As Integer 'this will count the number of records you will need for your array
Dim n1, n2, n3, n4, n5, n6, n7, n8, n9 As Integer 'this will be what gets you through your For loops
Dim Searchvalue1, Searchvalue2, Searchvalue3, Searchvalue4, Searchvalue5, Searchvalue6, Searchvalue7, Searchvalue8, Searchvalue9 As Integer 'this is the number to look for
Dim Foundstring1, Foundstring2, foundstring3, foundstring4, foundstring5, foundstring6, foundstring7, Foundstring8, Foundstring9 As String 'this will build where the numbers are found
Dim k1 As Integer
Dim data As Worksheet
Set data = ThisWorkbook.Sheets("data")
If Cells(1, 3) <> "" Then 'test to make sure it has something to look for
Searchvalue1 = Cells(1, 3) * 1 'E18 is the search value for the function this converts it to number and not text
Else
MsgBox "No input found!", vbCritical 'if blank, give a warning and quit
GoTo the_end
End If
k1 = data.Range("A5000").End(xlUp).Row
count1 = 0 'always initialize your variables
For n1 = 1 To k1 'count through all the rows to find the end of the list
If Cells(n1, 1) <> "" Then 'is the cell blank or not?
count1 = count1 + 1 'if not blank, then increase the count
Else
Exit For 'if it's blank, stop looking and counting
End If
Next n1
ReDim Findarray1(0 To count1 - 1) As Integer 'array indexes start at 0, so subtract 1 to get the right number of elements
For n1 = 0 To UBound(Findarray1) 'go from the first element to the last of the array
Findarray1(n1) = Cells(n1 + 1, 1) 'load the array change the last number in the cells box to change where to start looking
Next n1
For n1 = 0 To UBound(Findarray1)
If Findarray1(n1) = Searchvalue1 Then
If Foundstring1 = "" Then 'if this is the first time found, create the string
Foundstring1 = n1 + 1
Else
Foundstring1 = Foundstring1 & ", " & n1 + 1 'if this is the second time the number is found, add a comma and continue to build the string.
End If
End If
Next n1
If Foundstring1 = "" Then
MsgBox "Not found", vbCritical 'msgbox nothing was found
Else
Cells(1, 4) = Foundstring1 'output the string to a certain cell
End If
'-----------------------------------------------------------------------
If Cells(2, 3) <> "" Then 'test to make sure it has something to look for
Searchvalue2 = Cells(2, 3) * 1 'E18 is the search value for the function this converts it to number and not text
Else
MsgBox "No input found!", vbCritical 'if blank, give a warning and quit
GoTo the_end
End If
k1 = data.Range("A5000").End(xlUp).Row
count2 = 0 'always initialize your variables
For n2 = 1 To k1 'count through all the rows to find the end of the list
If Cells(n2, 1) <> "" Then 'is the cell blank or not?
count2 = count2 + 1 'if not blank, then increase the count
Else
Exit For 'if it's blank, stop looking and counting
End If
Next n2
ReDim FindArray2(0 To count2 - 1) As Integer 'array indexes start at 0, so subtract 1 to get the right number of elements
For n2 = 0 To UBound(FindArray2) 'go from the first element to the last of the array
FindArray2(n2) = Cells(n2 + 1, 1) 'load the array change the last number in the cells box to change where to start looking
Next n2
For n2 = 0 To UBound(FindArray2)
If FindArray2(n2) = Searchvalue2 Then
If Foundstring2 = "" Then 'if this is the first time found, create the string
Foundstring2 = n2 + 1
Else
Foundstring2 = Foundstring2 & ", " & n2 + 1 'if this is the second time the number is found, add a comma and continue to build the string.
End If
End If
Next n2
If Foundstring2 = "" Then
MsgBox "Not found", vbCritical 'msgbox nothing was found
Else
Cells(2, 4) = Foundstring2 'output the string to a certain cell
End If
'-----------------------------------------------------------
If Cells(3, 3) <> "" Then 'test to make sure it has something to look for
Searchvalue3 = Cells(3, 3) * 1 'E18 is the search value for the function this converts it to number and not text
Else
MsgBox "No input found!", vbCritical 'if blank, give a warning and quit
GoTo the_end
End If
k1 = data.Range("A5000").End(xlUp).Row
count3 = 0 'always initialize your variables
For n3 = 1 To k1 'count through all the rows to find the end of the list
If Cells(n3, 1) <> "" Then 'is the cell blank or not?
count3 = count3 + 1 'if not blank, then increase the count
Else
Exit For 'if it's blank, stop looking and counting
End If
Next n3
ReDim FindArray3(0 To count3 - 1) As Integer 'array indexes start at 0, so subtract 1 to get the right number of elements
For n3 = 0 To UBound(FindArray3) 'go from the first element to the last of the array
FindArray3(n3) = Cells(n3 + 1, 1) 'load the array change the last number in the cells box to change where to start looking
Next n3
For n3 = 0 To UBound(FindArray3)
If FindArray3(n3) = Searchvalue3 Then
If foundstring3 = "" Then 'if this is the first time found, create the string
foundstring3 = n3 + 1
Else
foundstring3 = foundstring3 & ", " & n3 + 1 'if this is the second time the number is found, add a comma and continue to build the string.
End If
End If
Next n3
If foundstring3 = "" Then
MsgBox "Not found", vbCritical 'msgbox nothing was found
Else
Cells(3, 4) = foundstring3 'output the string to a certain cell
End If
the_end: 'end marker
End Sub
I'm not sure what your data looks like, but I would suggest a few things:
'finds the absolute last row with data in it // ignores empty cells
k1 = data.UsedRange.Rows.Count
count1 = 0
For n1 = 1 To k1
'here's a built-in function to help you check empty cells.
'if you're going to reference ranges, then you need to define on which worksheet
'you're referencing the ranges from. if you work with multiple worksheets, you
'can't simply use Cells(x,y); you need to use worksheet.Cells(x,y).
'If you want to access a cell's value, then you use the .Value property.
If Not IsEmpty(data.Cells(n1, 1).Value) Then
count1 = count1 + 1
Else
Exit For
End If
Next n1
I highly suggest using Collections instead of arrays because, for me, they're far easier to deal-with than arrays. They also start their indices at 1, so if you have a collection of 10 items, you count 1 through 10 instead of 0 through 9.
Defining and setting Collections:
Dim FindArray As New Collection
Or
Dim FindArray As Collection
Set FindArray = New Collection
Iterating over a collection:
Dim v As Variant
Dim sample As Double
For Each v In FindArray
'i personally like to recast all variant types to
'the intended data type
sample = v
'do your thing here
Next
Or
For i = 0 To FindArray.Count
'do your other thing here
Next
Here's my attempt at making your code cleaner; hope it helps!
PS: All my castings are based on assumptions, so change them to whatever type you were intending.
Main:
Dim wb As Workbook
Dim data As Worksheet
Dim n As Long
Dim SearchValue As Double
Dim Found As String
Dim output As New Collection
Dim FindArray As Collection
Dim count As Long
Dim k As Long
Dim i As Long
Dim j As Long
Set wb = Excel.Application.ThisWorkbook
Set data = wb.Worksheets("data")
k = data.UsedRange.Rows.Count
'find the last row before an empty record appears
For n = 1 To k
If Not IsEmpty(data.Cells(n, 1).Value) Then
count = n
ElseIf IsEmpty(data.Cells(n ,1).Value) Then
Exit For
End If
Next
'do the thing 9 times
For n = 1 To 9
If Not IsEmpty(data.Cells(n, 3).Value) Then
'cast the cell value to double and assign it to searchvalue
SearchValue = CDbl(data.Cells(n, 3).Value)
'i prefer to be extremely specific, but that's just me
Elseif IsEmpty(data.Cells(n, 3).Value) Then
MsgBox "No input found!", vbCritical
GoTo the_end
End If
'reset findarray to an empty collection
Set FindArray = New Collection
For i = 1 To count
'load your collection with the sample values casted to doubles
FindArray.Add CDbl(data.Cells(i, 1).Value)
Next
j = 1
Found = ""
For i = 1 To FindArray.Count
If FindArray(i) = SearchValue Then
If j = 1 Then
Found = i
ElseIf j > 1 Then
'im not sure what you're trying to accomplish with this line,
'so im just going to leave it as is and adjust it for my solution
'so it gives you the same output
Found = n & ", " & i
End If
j = j + 1
End If
Next
If Found = "" Then
MsgBox "Not found", vbCritical
Else
output.Add Found
End If
'i like to always just set the collection to nothing before a new iteration
Set FindArray = Nothing
Next
'dump all results in column 4
For i = 1 To output.Count
data.Cells(i, 4).Value = output(i)
Next
'garbage collection
Set FindArray = Nothing
Set output = Nothing
Set wb = Nothing
Set data = Nothing
the_end:
Exit Sub
I'm not entirely sure if this attempt at a solution will work for you, but I hope it's helpful in any way. Good luck!
Col A populated with values, Col C populated with search values to be found in Col A, Col D is where the row numbers of each value in Col C can be found.
The number of values in Col A and Col C are essentially limited only by the number of rows in your spreadsheet with no changes required to the code.
If you have not added a Dictionary reference, click on [Tools] at the top of the screen, click on [References...], scroll way down until you find [Microsoft Scripting Runtime], click the check mark next to that, then click [OK].
Sub FindAircraftHourly()
Dim dnySource As New Scripting.Dictionary
Dim strCellValue As String
Dim i0 As Long
With ActiveSheet
For i0 = 2 To .Rows.Count 'This starts at row 2 and goes to the end of the list. I'm assuming row 1 has a heading of some sort.
If Not IsEmpty(.Cells(i0, 1)) Then 'If the cell in col A of that row has a value, add that value to the dictionary as the key and the row number(s) as the items(s).
strCellValue = .Cells(i0, 1).Value
dnySource(strCellValue) = dnySource(strCellValue) & i0 & ", "
Else
Exit For 'Once you hit an empty cell, stop building the dictionary.
End If
Next i0
For i0 = 2 To .Rows.Count
If Not IsEmpty(.Cells(i0, 3)) Then 'If the cell in col C has a value then look through the dictionary to see if you have a list of row numbers to display.
strCellValue = .Cells(i0, 3).Value
If dnySource.Exists(strCellValue) Then 'If there is a list of row numbers for that value, display them in col D.
.Cells(i0, 4).Value = Left$(dnySource(strCellValue), Len(dnySource(strCellValue)) - 2) 'The - 2 here chops off the extra ", " at the end
Else 'Otherwise, indicate that the value in col C doesn't exist in col A.
.Cells(i0, 4).Value = "Value Not Found"
End If
Else
Exit For
End If
Next i0
End With
End Sub
Here's my attempt to solve your problem.
For questions just ask.
Option Explicit
Sub FindAircraftHourly()
Dim data As Worksheet
Dim searchrange As Range
Dim cell As Range
Dim findarray() As String
Dim searcharray() As Integer
Dim i As Integer
Set data = ThisWorkbook.Worksheets("data") 'the sheet your working on
Const maxinputvals As Integer = 3 'The number of search values you want to be looking for
ReDim searcharray(1 To maxinputvals) As Integer 'VBA doesn't need to start at 0
ReDim findarray(1 To maxinputvals) As String 'VBA doesn't need to start at 0
i = 0 'not needed but always nice to make sure
Do
i = i + 1 'add 1 to the count-var
If i > maxinputvals Then Exit Do 'if the count-var si bigger than max number of searchvalues quit the loop
If data.Cells(i, 3) <> "" Then 'check if cells is not empty
searcharray(i) = data.Cells(i, 3).Value 'safe the searchvalue
Else
MsgBox "Searchvalue #" & i & " is missing." 'exit the sub if a searchvalue is missing
Exit Sub
End If
Loop
Set searchrange = data.Range("A1:A5000")
For Each cell In searchrange 'go through the whole range
If cell.Value <> "" Then 'check is cell is empty
For i = 1 To maxinputvals 'if not, check if match with one of the searchvals
If cell.Value = searcharray(i) Then
findarray(i) = findarray(i) & ", " & cell.Row 'append your findarray string
End If
Next i
Else
Exit For 'if cell is empty exit the loop
End If
Next cell
For i = 1 To maxinputvals
findarray(i) = Mid(findarray(i), 3) 'delete the first ", "
data.Cells(i, 4) = findarray(i) 'output the findstring
Next i
End Sub

VBA Compare 2 arrays, write unique values to cell with comma delimiter

I have a series of 2 cells in which values are separated by a comma delimiter.
Example
Cell D1 = 1,2,3,4,5,6,7,8,9,10
Cell O1 = 1,2,3,4,5,6
I want to first use the split function to pass the values to an Array and subsequently compare those 2 Arrays to find out the unique/not double values.
These values then i want to write to another cell as values with a comma delimiter.
Based on this answer
Comparing two Dimension array
and something I found about adding values to an Array i tried my luck with this code
Sub compare()
Dim cont As Long
Dim x As Long
Dim y As Long
Dim Source As Variant
Dim Comparison As Variant
Dim Target As Variant
With ThisWorkbook.Worksheets("Open items")
For cont = 1 To .Cells(Rows.Count, 4).End(xlUp).Row
Source = Split(.Range("D" & cont).Value, ",")
Comparison = Split(.Range("O" & cont).Value, ",")
For x = LBound(Source) To UBound(Source)
For y = LBound(Comparison) To UBound(Comparison)
If Source(x, y) = !Comparison(x, y) Then
Target(UBound(Target)) = Source(x, y).Value
Next
Next
Next cont
End Sub
But seem to be stuck.
Is this the correct way to add a value to the Array Target?
How do I get the Array into the cell?
The result in my example should be for Target to contain "7", "8", "9" , and "10" and should be shown in a cell in the way
7,8,9,10
Thank you for your help!
Some issues:
Rows.Count will look in the active sheet, not necessarily in the "Open items" sheet. So you need to add the dot: .Rows.Count
Source(x, y) will not work, since Source only has one dimension. In fact y has nothing to do with Source. A similar remark holds for Comparison.
= ! is not a valid comparison operator. You maybe intended <>.
Target is not defined, and Target(UBound(Target)) will always refer to the same location. Instead, you could append the result to a string variable immediately.
Furthermore, I would use a Collection object for fast look up, so that the algorithm is not O(n²), but O(n):
Sub Compare()
Dim cont As Long
Dim source As Variant
Dim comparison As Variant
Dim part As Variant
Dim parts As Collection
Dim result As String
With ThisWorkbook.Worksheets("Open items")
For cont = 1 To .Cells(.Rows.Count, 4).End(xlUp).Row
source = Split(.Range("D" & cont).Value, ",")
comparison = Split(.Range("O" & cont).Value, ",")
' Add the source items in a collection for faster look-up
Set parts = New Collection
For Each part In source
parts.Add Trim(part), Trim(part)
Next
' Remove the comparison items from the collection
For Each part In comparison
On Error Resume Next ' Ignore error when part is not in parts
parts.Remove Trim(part)
If Err Then parts.Add Trim(part), Trim(part) ' Add part if not yet in parts
On Error GoTo 0 ' Stop ignoring errors
Next
' Turn the remaining collection to comma-separated string
result = ""
For Each part In parts
result = result & ", " & part
Next
result = Mid(result, 3) ' Remove first comma and space
' Store the result somewhere, for example in the E column
.Range("E" & cont).Value = result
Next cont
End With
End Sub
Alternative for Sorted Lists
When your source and comparison lists are sorted in numerical order, and you need the target to maintain that sort order, you could use a tandem-kind of iteration, like this:
Sub Compare()
Dim cont As Long
Dim source As Variant
Dim comparison As Variant
Dim x As Long
Dim y As Long
Dim result As String
With ThisWorkbook.Worksheets("Open items")
For cont = 1 To .Cells(.Rows.Count, 4).End(xlUp).Row
source = Split(.Range("D" & cont).Value, ",")
comparison = Split(.Range("O" & cont).Value, ",")
x = LBound(source)
y = LBound(comparison)
result = ""
Do While x <= UBound(source) And y <= UBound(comparison)
If Val(source(x)) < Val(comparison(y)) Then
result = result & ", " & Trim(source(x))
x = x + 1
ElseIf Val(source(x)) > Val(comparison(y)) Then
result = result & ", " & Trim(comparison(y))
y = y + 1
Else
x = x + 1
y = y + 1
End If
Loop
' Flush the remainder of either source or comparison
Do While x <= UBound(source)
result = result & ", " & Trim(source(x))
x = x + 1
Loop
Do While y <= UBound(comparison)
result = result & ", " & Trim(comparison(y))
y = y + 1
Loop
result = Mid(result, 3) ' Remove first comma and space
' Store the result somewhere, for example in the E column
.Range("E" & cont).Value = result
Next cont
End With
End Sub
Try this small UDF():
Public Function unikue(BigString As String, LittleString As String) As String
Dim B As Variant, L As Variant, Barr, Larr
Dim Good As Boolean
Barr = Split(BigString, ",")
Larr = Split(LittleString, ",")
For Each B In Barr
Good = True
For Each L In Larr
If L = B Then Good = False
Next
If Good Then unikue = unikue & "," & B
Next B
If unikue <> "" Then unikue = Mid(unikue, 2)
End Function
Couple of things with this code
the variable Target() - You never tell code how big this array is or if you want to make it bigger - my full code below will grow for each match that is found
Source(x, y).Value - You dont need to use Value for arrays. you also do not need x and y as you are only reading in one column you only need source(x)
Where I have wrote MISSING in the full code - these lines where missing and would have caused you issues.
The purpose of Found is that for every time source(x) is found in Comparison(y) then Found is incremented. If it has never been incremented then we can assume that it is to be captured in target.
One other note is that you do not specify where you want to output Target to. so currently the target array does not go anywhere
Sub compare()
Dim cont As Long
Dim x As Long
Dim y As Long
Dim Source As Variant
Dim Comparison As Variant
Dim Target() As Variant
ReDim Target(1)
With ThisWorkbook.Worksheets("Open items")
For cont = 1 To .Cells(.Rows.Count, 4).End(xlUp).Row
Source = Split(.Range("D" & cont).Value, ",")
Comparison = Split(.Range("O" & cont).Value, ",")
For x = LBound(Source) To UBound(Source)
Found = 0
For y = LBound(Comparison) To UBound(Comparison)
If Source(x) = Comparison(y) Then
Found = Found + 1
'count if found
End If 'MISSING
Next
'if values are found dont add to target
If Found = 0 Then
Target(UBound(Target)) = Source(x)
ReDim Preserve Target(UBound(Target) + 1)
End If
Next
Next cont
End With 'MISSING
End Sub

Checking for duplicate substrings in Excel

I am trying to find a way to compare the first significant words in each cell with first significant words in the next cell, and if the first significant words matches, it removes the second entry. For example, the starting data can look like this:
General Electric
General Electric Inc
General Electric Company
Microsoft
Microsoft Corporation
Microsoft Servers
Nintendo
Nintendo Enterprises
And the result should end up looking like this:
General Electric
Microsoft
Nintendo
So far, I have this code set up that traverses the column of data:
Sub CompanyNameConsolidate()
Dim companyName As String
Dim companyArray() As String
Dim companyName2 As String
Dim companyArray2() As String
Dim totalArray() As String
Dim wordCount As Integer
Dim i As Integer
Dim r As Range
With Sheets("Unassigned")
Range("B1").Select
Do Until IsEmpty(ActiveCell)
companyName = Range("B" & ActiveCell.Row).Text
companyName2 = ActiveCell.Offset(1, 0).Text
companyArray = Split(companyName, " ")
companyArray2 = Split(companyName2, " ")
wordCount = UBound(companyArray) - LBound(companyArray)
For i = 0 To wordCount
If companyArray(i) = companyArray2(i) Then
[*********HELP**********]
Next
ActiveCell.Offset(1, 0).Select
Loop
End With
End Sub
Basically, the code above compares the substrings in each cell with the substrings in the next cell. Unfortunately, that's as far as I have gotten.
The tricky thing is that some company names can have two words (General Electric) and others can have only one word (Microsoft).
You can assume that the list will be sorted in alphabetical order, so the shortest name (the name I want to keep) will always be on top.
I have over 16,000 entries to go through and fix, so I absolutely must have an automated way of doing it!
1st variant using rows deletion:
Sub test()
Dim Dic As Object: Set Dic = CreateObject("Scripting.Dictionary")
Dim x&, cl As Range, DataRange As Range, k1, k2
Dic.comparemode = vbTextCompare
With Sheets("Unassigned")
Set DataRange = .[B1].Resize(.Cells(Rows.Count, "B").End(xlUp).Row, 2)
x = 1
For Each cl In DataRange
If cl.Value <> "" Then
Dic.Add x, cl.Value
x = x + 1
End If
Next cl
For Each k1 In Dic
For Each k2 In Dic
If IsNumeric(k1) And IsNumeric(k2) Then
If Dic(k2) Like Dic(k1) + "*" And k2 > k1 Then
Dic.Remove (k2)
End If
If Not Dic.exists(Dic(k1)) Then Dic.Add Dic(k1), Nothing
End If
Next k2, k1
x = Split(DataRange.Address, "$")(4)
While x <> 0
If Not Dic.exists(.Cells(x, "B").Value) Then .Rows(x).Delete
x = x - 1
Wend
End With
End Sub
2nd variant using Workbook.Add:
Sub test2()
Dim Dic As Object: Set Dic = CreateObject("Scripting.Dictionary")
Dim x&, cl As Range, DataRange As Range, k1, k2
Dic.comparemode = vbTextCompare
With Sheets("Unassigned")
Set DataRange = .[B1].Resize(.Cells(Rows.Count, "B").End(xlUp).Row, 2)
x = 1
For Each cl In DataRange
If cl.Value <> "" Then
Dic.Add x, cl.Value
x = x + 1
End If
Next cl
For Each k1 In Dic
For Each k2 In Dic
If Dic(k2) Like Dic(k1) + "*" And k2 > k1 Then
Dic.Remove (k2)
End If
Next k2, k1
End With
Workbooks.Add
x = 1
For Each k1 In Dic
Cells(x, 2) = Dic(k1)
x = x + 1
Next k1
End Sub
test for both variants
before: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~> after:
Find a common demoninator for your company names, according to your examples this appears to just remove the last word, if its greater than 1 word.
Dim listOfCompanies As New Collection
Dim companyName As String
Dim companyArray As Variant
Dim item As Variant
Dim i as Integer, j As Integer
'The 2 denotes column B, where i denotes the row
'You can change this outter loop to your specific needs, this one just processes the first column B1, to when it encounters a blank row
while(ThisWorkbook.Worksheets("Unassigned").Cells(i, 2).Value <> "")
companyName = ThisWorkbook.Worksheets("Unassigned").Cells(i, 2).Value
companyArray = Split(companyName, " ")
companyName = ""
'This truncates the last word off
for j = 0 to UBound(companyArray) - 1
companyName = companyName + companyArray(j) + " "
next j
'Trim off the last space character
companyName = Trim(companyName)
'Now Add your companyName string to a Dictionary Object
'VBA will throw an error if a duplicate gets added, but this is okay and we can continue processing
On Error Resume Next
listOfCompanies.Add(companyName)
On Error Goto 0 'This resets the handler in case an error occurs somewhere else unexpectedly
i = i + 1
wend
'Now we can do a ForEach and spit out the entire 'unique list'
For Each item in listOfCompanies
'Your code here
Next item

Converting an Excel list according to row indexes

I'm trying to convert a list in Excel VBA as follows:
My original list in the one colored in grey. It shows a sequence.
I want to generate the list on the right according to each number location.
For example:
3 is second in the left list so 2 is on the third location in the right list;
6 is fourth in the left list so 4 is on the sixth location in the right list ...
I tried using 'For' loops in VBA, but it's getting a bit long and complex, is there a way to do it by using arrays in VBA?
A formula can easily achieve this. Assuming data is in A1:A8, in B1 and copied down:
=MATCH(ROW(),A$1:A$8,0)
This will work, just set the first, last and ranges as needed.
Private Sub cbSort_Click()
Dim wArray As Variant, dArray As Variant
Dim first As Integer, last As Integer
Dim i As Integer, j As Integer
first = 1
last = 8
Set wArray = Range("A" & first & ":A" & last)
ReDim dArray(1 To last - first + 1, 1 To 1)
j = 1
For i = first To last
dArray(wArray(i, 1), 1) = j
j = j + 1
Next i
Range("B" & first & ":B" & last) = dArray
End Sub
Option Explicit
Sub Main()
Dim source As Range
On Error GoTo ErrTransformIt
Set source = Application.InputBox(prompt:="Source", Type:=8)
TransformIt source
Exit Sub
ErrTransformIt:
MsgBox Err.Description
End Sub
Private Sub TransformIt(ByVal source As Range)
Dim target As Range
Dim c As Range
Dim i As Integer
Dim firstRow As Long
firstRow = source(1).Row
i = 1
For Each c In source.Cells
Set target = ActiveSheet.Cells(firstRow + c.Value - 1, c.Column + 1)
If target.Value <> "" Then
MsgBox "Target is already used by [" & target.Value & "]", vbExclamation
Exit Sub
End If
target.Value = i
i = i + 1
Next c
End Sub

Resources