Turn a sub into a sub with arguments - excel

new to VBA and i have a bit of problems with a sub I wrote.
This sub takes values from various coloumns and put the values into a dictionary, then prints the dictionary in another coloumn.
Sub Unitario()
Dim Dict As Object
Dim bRiga As Long
Dim aRiga As Long
Dim cRiga As Long
Dim dRiga As Long
Dim I As Long
Dim MyString As String
Dim arr
Set Dict = CreateObject("Scripting.Dictionary")
Dict.CompareMode = vbTextCompare 'compare without distinction between capitals
'while vbBinaryCompare distinguish between capitals
ThisWorkbook.Worksheets("Foglio2").Range("c1").EntireColumn.Clear
aRiga = Sheets("Lavoro").Cells(Rows.Count, "M").End(xlUp).Row
bRiga = Sheets("Lavoro").Cells(Rows.Count, "N").End(xlUp).Row
cRiga = Sheets("Lavoro").Cells(Rows.Count, "O").End(xlUp).Row
dRiga = Sheets("Lavoro").Cells(Rows.Count, "P").End(xlUp).Row
For I = 4 To aRiga
MyString = Sheets("Lavoro").Cells(I, "M")
'to change coloumn i need to change values up there
If Not Dict.exists(MyString) Then
Dict.Add MyString, MyString
End If
Next I
'adds coloumns value to dictionary
For I = 4 To bRiga
MyString = Sheets("Lavoro").Cells(I, "N")
'to change coloumn i need to change values up there
If Not Dict.exists(MyString) Then
Dict.Add MyString, MyString
End If
Next I
'adds coloumns value to dictionary
For I = 4 To cRiga
MyString = Sheets("Lavoro").Cells(I, "O")
'to change coloumn i need to change values up there
If Not Dict.exists(MyString) Then
Dict.Add MyString, MyString
End If
Next I
'adds coloumns value to dictionary
For I = 4 To dRiga
MyString = Sheets("Lavoro").Cells(I, "P")
'to change coloumn i need to change values up there
If Not Dict.exists(MyString) Then
Dict.Add MyString, MyString
End If
Next I
'adds coloumns value to dictionary
arr = Dict.Items
Worksheets("Foglio2").Range("c1").Resize(Dict.Count, 1).Value = Application.Transpose(arr)
End Sub
it is clear that this sub is not optimized, since i have to manually change the values in the sub anytime i have to use it with other ranges.
what i'm trying to do is make a sub that can be called with various range arguments from buttons, without having to write 100 times the same macro with different ranges.
so that i could simply write something like this instead of manually modifying the code:
Private sub Commandbutton1_Click
Unitario(OutputSheet,OutputCell,InputRange1,InputRange2,..., InputRangeN)
End Sub
so that i have only 1 macro on the excel and various buttons with different arguments.
can you help me?

It can be like below:
Sub Unitario(strFirstCol as String,strSecondCol as String, strThirdCol as String, strFourthCol as String)
And then you will have to adopt the following section.
aRiga = Sheets("Lavoro").Cells(Rows.Count, strFirstCol).End(xlUp).Row
bRiga = Sheets("Lavoro").Cells(Rows.Count, strSecondCol).End(xlUp).Row
cRiga = Sheets("Lavoro").Cells(Rows.Count, strThirdCol).End(xlUp).Row
dRiga = Sheets("Lavoro").Cells(Rows.Count, strFourthCol).End(xlUp).Row
Inside each "For loop":
MyString = Sheets("Lavoro").Cells(I, strFirstCol) '\\ Column M
MyString = Sheets("Lavoro").Cells(I, strSecondCol) '\\ Column N
MyString = Sheets("Lavoro").Cells(I, strThirdCol) '\\ Column O
MyString = Sheets("Lavoro").Cells(I, strFourthCol) '\\ Column P
And then call the sub like
Call Unitario("M","N","O","P")

whenever i have to add arguments to a often used sub, or function, i just add the arguments with ´optionaĺ.
This way i won ´t have to recode every call to the sub.
Example
Public sub test (byval optional addr as string)

Related

Combo boxes where available choices are unique and dependent on the choice in previous combo box

