Parse Multiple Arrays and write all possible combinations - arrays

I am trying to use VBA in Excel to write all of the possible combinations of the contents of three arrays to a column to create a wordlist.
Currently, I know how to loop through the arrays and get some output that I want but I can't figure out how to build the loop to give me all possible combinations of the baseWord(n) & numberCharSet(n) & specialCharSet(n).
How do I properly loop through the baseWord array to get all combinations of each baseWord with the contents of the specialCharSet and numberCharSet arrays?
Example:
Cloud1!
Cloud1#
Cloud1#
Cloud1$
Cloud2!
etc...
Private Sub createWordlist()
Dim baseWord(1 To 2) As String
baseWord(1) = "Cloud"
baseWord(2) = "cloud"
Dim numberCharSet(1 To 4) As String
numberCharSet(1) = "1"
numberCharSet(2) = "2"
numberCharSet(3) = "3"
numberCharSet(4) = "4"
Dim specialCharSet(1 To 4) As String
specialCharSet(1) = "!"
specialCharSet(2) = "#"
specialCharSet(3) = "#"
specialCharSet(4) = "$"
x = 1
y = 1
z = 4
w = 1
For Each Item In baseWord
Range("A" & x).Value = baseWord(w) & numberCharSet(y) & specialCharSet(z)
x = x + 1
y = y + 1
z = z - 1
Next
End Sub

As #ScottCraner mentioned in the comments, all you need to do is nest 3 loops:
For Each word In baseWord
For Each num In numberCharSet
For Each special In specialCharSet
Debug.Print word & num & special
Next
Next
Next

Related

Spiting 2D Arrays using "vbnewline" Visual Basic

I have a hard-coded 2d array that prints to a text box in visual basic. However I am unable to split the lines between each row. This is an extract from the array:
stpeople(0, 0) = "Bob"
stpeople(1, 0) = "Last"
stpeople(0, 2) = "Jamie"
stpeople(1, 2) = "Smart"
This will show as one line like so: "Bob Last Jamie Smart"
this is the code that I am using to display the array am I using vbNewLine incorrectly? I am using visual basic.net.
Dim stoutput As String
For y As Integer = 0 To 9
For x As Integer = 0 To 4
stoutput = stoutput & stpeople(x, y) & " "
Next
stoutput = stoutput & vbNewLine
Next
ListArray.Items.Add(stoutput)
I don't know what type is ListArray but I'll try to guess. Sometime, these things don't display new lines, you would need to add each line to the Items.
Dim stoutput As String
For y As Integer = 0 To 9
stoutput = ""
For x As Integer = 0 To 4
stoutput = stoutput & stpeople(x, y) & " "
Next
ListArray.Items.Add(stoutput)
Next
Your code should work with textbox, if not, it mean you didn't set them to support multiline.

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

Grouping Values by Unique Index in a String Array

How can I retrieve only unique array of this example.
"58|270,58|271,58|272,59|270,59|271,59|272"
I want this array to be stored like :
"58,270,271,272|59,270,271,272"
Can someone help me in ASP classic or VB script
This isn't a straight forward problem I found myself thinking about it for a few minutes before I finally thought of a way of doing it.
To produce the output from the input specified requires some sort of custom de-serialise / serialise approach. The code below creates a 2D array that will contain the unique indexes (58, 59 etc.) and populate them with a comma delimited list of the associated values (done it like this to make the serialise easy).
Structure wise it will look something like this when de-serialised
----- Array Debug ------
data(0, 0) = 58
data(1, 0) = 270,271,272
data(0, 1) = 59
data(1, 1) = 270,271,272
We then use that as the basis to build the serialised string in the format required.
'Function takes string input in the form <index>|<value>, ... extracts
'them into a 2D array groups duplicate indexes together.
Function DeserialiseToCustomArray(str)
Dim a1, a2, x, y, idx
If Len(str & "") > 0 Then
a1 = Split(str, ",")
ReDim data(1, 0)
For x = 0 To UBound(a1)
a2 = Split(a1(x), "|")
If IsArray(data) Then
idx = -1
'Check for duplicates
For y = 0 To UBound(data, 2)
If data(0, y) = a2(0) Or IsEmpty(data(0, y)) Then
idx = y
Exit For
End If
Next
'No duplicate found need to add a new element to the array.
If idx = -1 Then
idx = UBound(data, 2) + 1
ReDim Preserve data(1, idx)
End If
data(0, idx) = a2(0)
If IsEmpty(data(1, idx)) Then
data(1, idx) = a2(1)
Else
data(1, idx) = Join(Array(data(1, idx), a2(1)), ",")
End If
End If
Next
End If
DeserialiseToCustomArray = data
End Function
'Function takes a 2D array built from DeserialiseToCustomArray() and
'serialises it into a custom string in the form <index>,<value>, ... | ...
Function SerialiseArray(data)
Dim x, y
Dim str: str = Empty
If IsArray(data) Then
For y = 0 To UBound(data, 2)
If y > 0 And y <= UBound(data, 2) Then str = str & "|"
str = str & data(0, y) & "," & data(1, y)
Next
End If
SerialiseArray = str
End Function
Couple examples of usage:
Dim str: str = "58|270,58|271,58|272,59|270,59|271,59|272"
Dim data, result
data = DeserialiseToCustomArray(str)
result = SerialiseArray(data)
WScript.Echo "input: " & str
WScript.Echo "output: " & result
Output:
Result: 58,270,271,272|59,270,271,272
Dim str: str = "58|270,58|271,58|272,59|270,59|271,59|272,60|345,61|345,58|270,60|200"
Dim data, result
data = DeserialiseToCustomArray(str)
result = SerialiseArray(data)
WScript.Echo "input: " & str
WScript.Echo "output: " & result
Output:
Result: 58,270,271,272,270|59,270,271,272|60,345,200|61,345
Note: If using these examples in Classic ASP remove the WScript.Echo and replace with Response.Write.
A common way to get unique items from an array is to put them as keys into a Dictionary:
a = Array(58, 270, 271, 272, 270, 271, 272)
Set d = CreateObject("Scripting.Dictionary")
For Each i In a
d(i) = True 'value can be anything, relevant is using i as key
Next
WScript.Echo Join(d.Keys, ",") 'Output: 58,270,271,272

