I have about 15 Address that are formatted like, 1111 Really Cool Street, Sweet City, Awesome State I need to split them up into separate cells for their Street, City and State.
The problem I have currently is storing the Street,City and State. I've tried something along the lines..
Dim addressArray() As String
posRow = 1
Do
posRow = posRow + 1
addressFromCell= Sheet1.Cells(posRow , "C")
addressArray() = Split(addressFromCell, ",")
Sheet2.Cells(posRow , "A") = addressArray(0)
Sheet2.Cells(posRow , "B") = addressArray(1)
Sheet2.Cells(posRow , "C") = addressArray(2)
Loop Until posRow = 15
I thought addressArray would look like {"1111 Really Cool Street","Sweet City", "Awesome State"} and I could simply pass in the element I wanted. Instead I keep on getting
Run-time error '9': Subscript out of range
Any help would be great, thanks!
As Ken White pointed out, the reason for the error is that one of the values doesn't contain 2 commas.
Here is how I would do it:
Dim addressArray() As String
posRow = 1
Do
posRow = posRow + 1
addressArray() = Split(Sheet1.Cells(posRow, "C").Value, ",")
Sheet2.Cells(posRow, "A").Resize(1, UBound(addressArray) + 1).Value = addressArray()
Loop Until posRow = 15
This code uses the UBound function to retrieve the Upper Bound (number of the last element) for the Array and ensures that the Range is always the same size as the Array.
To avoid any such error, you should check if the cell is not blank and the address in the cell is not the complete address.
posRow = 1
Do
posRow = posRow + 1
addressFromCell = Sheet1.Cells(posRow, "C")
If addressFromCell <> "" Then
addressArray() = Split(addressFromCell, ",")
If UBound(addressArray) = 2 Then
Sheet2.Cells(posRow, "A") = addressArray(0)
Sheet2.Cells(posRow, "B") = addressArray(1)
Sheet2.Cells(posRow, "C") = addressArray(2)
End If
End If
Loop Until posRow = 15
Try,
Sub test()
Dim vArray
Dim vDB, vR()
Dim i As Long, r As Long, j As Integer
Dim k As Integer
Dim s, e
's = Timer
vDB = Range("c2", "c15")
r = UBound(vDB, 1)
ReDim vR(1 To r, 1 To 3)
For i = 1 To r
vArray = Split(vDB(i, 1), ",")
k = UBound(vArray)
If k > 0 And k < 3 Then
For j = 1 To k
vR(i, j) = vArray(j - 1)
Next j
End If
Next i
With Sheet2
.Range("a2").Resize(r, 3) = vR
End With
'e = Timer
'Debug.Print "Test() time : " & (e - s)
End Sub
Related
I have a table with two Columns Product and Price($).
Product
Price($)
A
100
B
400
C
350
D
50
E
515
F
140
I am trying to use vba to get combination of value of all products that will not exceed $500. I have been trying with this code and I am not sure how to proceed from this point on.
Sub getCombination()
Dim price As Long
Dim limit As Long
Dim i As Integer
Dim j As Integer
Dim combination As String
limit = 500
combination = ""
Range("B2").Activate
price = Range("B2").Value
For i = 1 To 6
For j = 1 To 6
If price <= limit Then
price = price + ActiveCell.Offset(j, 0).Value
combination = combination & ActiveCell.Offset(0, -1).Value & "," & ActiveCell.Offset(1, -1).Value
End If
Next j
Next i
ActiveCell.Offset(1, 0).Activate
MsgBox combination
End Sub
My Expected output is something like
A,B
A,C
A,C,D
B,D
C,F
A,D
C,D
(Please note: Not All output combinations are specified here!)
How should I proceed with the existing code? Or do I really have a better way for me to implement this?
Since the item can be used or not, that is a binary response. Using a binary number with the same number of digits as the number of items we can do all the combinations and do the testing:
Sub getCombination()
Dim rngArr As Variant
rngArr = ActiveSheet.Range("A2:B7")
Dim cnt As Long
cnt = 2 ^ UBound(rngArr, 1) - 1
Dim OutArray As Variant
ReDim OutArray(1 To cnt, 1 To 2)
Dim k As Long
k = 1
Dim i As Long
For i = 1 To cnt
Dim bin As String
bin = Application.Dec2Bin(i, UBound(rngArr, 1))
Dim delim As String
delim = ""
Dim j As Long
For j = 1 To UBound(rngArr, 1)
If Mid(bin, j, 1) = "1" Then
OutArray(k, 1) = OutArray(k, 1) & delim & rngArr(j, 1)
delim = ", "
OutArray(k, 2) = OutArray(k, 2) + rngArr(j, 2)
End If
Next j
If OutArray(k, 2) <= 500 Then
k = k + 1
Else
OutArray(k, 1) = ""
OutArray(k, 2) = 0
End If
Next i
Dim fnlarr As Variant
ReDim fnlarr(1 To k - 1)
For i = 1 To k - 1
fnlarr(i) = OutArray(i, 1)
Next i
Debug.Print Join(fnlarr, " | ")
End Sub
A separate program that I cannot change adds to a spreadsheet and sometimes it duplicates something.
For example:in cell 5, 3
ABC, vbd, S19M-0027757-27760, S19M-0027757-27760(1)
or it could be
ABC, vbd S19M-0027757-27760, S19M-0027757-27760(1)
What I need to do is replace both of them with S19M-0027757-27760(1) so the out come would be:
ABC, vbd, S19M-0027757-27760(1)
So far I have:
For i = 5 To lRow
inputArray = Split(Cells(i, 3).Value, " ")
For j = 0 To (UBound(inputArray) - LBound(inputArray) - 1)
Dim firstString As String
Dim secondString As String
firstString = inputArray(j)
secondString = inputArray(j + 1)
Next
Next
I am thinking the next step would be to compare letter by letter? But what about the comma and (1)?
Try this. Possibly not enough examples to be sure it will work in all cases, but a short test worked.
Sub x()
Dim i As Long, inputArray, j As Long, outputArray(), k As Long
For i = 1 To 3
inputArray = Split(Cells(i, 3).Value, ", ")
For j = LBound(inputArray) To UBound(inputArray)
k = k + 1
ReDim Preserve outputArray(1 To k)
If j = UBound(inputArray) - 1 Then
If inputArray(j + 1) Like inputArray(j) & "(*)" Then
outputArray(k) = inputArray(j + 1)
Exit For
Else
outputArray(k) = inputArray(j)
End If
Else
outputArray(k) = inputArray(j)
End If
Next j
Cells(i, 4).Value = Join(outputArray, ", ")
Erase outputArray: k = 0
Next i
End Sub
Some other way, possible through RegEx:
Sub Test()
Dim RegEx As Object: Set RegEx = CreateObject("VBScript.RegExp")
RegEx.Global = True
RegEx.Pattern = "([A-Z0-9-]{18})(?=.+\1)"
Dim lr As Long, x As Long
With Sheet1
lr = .Cells(.Rows.Count, 3).End(xlUp).Row
For x = 5 To lr
.Cells(x, 3).Value = Replace(Replace(RegEx.Replace(.Cells(x, 3).Value, ""), ", ,", ", "), " ,", ", ")
Next x
End With
End Sub
I agree with #SJR, some more examples would be great to know if the RegEx.Pattern would hold TRUE. I now went with the assumptions of 18-char patterns. It would hold for the current sample data:
Before:
After:
With this code I'm trying to search cells in a column where there is a comma character, and divide it into 2 new cells.
Next I want to Delete the original line, but it seems impossible as the value is used in FindNext operation.
What I have :
Column D Column E
Carrot Vegetable
Apple,Banana Fruit
What I need :
Column D Column E
Carrot Vegetable
Apple Fruit
Banana Fruit
What I've done :
Sub newentry()
'
' newentry Macro
'
Dim line
Dim col
Dim content
With Sheets("Feuil3").Columns("D")
Set c = .Find(",", LookIn:=xlValues)
If Not c Is Nothing Then
firstAddress = c.Address
Do
c.Select
line = ActiveCell.Row
col = ActiveCell.Column
content = ActiveCell
category = Cells(line, "E")
Dim Table() As String
Dim i As Integer
'split content in a table
Table = Split(content, ",")
'loop on table
For i = 0 To UBound(Table)
'copy result on next line
Rows(line + 1).Insert
Tableau(i) = Application.WorksheetFunction.Trim(Table(i))
Cells(line + 1, col).Value = Table(i)
Cells(line + 1, "E").Value = category
Next i
Set c = .FindNext(c)
If c Is Nothing Then
GoTo DoneFinding
End If
'where/how to do this ?
Rows(c.Row).Delete Shift:=xlUp
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
DoneFinding:
End With
End Sub
How can I delete the line that I just found ?
Thanks.
Say we have data in column D like:
Running this short macro:
Sub Restructure()
Dim N As Long, i As Long, j As Long
Dim arr1, arr2, arr3, a1, s As String
N = Cells(Rows.Count, "D").End(xlUp).Row
j = 1
arr1 = Range("D1:D" & N)
For Each a1 In arr1
s = Mid(a1, 2, Len(a1) - 2)
If InStr(s, ",") = 0 Then
Cells(j, "E").Value = "[" & s & "]"
j = j + 1
Else
arr2 = Split(s, ",")
For Each a2 In arr2
Cells(j, "E").Value = "[" & a2 & "]"
j = j + 1
Next a2
End If
Next a1
End Sub
will produce this in column E:
NOTE:
The original data is not disturbed.
insert as many lines as needed minus one below the found cell,
then simply write needed data including found cell row
don't rely on any ActiveCell, just use the c range object you found
Sub newentry()
'
' newentry Macro
'
Dim content As String, Category As String
Dim c As Range
Dim Table() As String
With Sheets("Feuil3").Columns("D")
Set c = .Find(",", LookIn:=xlValues)
If Not c Is Nothing Then
Do
content = c
Category = c.Offset(, 1).Value2
'split content in a table
Table = Split(content, ",")
c.Offset(1).EntireRow.Resize(UBound(Table)).Insert ' insert as many rows needed minus one below the found cell
c.Resize(UBound(Table) + 1).Value = Application.Transpose(Table) ' write contents in as many cells as needed, including the found one
c.Offset(, 1).Resize(UBound(Table) + 1).Value = Array(Category, Category) ' write category in as many cells as needed one column to the right of found one
Set c = .FindNext(c)
Loop While Not c Is Nothing
End If
End With
End Sub
Try this code
Sub Test()
Dim a, b, x, i As Long, j As Long, k As Long
a = Range("D1:E" & Cells(Rows.Count, 1).End(xlUp).Row).Value
ReDim b(1 To UBound(a) * 3, 1 To UBound(a, 2))
For i = LBound(a) To UBound(a)
If InStr(a(i, 1), ",") > 0 Then
x = Split(a(i, 1), ",")
For j = LBound(x) To UBound(x)
k = k + 1
b(k, 1) = Trim(x(j))
b(k, 2) = a(i, 2)
Next j
Else
k = k + 1
b(k, 1) = a(i, 1)
b(k, 2) = a(i, 2)
End If
Next i
Columns("D:E").ClearContents
Range("D1").Resize(k, UBound(b, 2)).Value = b
End Sub
Column A contains the labels or outcome value, Columns B-N contain varying lengths of comma separated values, but range for each column is the same (i.e., 1-64). The goal is to covert to a new table with Column A representing the value range (1-64) and Columns B-N the labels/outcome from the original table.
A semi-related solution was sought here, but without use of macros.
I will let you to modify this code,
Sub splitThem()
Dim i As Long, j As Long, k As Long, x As Long
x = 1
Sheets.Add.Name = "newsheet"
For j = 2 To Cells(1, Columns.Count).End(xlToLeft).Column
For i = 1 To Cells(Rows.Count, 2).End(xlUp).Row
If Cells(i, j) <> "" Then
For k = 1 To Len(Cells(i, j)) - Len(Replace(Cells(i, j), ",", "")) + 1
Sheets("newsheet").Cells(x, j) = Cells(i, 1)
x = x + 1
Next k
End If
Next i
x = 1
Next j
End Sub
Try this code.
Sub test()
Dim vDB, vR()
Dim vSplit, v As Variant
Dim Ws As Worksheet
Dim i As Long, n As Long, j As Integer, c As Integer
vDB = Range("a2").CurrentRegion
n = UBound(vDB, 1)
c = UBound(vDB, 2)
ReDim vR(1 To 64, 1 To c)
For i = 1 To 64
vR(i, 1) = i
Next i
For i = 2 To n
For j = 2 To c
vSplit = Split(vDB(i, j), ",")
For Each v In vSplit
vR(v, j) = vDB(i, 1)
Next v
Next j
Next i
Set Ws = Sheets.Add '<~~ replace your sheet : Sheets(2)
With Ws
For i = 1 To c
.Range("b1")(1, i) = "COND" & i
Next i
.Range("a2").Resize(64, c) = vR
End With
End Sub
The code below extracts & format values from the range B6:E6, and then stores them in the variable. Afterwards, the routine sorts the collection of 4 variables in the ascending order. When sorted they're being put into the range L31:O31.
The problem is that if there are less than 4 variables selected, say 3, the routine will skip L31 cell, and put the rest to M31:O31. Whilst it should be input as L31:N31, and O31 - blank.
How can the code be modified to make it fulfill the data starting from L31 if less than 4 variables are in the collection?
Function ExtractKey(s As Variant) As Long
Dim v As Variant, n As Long
v = Trim(s) 'remove spaces leave only spaces between words
If v Like "*(*)" Then 'if it's SOPXX (YYYY) then
n = Len(v) 'find number of the characters
If n = 11 Then
v = Mid(v, n - 7, 7) 'find the number of SOP + year in bracket
ElseIf n = 12 Then
v = Mid(v, n - 8, 8)
End If
v = Replace(v, "(", "") 'replace the brackets with nothing
v = Replace(v, " ", "")
'SOP10 (2015) doesn't have to go first before SOP12 (2014); switch figures
If n = 11 Then
v = Right(v, 4) + Left(v, 1)
ElseIf n = 12 Then
v = Right(v, 4) + Left(v, 2)
End If
ExtractKey = CLng(v)
Else
ExtractKey = 0
End If
End Function
Sub Worksheet_Delta_Update()
Dim SourceRange As Range, TargetRange As Range
Dim i As Long, j As Long, minKey As Long, minAt As Long
Dim v As Variant
Dim C As New Collection
Set SourceRange = Worksheets("t").Range("B6:E6")
Set TargetRange = Worksheets("x").Range("L31:O31")
For i = 1 To 4
v = SourceRange.Cells(1, i).Value
C.Add Array(ExtractKey(v), v)
Next i
'transfer data
For i = 1 To 4
minAt = -1
For j = 1 To C.Count
If minAt = -1 Or C(j)(0) < minKey Then
minKey = C(j)(0)
minAt = j
End If
Next j
TargetRange.Cells(1, i).Value = C(minAt)(1)
C.Remove minAt
Next i
End Sub
You could add one variable e.g. col which will be used instead of variable i when the value is inserted into TargetRange. This variable will work the same way as the i works but it will be incremented only when the value which is inserted is not empty. HTH
'transfer data
Dim col As Integer
col = 1
For i = 1 To 4
minAt = -1
For j = 1 To C.Count
If minAt = -1 Or C(j)(0) < minKey Then
minKey = C(j)(0)
minAt = j
End If
Next j
If (C(minAt)(1) <> "") Then
TargetRange.Cells(1, col).Value = C(minAt)(1)
col = col + 1
End If
C.Remove minAt
Next i