I have a data set in another file that has 3 columns with thousands of rows. All 3 columns have values that are not unique.
I need 3 combo boxes.
The first combo box is for selecting from column "A" (bringing back unique values) for the different types of business units.
Next, depending on the business unit, combo box 2 is for selecting a specific customer (depending on the business unit selected).
Finally, combo box 3 is for selecting from the different cost centers that exist for a given customer.
I need unique values for all 3 columns.
I think I have combo box 1 with the following code:
Option Explicit
Private Sub UserForm_Initialize()
Dim wbExternal As Workbook '<-- the other workbook with the data
Dim wsExternal As Worksheet '<-- the worksheet in the other workbook
Dim lngLastRow As Long '<-- the last row on the worksheet
Dim rngExternal As Range '<-- range of data for the RowSource
Dim myCollection As collection, cell As Range
On Error Resume Next
Application.ScreenUpdating = False
Set wbExternal = Application.Workbooks.Open("C:\Users\sarabiam\desktop\OneFinance_Forecast_Model\FY19_New_Forecast_Model_Data_Tables.xlsm", True, True)
Set wsExternal = wbExternal.Worksheets("#2Table_Revenue") '<-- identifies worksheet
Set rngExternal = wsExternal.Range("A8:A" & CStr(lngLastRow))
Set myCollection = New collection
With ComboBox1
.Clear
For Each cell In Range("A8:A" & Cells(Rows.Count, 1).End(xlUp).Row)
If Len(cell) <> 0 Then
Err.Clear
myCollection.Add cell.Value, cell.Value
If Err.Number = 0 Then .AddItem cell.Value
End If
Next cell
End With
ComboBox1.ListIndex = 0
wbExternal.Close
Application.ScreenUpdating = True '<-- updates the worksheet on your screen
any time there is a change within the worksheet
End Sub
Here's a pretty generic approach - it only loads the data once, into an array, then uses that to reset list content on selection of a "previous" list.
Option Explicit
Const dataPath As String = "C:\Users\usernameHere\Desktop\tmp.xlsx"
Dim theData 'source data
Private Sub UserForm_Activate()
LoadData
Me.cboList1.List = GetList(1, "")
End Sub
Private Sub cboList1_Change()
Me.cboList2.Clear
Me.cboList2.List = GetList(2, Me.cboList1.Value)
Me.cboList3.Clear
End Sub
Private Sub cboList2_Change()
Me.cboList3.Clear
Me.cboList3.List = GetList(3, Me.cboList2.Value)
End Sub
'Return unique values from source data, given a specific column
' If given a value for "restrictTo", filter on match in column to "left"
' of the requested value column
Function GetList(colNum As Long, restrictTo)
Dim i As Long, n As Long, rv()
Dim dict As Object, v, ub As Long, inc As Boolean
Set dict = CreateObject("scripting.dictionary")
ub = UBound(theData, 1)
ReDim rv(1 To ub) 'will set final size after filling...
n = 0
For i = 1 To ub
v = theData(i, colNum)
'are we restricting the values we collect based on a different list?
If colNum > 1 And Len(restrictTo) > 0 Then
'is this value valid?
inc = (theData(i, colNum - 1) = restrictTo)
Else
inc = True 'collect all values
End If
If inc And Not dict.exists(v) Then
'don't already have this value - add to array and dict
n = n + 1
dict.Add v, True
rv(n) = v
End If
Next i
ReDim Preserve rv(1 To n) 'resize array to size of content
GetList = rv
End Function
'load data from external file
Private Sub LoadData()
With Workbooks.Open(dataPath).Worksheets("#2Table_Revenue")
theData = .Range(.Range("A8"), _
.Cells(.Rows.Count, 1).End(xlUp).Offset(0, 2)).Value
.Parent.Close False
End With
End Sub

dependent dictionaries excel vba