Add range of data/cells in dynamic multidimensional array vba

I would like to be able to add some range of data in a dynamic multidimensional array without using a double loop that screens each element of the array. But I don't know if it is possible. By double loop, I mean such a code (this is only an example):
Dim Films(1 To 5, 1 To 2) As String
Dim i As Integer, j As Integer
For i = 1 To 5
For j = 1 To 2
Films(i, j) = Cells(i, j).Value
Next j
Next i
I am using VBA 2010. I know how many rows my array has, but the number of columns is variable.
Here is my code :
Sub DRS(Item)
'item is a name to search for in a specific range
Dim SrcRange() As Variant
Dim cell3 As Range
Dim n As Integer, m As Integer
SrcRange() = Array()
ReDim SrcRange(45, 0)
m = -1
n = 0
With Sheets("X")
For Each cell3 In .Range("I13:AG" & .Cells(1, Columns.Count).End(xlToRight).Column)
'the range ("I13:AG...") contains names, and some will match with "item"
m = m + 1
If Len(cell3.Value) > 0 And cell3 = Item Then
SrcRange(0, n) = .Range(m + 8 & "30:" & m + 8 & "75")
'the previous line **should** add a whole range of cells (which contain numbers, one by cell) in a colum of the array, but this is the line that doesn't work.
n = n + 1
ReDim Preserve SrcRange(UBound(SrcRange), n)
End If
Next cell3
End With
End Sub
I already tried those::
SrcRange(:, n) = .Range(m + 8 & "30:" & m + 8 & "75")
SrcRange(0:45, n) = .Range(m + 8 & "30:" & m + 8 & "75")
SrcRange(, n) = .Range(m + 8 & "30:" & m + 8 & "75")
but no one worked.
Is there a way or a formula that would allow me to add a full range of cells to each column of the array, or am I obliged to use a double loop to add the elements one by one?
I'm guessing that this Range...
.Range("I13:AG" & .Cells(1, Columns.Count).End(xlToRight).Column)
...should actually be xlToLeft instead of xlToRight (xlToRight will always return I13:AG16384).
I'm also not entirely sure what the m + 8 & "30:" & m + 8 & "75" is supposed to be evaluating to, because you increment the variable m each time through the loop, and it gives you ranges like 930:975. I'll take a stab in the dark and assume that the m + 8 is supposed to be the column that you found the item in.
That said, the .Value property of a Range object will just give you a 2 dimensional array. There isn't really any reason to build an array - just build a range and then worry about getting the array out of it when you're done. To consolidate the range (you only get the first area if you grab its Value), just copy and paste it to a temporary Worksheet, grab the array, then delete the new sheet.
Sub DRS(Item)
'item is a name to search for in a specific range
Dim SrcRange() As Variant
Dim found As Range
Dim cell3 As Range
With Sheets("X")
For Each cell3 In .Range("I13:AG" & .Cells(1, Columns.Count).End(xlToLeft).Column)
'the range ("I13:AG...") contains names, and some will match with "item"
If Len(cell3.Value) > 0 And cell3.Value = Item Then
If Not found Is Nothing Then
Set found = Union(.Range(.Cells(30, cell3.Column), .Cells(75, cell3.Column)), found)
Else
Set found = .Range(.Cells(30, cell3.Column), .Cells(75, cell3.Column))
End If
End If
Next cell3
End With
If Not found Is Nothing Then
Dim temp_sheet As Worksheet
Set temp_sheet = ActiveWorkbook.Sheets.Add
found.Copy
temp_sheet.Paste
SrcRange = temp_sheet.UsedRange.Value
Application.DisplayAlerts = False
temp_sheet.Delete
Application.DisplayAlerts = True
End If
End Sub

