How to split strings based on special character (multiple times) - excel

I'm trying to split a string based on ampersand (&), remove all the ampersands and separate each part to columns.
The number of strings differ every time.
Sample text:
My output:
What I need:
For i = 2 To 7
fullname = Cells(i, 1).Value
commaposition = InStr(fullname, "&")
Cells(i, 2).Value = Left(fullname, commaposition - 2)
For x = 2 To 7
fullname = Cells(i, 1).Value
commaposition = InStr(fullname, "&")
Cells(i, 3).Value = Mid(fullname, commaposition + 2)
Cells(x, 3).Value = Mid(fullname, commaposition + 2)
For y = 2 To 7
fullname = Cells(i, 1).Value
commaposition = InStr(fullname, "&")
Cells(i, 4).Value = Mid(fullname, commaposition + 2)
Cells(x, 4).Value = Mid(fullname, commaposition + 2)
Next y
Next x
Next i

Another option (other then #Storax's method) would be to use Regular Expressions which could account for more then just an ampersand.
Option Explicit
Public Sub FindNames()
Dim rng As Range
Dim j As Long
Dim c, Match
' Update for your range
With ActiveSheet
Set rng = .Range(.Cells(1, 1), .Cells(.Cells(.Rows.Count, 1).End(xlUp).Row, 1))
End With
With CreateObject("vbscript.regexp")
.Global = True
.Pattern = "\w+"
For Each c In rng
j = 0
If .test(c.Value2) Then
For Each Match In .Execute(c.Value2)
j = j + 1
c.Offset(0, j).Value2 = Match
Next Match
End If
Next c
End With
End Sub

You could try something like that
Sub SplitAmper()
Const AP = "&"
Dim v As Variant
Dim rg As Range
Set rg = Range("A2:A7") ' Adjust to your needs
Dim sngCell As Range
For Each sngCell In rg
v = Split(sngCell.Value, AP)
Cells(sngCell.Row, 1).Resize(, UBound(v) + 1) = v
Next
End Sub
Update: Another solution mentioned in the comments from SJR would be Text to Columns
Sub AnotherAmper()
Const AP = "&"
Dim rg As Range
Set rg = Range("A1:A7") ' Adjust to your needs
rg.TextToColumns Destination:=Range("B1"), DataType:=xlDelimited, _
Other:=True, OtherChar:=AP
End Sub

Related

Select the First and Last Values in a Subset of String Values

VBA Code:
Sub Example():
Dim i As Double
Dim Letter As String
Dim var1 As Long
Dim var2 As Long
Dim Row_For_Table As Integer
Row_For_Table = 1
For i = 1 To 12
If Cells(i + 1, 1).Value <> Cells(i, 1).Value Then
'MsgBox ("different")
Letter = Cells(i, 1).Value
var2 = Cells(i, 3).Value
var1 = Cells(i, 2).Value
Range("F" & Row_For_Table).Value = Letter
Range("G" & Row_For_Table).Value = var2 - var1
Row_For_Table = Row_For_Table + 1
Else
'MsgBox ("same")
End If
Next i
End Sub
I would like to create summary table of A, B, and C with the Values of (14-1), (12-5), and (4-1). I would like to write this is VBA as a template for a bigger project.
Thank you.
This uses a dictionary to do what you are looking for. It assumes your table is sorted by Column A.
Dim i As Long
Dim lr As Long
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
With Sheets("Sheet1") 'Change as needed
lr = .Cells(.Rows.Count, 1).End(xlUp).Row 'Lastrow
For i = 1 To lr + 1
If Not dict.exists(.Cells(i, 1).Value) Then 'Key doesn't exist
dict.Add .Cells(i, 1).Value, .Cells(i, 2).Value 'Add key and first value
If i > 1 Then 'Avoid out of range errors
dict(.Cells(i - 1, 1).Value) = .Cells(i - 1, 3).Value - dict(.Cells(i - 1, 1).Value) 'Subtract old value from new value
End If
End If
Next i
Dim key As Variant
i = 1
For Each key In dict
.Cells(i, 6).Value = key 'place values
.Cells(i, 7).Value = dict(key)
i = i + 1
Next key
End With
This also uses a dictionary and should work for multiple columns.
Option Explicit
Sub StuffDo()
Dim rng As Range
Dim arrData As Variant
Dim ky As Variant
Dim dicLetters As Object
Dim arrNumbers()
Dim cnt As Long
Dim idxCol As Long
Dim idxRow As Long
arrData = Sheets("Sheet1").Range("A1").CurrentRegion.Value
Set dicLetters = CreateObject("Scripting.Dictionary")
For idxRow = LBound(arrData, 1) To UBound(arrData, 1)
For idxCol = LBound(arrData, 2) + 1 To UBound(arrData, 2)
ky = arrData(idxRow, 1)
If Not dicLetters.exists(ky) Then
arrNumbers = Array(arrData(idxRow, idxCol))
Else
arrNumbers = dicLetters(ky)
cnt = UBound(arrNumbers) + 1
ReDim Preserve arrNumbers(cnt)
arrNumbers(cnt) = arrData(idxRow, idxCol)
End If
dicLetters(ky) = arrNumbers
Next idxCol
Next idxRow
Set rng = Range("A1").Offset(, Range("A1").CurrentRegion.Columns.Count + 2)
For Each ky In dicLetters.keys
arrNumbers = dicLetters(ky)
rng.Value = ky
rng.Offset(, 1) = arrNumbers(UBound(arrNumbers))
rng.Offset(, 2) = arrNumbers(0)
Set rng = rng.Offset(1)
Next ky
End Sub

VBA value range doing strange

So how do i put this i am a vba rookie and i have been trying to make an excel file and the purpose is that it should be an inventory of all items one sheet is for putting items in and other is for giving them away. But that is not the problem, the thing is i wanted to have a page called "databaseinventory" where all products that are taken out are writen down but my value is doing strange. (look at the image)
So this is the input screen and if i type this
this is the output on a different sheet but i don't want it to be 0
I noticed if i change the input and add 3 rows it works but that prevents me of typing more then one product
this is the output that i want to have and i really don't know what is wrong with the code
Sub Btn_Clickweggegeven()
Dim x As Long
Dim Givenaway As Worksheet
Dim Inventory As Worksheet
Dim productn As String
Dim erow As Long
Dim rng As Range
Dim rownumber As Long
Dim row As Long
Dim wsData As Worksheet
Dim wsIn As Worksheet
Dim nextRow As Long
Dim BtnText As String
Dim BtnNum As Long
Dim strName As String
x = 2
Do While Cells(x, 1) <> ""
' go through each item on list
productn = Cells(x, 1)
' if item is not new then add quanity to total Inventory
With Worksheets("Inventory").Range("A:A")
Set rng = .Find(What:=productn, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
'if item is new add item to the bottom of Inventory list
If rng Is Nothing Then
erow = Worksheets("Inventory").Cells(1, 1).CurrentRegion.Rows.Count + 1
Worksheets("Inventory").Cells(erow, 1) = Worksheets("Givenaway").Cells(x, 1)
Worksheets("Inventory").Cells(erow, 2) = Worksheets("Givenaway").Cells(x, 2)
Worksheets("Inventory").Cells(erow, 3) = Worksheets("Givenaway").Cells(x, 3)
Worksheets("Inventory").Cells(erow, 4) = Worksheets("Givenaway").Cells(x, 4)
GoTo ende
Else
rownumber = rng.row
End If
End With
Worksheets("Inventory").Cells(rownumber, 2).Value = Worksheets("Inventory").Cells(rownumber, 2).Value _
- Worksheets("Givenaway").Cells(x, 2).Value
Worksheets("Inventory").Cells(rownumber, 4).Value = Worksheets("Inventory").Cells(rownumber, 4).Value _
+ Worksheets("Givenaway").Cells(x, 2).Value
ende:
x = x + 1
Loop
'after complete delete items from Givenaway list
Worksheets("Givenaway").Select
row = 2
Do While Cells(row, 1) <> ""
Range(Cells(row, 1), Cells(row, 3)).Select
Selection.Delete
Loop
Set wsIn = Worksheets("Givenaway")
Set wsData = Worksheets("Databaseinventory")
With wsData
nextRow = .Cells(.Rows.Count, "A") _
.End(xlUp).Offset(1, 0).row
End With
With wsData
With .Cells(nextRow, 1)
.Value = Now
.NumberFormat = "mm/dd/yyyy hh:mm:ss"
End With
.Cells(nextRow, 2).Value = productn
.Cells(nextRow, 3).Value = Worksheets("Databaseinventory").Cells(rownumber, 3).Value _
+ Worksheets("Givenaway").Cells(x, 2).Value
End With
End Sub
This code is deleting the value
Worksheets("Givenaway").Select
row = 2
Do While Cells(row, 1) <> ""
Range(Cells(row, 1), Cells(row, 3)).Select
Selection.Delete
Loop
before this line copies it to Databaseinventory
Cells(nextRow, 3).Value = Worksheets("Databaseinventory").Cells(rownumber, 3).Value _
+ Worksheets("Givenaway").Cells(x, 2).Value
It appears to work if you have 3 rows is because on exit from the Do While Cells(x, 1) <> "" loop the value of x will be 3. After deleting the first record then Worksheets("Givenaway").Cells(x, 2).Value will be the value for the third record.
The database update routine also need to be within the loop
Option Explicit
Sub Btn_Clickweggegeven()
Dim wb As Workbook, rng As Range
Dim wsInv As Worksheet, wsGiven As Worksheet, wsData As Worksheet
Dim iRow As Long, iDataRow As Long, iInvRow As Long
Dim sProduct As String, nValue As Single
Set wb = ThisWorkbook
Set wsGiven = wb.Sheets("GivenAway")
Set wsInv = wb.Sheets("Inventory")
Set wsData = wb.Sheets("Databaseinventory")
iDataRow = wsData.Cells(Rows.Count, 1).End(xlUp).row
iRow = 2
With wsGiven
Do While .Cells(iRow, 1) <> ""
sProduct = .Cells(iRow, 1)
nValue = .Cells(iRow, 2)
' if item is not new then add quanity to total Inventory
With wsInv.Range("A:A")
Set rng = .Find(What:=sProduct, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
End With
If rng Is Nothing Then
iInvRow = wsInv.Cells(Rows.Count, 1).End(xlUp).row + 1
wsInv.Cells(iInvRow, 1).Resize(1, 4).Value = .Cells(iRow, 1).Resize(1, 4).Value
Else
iInvRow = rng.row
wsInv.Cells(iInvRow, 2).Value = wsInv.Cells(iInvRow, 2).Value - nValue
wsInv.Cells(iInvRow, 4).Value = wsInv.Cells(iInvRow, 4).Value + nValue
End If
' write to database
iDataRow = iDataRow + 1
With wsData.Cells(iDataRow, 1)
.Value = Now
.NumberFormat = "mm/dd/yyyy hh:mm:ss"
.Offset(0, 1) = sProduct ' col B
.Offset(0, 2) = wsInv.Cells(iInvRow, 3).Value + nValue ' col C ??
End With
iRow = iRow + 1
Loop
End With
'delete from GivenAway
wsGiven.Range("A2").Resize(iRow, 3).Delete
MsgBox iRow - 2 & " records processed", vbInformation
End Sub

Summing the results of a loop in VBA, Excel

I'm trying to sum the results of a loop. So far the code takes values from WsIn by row, runs them through a model and gives results in WsOut. The code takes the values in row 1 and gives results based on these and goes back and does it for row 2 and so on. The results are overwritten by the new results as the code loops. But I want it to add the results given by row 1 to the results given by row 2 added to row 3, etc. The results are ranges C5:C33 and D5:D33 in WsOut. I think the code for this would be something like the loop I put at the bottom but I'm not sure how to make this. Any ideas on what I should do?
Sub TEST1()
Dim WsIn As Worksheet ' Input
Dim WsT As Worksheet ' Taken
Dim WsOut As Worksheet ' Output
Dim WsMod As Worksheet ' Model
Dim Arr As Variant
Dim Rl As Long
Dim R As Long
Dim Rout As Long ' WsOut row
Dim Cmod As Long ' WsMod column
Dim XXX As Integer
Dim YYY As Integer
Dim WWW As Integer
Set WsT = Sheets("Inputs Taken")
Set WsIn = Sheets("Input Values")
Set WsOut = Sheets("Output")
Set WsMod = Sheets("Model")
Application.ScreenUpdating = False
Rl = WsIn.Cells(WsIn.Rows.Count, "B").End(xlUp).Row
For R = 2 To Rl
'Pasting Input Values into Inputs Taken
With WsIn
Arr = .Range(.Cells(R, 1), .Cells(R, 4)).Value
WsT.Cells(5, "D").Resize(UBound(Arr, 2), UBound(Arr)) _
.Value = Application.Transpose(Arr)
Arr = .Range(.Cells(R, 5), .Cells(R, 6)).Value
WsT.Cells(11, "C").Resize(UBound(Arr), UBound(Arr, 2)).Value = Arr
Arr = .Range(.Cells(R, 7), .Cells(R, 8)).Value
WsT.Cells(16, "C").Resize(UBound(Arr), UBound(Arr, 2)).Value = Arr
Arr = .Range(.Cells(R, 9), .Cells(R, 14)).Value
WsT.Cells(9, "G").Resize(UBound(Arr, 2), UBound(Arr)) _
.Value = Application.Transpose(Arr)
Arr = .Range(.Cells(R, 15), .Cells(R, 16)).Value
WsT.Cells(20, "C").Resize(UBound(Arr, 2), UBound(Arr)) _
.Value = Application.Transpose(Arr)
Arr = .Range(.Cells(R, 17), .Cells(R, 18)).Value
WsT.Cells(20, "D").Resize(UBound(Arr, 2), UBound(Arr)) _
.Value = Application.Transpose(Arr)
End With
'Setting Opening PUP to 100% and refreshing
WsT.Cells(5, "G").Value = 1
Application.CalculateFull
'Calculating No RPs
With WsOut
Cmod = 62 ' BJ:BP
For Rout = 7 To 13
.Cells(Rout, "C").Value = SumProduct(Cmod, WsMod)
Cmod = Cmod + 1
Next Rout
.Cells(14, 3).Value = Application.Sum(.Range("C11:C13"))
Cmod = 71 ' BS:CB
For Rout = 17 To 26
.Cells(Rout, "C").Value = SumProduct(Cmod, WsMod, True)
Cmod = Cmod + 1
Next Rout
.Cells(5, 3).Value = WsMod.Cells(6, "BL").Value _
- WsMod.Cells(6, "BS").Value _
- WsMod.Cells(6, "BT").Value
.Cells(15, 3).Value = Application.Sum(.Range("C5,C7:C10, C14"))
.Cells(27, 3).Value = Application.Sum(.Range("C17:C26"))
.Cells(29, 3).Value = Application.Sum(WsMod.Range("AN6:AN365"))
.Cells(30, 3).Value = Application.Sum(WsMod.Range("AP6:AP365"))
.Cells(31, 3).Value = WsOut.Cells(2, 3).Value
.Cells(33, 3).Value = WsOut.Cells(15, 3) - Application.Sum(.Range("C29:C31, C27"))
End With
'Changing PUP rate
WsT.Cells(5, "G").Value = 0
Application.CalculateFull
'Calculate with RP
With WsOut
Cmod = 62 ' BJ:BP
For Rout = 7 To 13
.Cells(Rout, "D").Value = SumProduct(Cmod, WsMod)
Cmod = Cmod + 1
Next Rout
.Cells(14, 4).Value = Application.Sum(.Range("D11:D13"))
Cmod = 71 ' BS:CB
For Rout = 17 To 26
.Cells(Rout, "D").Value = SumProduct(Cmod, WsMod, True)
Cmod = Cmod + 1
Next Rout
.Cells(5, 4).Value = WsMod.Cells(6, "BL").Value _
- WsMod.Cells(6, "BS").Value _
- WsMod.Cells(6, "BT").Value
.Cells(15, 4).Value = Application.Sum(.Range("D5,D7:D10, D14"))
.Cells(27, 4).Value = Application.Sum(.Range("D17:D26"))
.Cells(29, 4).Value = Application.Sum(WsMod.Range("AN6:AN365"))
.Cells(30, 4).Value = Application.Sum(WsMod.Range("AP6:AP365"))
.Cells(31, 4).Value = WsOut.Cells(2, 3).Value
.Cells(33, 4).Value = WsOut.Cells(15, 4) - Application.Sum(.Range("D29:D31, D27"))
End With
'Exit For
Next R
Application.ScreenUpdating = True
For XXX = 5 To 33
For YYY = 6 To 7
For WWW = 3 To 4
WsOut.Cells(XXX, YYY).Value = WsOut.Cells(XXX, WWW).Value
Next WWW
Next YYY
Next XXX
End Sub
Private Function SumProduct(ByVal Cmod As Long, _
WsMod As Worksheet, _
Optional ByVal Negative As Boolean) As Double
Dim AuxRng As Range
With WsMod
Set AuxRng = .Range(.Cells(6, Cmod), .Cells(365, Cmod))
SumProduct = Application.SumProduct( _
.Range("AD6:AD365"), _
.Range("AG6:AG365"), _
AuxRng)
End With
End Function
Have an integer add to itself at the end of each loop. totNum = totNum + this rows count. At the end total sum will = totNum.

If cell value same with upper cell value

I tried to make macro for my daily job, but i cannot use IF as formula due to so many item in my excel file, so solution is to convert formula to VBA code.
I need help to convert if formula to VBA code in excel as below:
=IF(J2<>J1,AD2-X2,AE1-X2).
Here is an answer to your question. However, it is limited to only work with OP information. Also, if the calculations are taking too long then, you should try setting your calculation to Manual (Formulas->Calculation Options->Manual).
Option Explicit
Public Sub RunIF()
Dim vntOut As Variant
Dim rngSame As Range
With ActiveSheet
Set rngSave = .Range("X2")
If (LCase(Trim(.Range("J2").Value)) <> LCase(Trim(.Range("J1").Value))) Then
vntOut = .Range("AD2").Value - rngSave.Value
Else
vntOut = .Range("AE1").Value - rngSave.Value
End If
.Range("AE2").value = vntOut
Set rngSave = Nothing
End With
End Sub
And here is your code converted to use Column J:
Private Sub CommandButton12_Click()
Dim x As Long
Dim LastRow As Long
Dim i as long
With Sheets("Shipping Schedule")
LastRow = .Cells(.Rows.Count, "J").End(xlUp).Row
For i = 2 to LastRow
set r = .Range("J" & I)
'For Each r In .Range("J2:J" & LastRow)
If LCase(Trim(r.Value)) <> LCase(Trim(r.Offset(-1, 0).Value)) Then
'ae2 = "AD2" - "x2"
r.Offset(0, 21).Value = r.Offset(0, 20).Value - r.Offset(0, 14).Value
Else
'ae2 = "AE1" - "x2"
r.Offset(0, 21).Value = r.Offset(-1, 21).Value - r.Offset(0, 14).Value
End If
set r = nothing
Next i
End With
End Sub
However, you should increment with I instead of for each as the cells are dependent on the previous row and excel may not loop through the range like you would prefer.
Excel Formula to VBA: Fill Column
Sub FillColumn()
Const cCol As Variant = "J" ' Last-Row-Column Letter/Number
Const cCol1 As Variant = "AD"
Const cCol2 As Variant = "X"
Const cCol3 As Variant = "AE"
Const cFirstR As Long = 1 ' First Row
Dim rng As Range ' Last Used Cell in Last-Row-Column
Dim i As Long ' Row Counter
Set rng = Columns(cCol).Find("*", , xlFormulas, , xlByColumns, xlPrevious)
If rng Is Nothing Then Exit Sub
For i = cFirstR To rng.Row - 1
If Cells(i + 1, cCol) <> Cells(i, cCol) Then
Cells(i + 1, cCol3) = Cells(i + 1, cCol1) - Cells(i + 1, cCol2)
Else
Cells(i + 1, cCol3) = Cells(i, cCol3) - Cells(i + 1, cCol2)
End If
Next
End Sub
Private Sub CommandButton12_Click()
Dim x As Long
Dim LastRow As Long
Sheets("Shipping Schedule").Select
With Sheets("Shipping Schedule")
LastRow = .Cells(.Rows.Count, "N").End(xlUp).Row
For Each r In .Range("N2:N" & LastRow)
If r.Value <> "" Then
r.Offset(0, 19).Value = ………………………………….
End if
Next r
End With
End Sub

How do I merge a random number of cells with a blank cell in a column?

Example of my dataset:
blank
1
2
blank
3
4
5
blank
6
I want to merge all cells below a blank cell into the blank cell, but stop counting when it reaches the next blank cell.
End result should look like this, with the strings concatenated
12
345
6
I'm currently trying to create an array with 1s and 2s with 2 meaning its a blank cell, then counting the 1s and merging them. I don't know if this will work or if there is an easier way to do this.
This requires you to select the area you want to merge, starting with the first blank cell and ending with the last cell with a value. It will delete entire rows; not sure if that's what you wanted:
Sub MergeConstantsIntoEmpties()
Dim BlankCells As Excel.Range
Dim ConstantCells As Excel.Range
Dim i As Long
Dim MungedContents As String
With Selection
Set BlankCells = .SpecialCells(xlCellTypeBlanks)
Set ConstantCells = .SpecialCells(xlCellTypeConstants)
End With
For i = 1 To BlankCells.Areas.Count
If ConstantCells.Areas(i).Count = 1 Then
MungedContents = ConstantCells.Areas(i).Value
Else
MungedContents = Join(Application.WorksheetFunction.Transpose(ConstantCells.Areas(i).Value))
End If
BlankCells.Areas(i).Value = MungedContents
Next i
ConstantCells.EntireRow.Delete
End Sub
If we start with:
and run this macro:
Sub PileOn()
Dim N As Long, st As String
Dim i As Long, v As Variant
N = Cells(Rows.Count, "A").End(xlUp).Row
For i = N To 1 Step -1
v = Cells(i, 1).Value
If v <> "" Then
st = st & v
Cells(i, 1).Delete shift:=xlUp
Else
Cells(i, 1).Value = st
st = ""
End If
Next i
End Sub
We end up with:
EDIT#1:
To fix the order of the concatenated cells use this instead:
Sub PileOn()
Dim N As Long, st As String
Dim i As Long, v As Variant
N = Cells(Rows.Count, "A").End(xlUp).Row
For i = N To 1 Step -1
v = Cells(i, 1).Value
If v <> "" Then
st = v & st
Cells(i, 1).Delete shift:=xlUp
Else
Cells(i, 1).Value = st
st = ""
End If
Next i
End Sub
Here is my take on it.
Sub JoinBetweenTheLines()
Dim X As Long
X = 1
Do Until X >= Range("A" & Rows.Count).End(xlUp).Row
If Range("A" & X).text = "" Then
Range("A" & X).Delete xlUp
ElseIf Range("A" & X).Offset(1, 0).text = "" Then
X = X + 1
Else
Range("A" & X).Formula = Join(Application.Transpose(Range("A" & X & ":A" & X + 1)), "")
Range("A" & X + 1).Delete xlUp
End If
Loop
End Sub
I normally work backwards also but for this one went forwards.
I had memory processing in mind.
Sub merg()
Dim v As Long, w As Long, vVALs As Variant
With ActiveSheet 'reference the worksheet properly!
With .Range(.Cells(1, 1), .Cells(Rows.Count, 1).End(xlUp))
vVALs = .Cells.Value2
For v = LBound(vVALs, 1) To UBound(vVALs, 1)
If vVALs(v, 1) = vbNullString Then
For w = v + 1 To UBound(vVALs, 1)
If vVALs(w, 1) = vbNullString Then Exit For
vVALs(v, 1) = vVALs(v, 1) & vVALs(w, 1)
vVALs(w, 1) = vbNullString
Next w
End If
Next v
.Cells = vVALs
With .SpecialCells(xlCellTypeBlanks)
.Delete Shift:=xlUp
End With
End With
End With
End Sub

Resources