I’d like to know which is the quickest way to get the unique values from a column and then the unique values in another column for each of the values previously found in the first column
Example
Column A Column B
Case 1 Item A
Case 1 Item B
Case 1 Item A
Case 2 Item C
Case 2 Item C
Case 3 Item D
Case 3 Item E
Case 3 Item F
Case 3 Item D
The result should return three values from the first column (Case 1, Case 2, Case 3) and then two values for Case 1 (Item A and Item B), one value for Case 2 (Item C), three values for Case 3 (Item D, Item E, Item F)
I do not want to use an advance filter or copy filtered rows in another sheet.
I tried to reach that using scripting dictionary, but I don’t know dictionary so well, and I was not able to use the keys of the first dictionary (Case 1, …) as parameters to add the items in the second dictionary (Item A, ….)
Ideally, at the end, the macro will create one textbox for each key of the first dictionary and then for each of those creates other text boxes for each key of the second dictionary (I kind of treeview but using textboxes)
Clearly, there will be a loop
Here one of the many tentatives (but, as I said, I have really poor knowledge in dictionary)
Dim d As Variant, dict As Object
Dim v As Long, a As Variant
Dim vCount As Long
Dim vCount1 As Long
Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare 'default is vbbinarycompare
With Sheets("Sheet1") '<- alter to suite
a = .Range("a2", Range("a" & Rows.Count).End(xlUp)).Value
' change "a1"/ "a" to appropreate column reference
'build dictionary
For v = LBound(a, 1) To UBound(a, 1)
'overwrite method - faster (no error control)
'writes name&position as key, ID as item
'dict.Itema(v, 1)(Join(Array(vVALs(v, 2)
dict.Item(Join(Array(a(v, 1)), ChrW(8203))) = a(v, 2)
Next v
Me.ComboBox1.List = dict.Keys
Me.ComboBox2.List = dict.Values
'loop through the second table
For v = 2 To .Cells(Rows.Count, 2).End(xlUp).row
d = (Join(Array(a(v, 1))))
If dict.Exists(d) Then
vCount = dict.Item(d)
MsgBox vCount
End If
Next v
End With
What if there is a third column ?
Example
Column A Column B Column C
Case 1 Item A
Case 1 Item B number 1
Case 1 Item A number 1
Case 2 Item C number 2
Case 2 Item C number 1
Case 3 Item D number 3
Case 3 Item E number 1
Case 3 Item F number 1
Case 3 Item D number 2
the result should be
Case 1
Item A number1
Item B number1
Case 2
Item C number1
number2
Case 3
Item D number2
number3
Item E number1
Item F number1
here the code I tried to build based on Zev Spitz suggestion, but without success
Dim row As Variant
Dim dict As New Dictionary
For Each row In Sheets("Positioning").Range("h2", Range("p" &
Rows.Count).End(xlUp)).Rows
Dim caseKey As String
caseKey = row.Cells.Item(2, 1).Value
Dim innerDict As Scripting.Dictionary
If dict.Exists(caseKey) Then
Set innerDict = dict(caseKey)
Else
Set innerDict = New Scripting.Dictionary
Set dict(caseKey) = innerDict
End If
innerDict(row.Cells.Item(2, 3).Value) = 1
Dim outerKey As Variant, innerKey As Variant, inner2Key As Variant
Dim inner2Dict As Scripting.Dictionary
For Each innerKey In innerDict.Keys
Set inner2Dict = New Scripting.Dictionary
If inner2Dict.Exists(inner2Dict) Then
Set innerDict(innerKey) = inner2Dict
Else
Set inner2Dict = inner2Dict
End If
inner2Dict(row.Cells.Item(2, 8).Value) = 1
Next
Next
For Each outerKey In dict.Keys
Debug.Print outerKey
For Each innerKey In innerDict.Keys
Debug.Print vbTab, innerKey
For Each inner2Key In inner2Dict.Keys
Debug.Print vbTab, vbTab, inner2Key
Next
Next
Next
Loading the data using nested dictionaries
You can use a dictionary which has other dictionaries as its' values:
Dim row As Variant
Dim dict As New Dictionary
For Each row In Worksheets("Sheet1").Range("A1", "B9").Rows
Dim caseKey As String
caseKey = row.Cells(1, 1).Value
Dim innerDict As Scripting.Dictionary
If dict.Exists(caseKey) Then
Set innerDict = dict(caseKey)
Else
Set innerDict = New Scripting.Dictionary
Set dict(caseKey) = innerDict
End If
innerDict(row.Cells(1, 2).Value) = 1 'an arbitrary value
Next
Then you can iterate over each key in the outer dictionary, and iterate over each key in the inner dictionary. The following code, for example:
Dim outerKey As Variant, innerKey As Variant
For Each outerKey In dict.Keys
Debug.Print outerKey
For Each innerKey In dict(outerKey).Keys
Debug.Print vbTab, innerKey
Next
Next
will output the following:
Case 1
Item A
Item B
Case 2
Item C
Case 3
Item D
Item E
Item F
For an description of how to use a dictionary to get a unique set of values, see here.
Populating another combobox based on the selection in the first combobox
Assuming you've set the List property of the first combobox to the Keys collection of the dictionary:
Me.ComboBox1.List = dict.Keys
you can handle the Change event of the combobox, and use it to fill the second combobox with the keys of the corresponding inner dictionary:
Private Sub ComboBox1_Change()
If Value Is Nothing Then
Me.ComboBox2.List = Nothing
Exit Sub
End If
Me.ComboBox2.Value = Nothing
Me.ComboBox2.List = dict(Me.ComboBox1.Value).Keys
End Sub
Unique values using SQL
Another way to get the unique combinations of values might be to execute an SQL statement on the Excel worksheet:
SELECT DISTINCT [Column A], [Column B]
FROM [Sheet1$]
but this generally comes back as an ADO or DAO flat Recordset -- with fields and rows -- while nested dictionaries preserve the hierarchical nature of this data.
Complete code-behind
Add a reference to the Microsoft Scripting Runtime (Tools > References...)
Option Explicit
Dim dict As New Dictionary
Private Sub ComboBox1_Change()
If Value Is Nothing Then
Me.ComboBox2.List = Nothing
Exit Sub
End If
Me.ComboBox2.Value = Nothing
Me.ComboBox2.List = dict(Me.ComboBox1.Value).Keys
End Sub
Private Sub UserForm_Initialize()
For Each row In Worksheets("Sheet1").Range("A1", "B9").rows
Dim caseKey As String
caseKey = row.Cells(1, 1).Value
Dim innerDict As Dictionary
If dict.Exists(caseKey) Then
Set innerDict = dict(caseKey)
Else
Set innerDict = New Dictionary
Set dict(caseKey) = innerDict
End If
innerDict(row.Cells(1, 2).Value) = 1 'an arbitrary value
Next
Me.ComboBox1.List = dict.Keys
End Sub
Complete code behind for two dependent comboboxes
Notice that the repetitious code has been (mostly) refactored into two methods: FindOrNew and HandleComboboxChange.
Option Explicit
Dim dict As New Dictionary
Private Function FindOrNew(d As Dictionary, key As String) As Dictionary
If d.Exists(key) Then
Set FindOrNew = d(key)
Else
Set FindOrNew = New Dictionary
Set d(key) = FindOrNew
End If
End Function
Private Sub HandleComboboxChange(source As ComboBox, target As ComboBox)
If source.Value Is Nothing Then
Set target.list = Nothing
Exit Sub
End If
Set target.Value = Nothing
End Sub
Private Sub ComboBox1_Change()
HandleComboboxChange ComboBox1, ComboBox2
ComboBox2.list = dict(ComboBox1.Value).Keys
End Sub
Private Sub ComboBox2_Change()
HandleComboboxChange ComboBox2, ComboBox3
ComboBox3.list = dict(ComboBox1.Value)(ComboBox2.Value).Keys
End Sub
Private Sub UserForm_Initialize()
For Each row In ActiveSheet.Range("A1", "C9").rows
Dim caseKey As String
caseKey = row.Cells(1, 1).Value
Dim itemKey As String
itemKey = rows.Cells(1, 2).Value
Dim dictLevel2 As Dictionary
Set dictLevel2 = FindOrNew(dict, caseKey)
Dim innerDict As Dictionary
Set innerDict = FindOrNew(dictLevel2, itemKey)
innerDict(row.Cells(1, 3).Value) = 1 'an arbitrary value
Next
ComboBox1.list = dict.Keys
End Sub