Adding to an array in VBA with strings as the index

Not sure I've labelled this correctly.
I have a bunch of cells containing strings of data. Each cell consists of something like this:
q1 = 1 | q2 = 3.2 | q3 = 5.6
q1 = 1.8 | q3 = 2.1 | q5 = 1.4
*Note: The delimiter is litteral, all that text is in a single cell, with a pipe char.
I want to loop through each cell, explode (to use the PHP term) by the pipe (|) delimiter, and then do so again by the = sign.
I want to create an array for each possible value to the left of the equal sign, and add the value found to the right to the array (not add as in sum, add as in append to the array).
Visually, I think the array should look something like this:
Vars[
q1 [ 1,1.8 ],
q2 [ 3.2 ],
q3 [ 5.6,2.1]....]
End goal being I'd like to get the average, mean and median for each of q1, q2 and q3.
Is this doable in VB? I'm more familiar with PHP, but would like to keep this in Excel.
Thanks.
This will handle an arbitrary number of "keys" (q1,q2, etc)
Sub Tester()
'needs a reference to microsoft scripting runtime
Dim d As New Scripting.dictionary
Dim c As Range
Dim arrP, arrE
Dim q, v, tmpV, tmpP, tmpArr, uB
Dim i As Long, n As Long
Dim k
For Each c In Selection.Cells
tmpV = Trim(c.Value)
If InStr(tmpV, "=") > 0 Then
arrP = Split(tmpV, "|") 'split on pipe
For i = LBound(arrP) To UBound(arrP)
tmpP = arrP(i)
If InStr(tmpP, "=") > 0 Then
q = Trim(Split(tmpP, "=")(0))
v = Trim(Split(tmpP, "=")(1))
If IsNumeric(v) Then
If Not d.exists(q) Then
d.Add q, Array(v)
Else
tmpArr = d(q) 'get dict value into temp array
uB = UBound(tmpArr) + 1
ReDim Preserve tmpArr(0 To uB) 'extend array
tmpArr(uB) = v
d(q) = tmpArr 'put back into dict
End If
End If
End If
Next
End If 'cell has at least one "="
Next c
'dump the dictionary to the immediate pane
For Each k In d.keys
Debug.Print k, Join(d(k), ",")
Next k
End Sub
It's complicated, but it can be done. I tested this in excel based on your cell input, putting them in A1 and A2:
q1 = 1 | q2 = 3.2 | q3 = 5.6
q1 = 1.8 | q3 = 2.1 | q5 = 1.4
I put together a macro in Excel called "Looper" that uses two loops to cycle through the cells in column A, split them at the '|' and search for each number value, convert it to a double and place it in the corresponding array.
Private Sub Looper()
''Loop Variables
Dim i, k As Integer
Dim MoveDown As String
''Variables to manipulate the string
Dim Selecter As String
Dim TotalCell As String
Dim Splitter As Variant
Dim strIncrement As String
''Array variables and counters
Dim q1(50) As Double
Dim q2(50) As Double
Dim q3(50) As Double
Dim qv1, qv2, qv3 As Integer
''Variables for finding the number in each increment
Dim Equals As Integer
Dim strNumber As String
Dim dblNumber As Double
''Set the array counters to 0
qv1 = 0
qv2 = 0
qv3 = 0
i = 0
Do Until MoveDown = "DONE"
Selector = "A" + Replace(Str(i), " ", "")
If Range(Selector).Value = "" Then
MoveDown = "DONE"
Else
TotalCell = Range(Selector).Value
Splitter = Split(TotalCell, "|")
For k = LBound(Splitter) To UBound(Splitter)
''strIncrement holds the data in between each |
strIncrement = Splitter(k)
''Remove any spaces
strIncrement = Replace(strIncrement, " ", "")
''Equals shows the location of the number (length of string - loc of =)
Equals = Len(strIncrement) - InStr(1, strIncrement, "=")
strNumber = Right(strIncrement, Equals)
dblNumber = CDbl(strNumber)
''Check for the array name and then add the data to the corresponding array
If InStr(1, strIncrement, "q1") > 0 Then
q1(qv1) = dblNumber
qv1 = qv1 + 1
Else
If InStr(1, strIncrement, "q2") > 0 Then
q2(qv2) = dblNumber
qv2 = qv2 + 1
Else
If InStr(1, strIncrement, "q3") > 0 Then
q3(qv3) = dblNumber
qv3 = qv3 + 1
End If
End If
End If
Next
End If
i = i + 1
Loop
End Sub
I was able to successfully add the data to the arrays, so it should be simple to go from there to calculate the means, etc.

Resources