What is the easiest way to take two columns of data and convert to dictionary?

I have a worksheet with data in columns A and B.
I am looking for a convenient way to take these columns and convert to dictionary where the cell in column A is the key and column B is the value, something like :
Dim dict as Dictionary
Set dict = CreateDictFromColumns("SheetName", "A", "B")
NOTE: I am already referencing the scripting dll.
You would need to loop, E.g.
Function CreateDictFromColumns(sheet As String, keyCol As String, valCol As String) As Dictionary
Set CreateDictFromColumns = New Dictionary
Dim rng As Range: Set rng = Sheets(sheet).Range(keyCol & ":" & valCol)
Dim i As Long
Dim lastCol As Long '// for non-adjacent ("A:ZZ")
lastCol = rng.Columns.Count
For i = 1 To rng.Rows.Count
If (rng(i, 1).Value = "") Then Exit Function
CreateDictFromColumns.Add rng(i, 1).Value, rng(i, lastCol).Value
Next
End Function
This breaks on the first empty key value cell.
I think it'd be best form to pass two ranges to a create dictionary function. This allows for the ranges to be completely separate, even different workbooks. It also allows for a 1D range to be mapped to a 2D range as demonstrated below.
Alternatively, you could also pass two arrays of range values. That may be cleaner for 1D ranges, but would result in slightly more code for 2D mapping. Notice that range elements can be looped through left to right top to bottom by index. You can use Application.Transpose(Range("A1:A5")) to effectively run top to bottom left to right.
Jagged Mapping
Sub Test()
RangeToDict Sheets(1).Range("A1:A5"), Sheets(2).Range("C1:E2")
End Sub
Function RangeToDict(ByVal KeyRng As Range, ByVal ValRng As Range) As Dictionary
Set RangeToDict = New Dictionary
For Each r In KeyRng
vi = vi + 1
'It may not be advisable to handle empty key values this way
'The handling of empty values and #N/A/Error values
'Depends on your exact usage
If r.Value2 <> "" Then
RangeToDict.Add r.Value2, ValRng(vi)
Debug.Print r.Value2 & ", " & ValRng(vi)
End If
Next
End Function
Side-By-Side (As Range)
If your target range is a single 2 column range side by side, you can simplify to passing a single range as shown below. Consequently, this also works for mapping every other element in a 1 dimensional range.
Sub Test()
RangeToDict2 Range("A1:B5")
End Sub
Function RangeToDict2(ByVal R As Range) As Dictionary
Set RangeToDict2 = New Dictionary
i = 1
Do Until i >= (R.Rows.Count * R.Columns.Count)
RangeToDict2.Add R(i), R(i + 1)
Debug.Print R(i) & ", " & R(i + 1)
i = i + 2
Loop
End Function
Two Columns (As Array)
Lastly, as an example of passing arrays as arguments, you could do something like the following. However, the following code will only work given the OP's specific scenario of mapping two columns. As is, it won't handle mapping rows or alternating elements.
Sub Test()
Dim Keys() As Variant: Keys = Range("E1:I1").Value2
Dim Values() As Variant: Values = Range("E3:I3").Value2
RangeToDict Keys, Values
End Sub
Function RangeToDict(Keys() As Variant, Values() As Variant) As Dictionary
Set RangeToDict = New Dictionary
For i = 1 To UBound(Keys)
RangeToDict.Add Keys(i, 1), Values(i, 1)
Debug.Print Keys(i, 1) & ", " & Values(i, 1)
Next
End Function
Use of Named Ranges
It may be convenient to used named ranges, in which case you can pass a Range as an argument likes this...
Sub Test()
RangeToDict Names("Keys").RefersToRange, Names("Values").RefersToRange
End Sub
The best approach to take, is to populate a variant array with the data from the worksheet. You can then loop through the array, assigning the elements of the first array column as the dictionary key; the elements of the second array column can then be used as the value.
The lrow function is used to find the last populated row from column A - allowing the code to create a dynamically sized array and dictionary.
To enable use of dictionaries within VBA, you will need to go to Tools -> References and then enable Microsoft Scripting Runtime.
Sub createDictionary()
Dim dict As Scripting.Dictionary
Dim arrData() As Variant
Dim i as Long
arrData = Range("A1", Cells(lrow(1), 2))
set dict = new Scripting.Dictionary
For i = LBound(arrData, 1) To UBound(arrData, 1)
dict(arrData(i, 1)) = arrData(i, 2)
Next i
End Sub
Function lrow(ByVal colNum As Long) As Long
lrow = Cells(Rows.Count, 1).End(xlUp).Row
End Function
This should do the trick :
Public Function test_leora(SheetName As String, _
KeyColumn As String, _
ValColumn As String) _
As Variant
Dim Dic, _
Val As String, _
Key As String, _
Ws As Worksheet, _
LastRow As Long
Set Ws = ThisWorkbook.Sheets(SheetName)
Set Dic = CreateObject("Scripting.Dictionary")
With Ws
LastRow = .Range(KeyColumn & .Rows.Count).End(xlUp).Row
For i = 1 To LastRow
Val = .Cells(i, ValColumn)
Key = .Cells(i, KeyColumn)
If Dic.exists(Key) Then
Else
Dic.Add Key, Val
End If
Next i
End With
test_leora = Dic
End Function

Finding the n-th cell in a column with a given property

I don't have much experience but I'm trying to write a function that will search column A and the 1st time it finds a string beginning with "AT" it will copy that whole string to Cell N1, the 2nd string beginning with "AT" will be copied to N2, so on and so forth until column A is exhausted. This is my feeble attempt so far but I'm not having much luck.
Function Find_AT(ByVal I As Integer)
Dim c As Range
Dim COUNTER As Integer
Dim CAPTURE As Long
COUNTER = 0
For Each c In Range("A1", Range("A65636").End(xlUp))
If Left(c, 2) = AT Then
COUNTER = COUNTER + 1
If COUNTER = I Then
CAPTURE = c
Exit For
End If
End If
Next c
Find_AT = CAPTURE
End Function
Consider:
Function Find_AT(ByVal I As Long) As String
Dim c As Range
Dim COUNTER As Long
Dim CAPTURE As String
Dim v As String
COUNTER = 0
CAPTURE = "xx"
For Each c In Range("A1", Range("A65636").End(xlUp))
v = c.Text & " "
If Left(v, 2) = "AT" Then
COUNTER = COUNTER + 1
If COUNTER = I Then
CAPTURE = c.Address
Exit For
End If
End If
Next c
Find_AT = CAPTURE
End Function
The error with your code is that the text (the string) AT needs to be enclosed in double-quotes "AT". Add Option Explicit to the top of the Module and it would take you to this error when you try to compile or execute the function.
However, given your description, I suspect that you might want to write a sub-procedure (SUB) not a Function. A function is intended to return a value. If you want to use a function you might define it like this:
Function Find_AT(rng As Range, ByVal i As Integer)
That is, you would supply it a Range to search and the number 1 to find the first value in the range that begins with "AT". However, if you put this function in a cell and copy it down, it will still return only the first occurrence. You would need to manually change 1 to 2, 3, etc. (or use a variation of ROW() to automatically generate this sequence).
Anyway, I suspect you really want a SUB-procedure that you might run by clicking a button on the worksheet.
If you wish to continue with your current function, then you could declare the return type as a string:
Function Find_AT(ByVal i As Integer) As String
'...
Dim CAPTURE As String
'...
CAPTURE = c.Text
Otherwise, setting CAPTURE = c and attempting to return this value causes a problem because c is a Range object.
Filtering is much more efficient. Two approaches below:
Filter
Sub GetAT1()
X = Filter(Application.Transpose(Range([a1], Cells(Rows.Count, "A").End(xlUp))), "AT", True)
If UBound(X) > 0 Then [n1].Resize(UBound(X) + 1) = Application.Transpose(X)
End Sub
AutoFilter
Sub GetAT()
Dim rng1 As Range
Set rng1 = Range([a1], Cells(Rows.Count, "A").End(xlUp))
Application.ScreenUpdating = False
ActiveSheet.AutoFilterMode = False
rng1.AutoFilter 1, "=AT*"
rng1.Copy [n1]
If LCase$(Left$([n1], 2)) <> "at" Then [n1].Delete xlUp
ActiveSheet.AutoFilterMode = False
Application.ScreenUpdating = True
End Sub

Parse strings, and add a number to the value

I have an Excel table in which sometimes an entire cell has the following content:
pos=51;70;112;111;132;153
Note the whole content in in a single cell, that is to say the value 51;70;112... are strings clumped together in a single cell and not in their own cells.
Can I write a macro that in all cells that contain the keyphrase "pos=", add 2 to each value, so that the end result is:
pos=53;72;114;113;134;155
Here is a code that will do it (tested on a sample on my Excel 2003):
Sub t()
Dim rCells As Range, c As Range
Dim arr As Variant, i As Integer
'Define the range to apply the code
Set rCells = Range("A1")
For Each c In rCells
'check if the cell desserves to be changed (could be adapted though to another check)
If Left(c.Value, 4) = "pos=" Then
'split all the values after the "pos=" into an array
arr = Split(Mid(c.Value, 5, Len(c.Value)), ";")
'add +2 to every value of the array (we convert the value to be sure, probably unneeded)
For i = 0 To UBound(arr)
arr(i) = CLng(arr(i)) + 2
Next i
'set back the value to the worksheet
c.Value = "pos=" & Join(arr, ";")
End If
Next c
End Sub
Note that I didn't add the error checking part if your values aren't well formated.
You know that you can easily split data without using macros, right? Just use the TextToColumns function on the Data tab
But if you really want a macro, you can do something like the following:
Sub AddNumber()
Dim numberToAdd As Integer
numberToAdd = 2
Set myRange = Range("A1:A5")
For Each myCell In myRange
If Left(myCell.Value, 4) = "pos=" Then
arEquality = Split(myCell, "=")
arElements = Split(arEquality(1), ";")
For i = 0 To UBound(arElements)
arElements(i) = arElements(i) + numberToAdd
Next
myCell.Offset(0, 1).Value = arEquality(0) + "=" + Join(arElements, ";")
End If
Next
End Sub

Resources