I have a question regarding "creating a matrix" out of single arrays wihtout having to loop through:
From a function I get back one array with data (Return_Calc, rows = n-1). I am looking for something like
Output(n-1, j-1) = Return_Calc(Nav_Range)
At the moment, I am doing this:
Temp = Return_Calc(Nav_range)
For i = 1 To n - 1
Output(i - 1, j - 1) = Temp(i - 1)
Next i
The current option works. I was just wondering if there is another possibility without looping. Thanks for your help!
I'm not sure if you would be happy with that proposal. It presents possibility of creating Array-of-Arrays which, in some situation, would work similar to Multidimensional Array. You could consider solving your problems this way.
Here is a sample code how to create and which way you could retrieve data from final array.
Sub Array_Workaround()
Dim oneDimArrA, oneDimArrB
oneDimArrA = Array(1, 2, 3, 4)
oneDimArrB = Array("A", "B", "C", "D")
Dim multiDimArr
'creating multidemmnsional array
multiDimArr = Array(oneDimArrA, oneDimArrB)
'get element- different to standard syntax
Debug.Print multiDimArr(0)(0) '--> 1
Debug.Print multiDimArr(0)(1) '--> 2
Debug.Print multiDimArr(1)(1) '--> B
End Sub
There is one important benefit of presented solution- each internal array can have different dimension.
Related
I have a 2-dimensional array of values looking like that:
In a different table, I have long strings with VALUE_1, VALUE_2 that can be found anywhere. It looks like in the table below:
Now, I want to write a program that translates the existing VALUE_1, VALUE_2 etc. in the long strings by adding the respective element in the 2nd dimension of the array (/BB, /CCC etc.) and if necessary duplicating and separating the values with a comma and a blank space. So VALUE_1 for example is turned into VALUE_1/BB, VALUE_1/A for each finding in the string. The result is supposed to look exactly like in the table below.
That's challenging. I my first approach I tried to locate the VALUE_1, VALUE_2 in the strings by using InStr() but I don't think that this will help me since only the first hit is taken into consideration. I need every occurrence.
For i = 1 To Worksheets("table2").Range("H1").End(xlDown).Row
For j = LBound(arr2) To UBound(arr2)
If InStr(Worksheets("table2").Range("H" & i), arr2(j, 0)) > 0 Then
Worksheets("table2").Range("H" & i).Font.Bold = True
End If
Next j
Next i
Use your 2D table to build a scripting dictionary so that value1 is associated with the concatenation of all column values in column 2 that have value 1 in the first column.
In the (untested) code below the array (ipArray)is that derived from the 2D range.
Public Function GetReplacements(ByVal ipArray As Variant) As Scripting.dictionary
Dim myD As Scripting.dictionary
Set myD = New Scripting.dictionary
Dim myIndex As Long
For myIndex = LBound(ipArray) To UBound(ipArray)
Dim myKey As String
myKey = ipArray(myIndex, 1)
Dim myItem As String
myItem = ipArray(myIndex, 2)
If myD.exists(myKey) Then
myD.Item(myKey) = myD.Item(myKey) & ", " & myKey & myItem
Else
myD.Add myKey, myKey & myItem
End If
Next
Set GetReplacements = myD
End Function
Now when you find an item such as "Value 1" you can replace with the value retrieved from the dictionary.
Building on #freeflow's excellent answer, I would also use a Scripting.Dictionary to hold the mappings from VALUE1 etc. to the target text.
I would then use Replace for each key in the Dictionary. You can loop like:
Dim key as Variant
For Each key in dict
Replace(<your string>, CStr(key), dict(key))
Next key
This will work so long as all your 'find' strings are totally unique i.e. none of them appears within another - so if you had "Value" and "Value 1" it would not work. Also, the simplest form of this method only works if there is a one-to-one mapping of text strings.
Thus, if your sample data is representative, you would want to look into using the Count argument of Replace so that you can replace the second occurrence of VALUE_4 with the different text, and so on.
I would do this by storing the dict values as an array e.g.
Dim my_arr(1 to 3) as String
my_arr(1) = "VALUE_4/CCC"
my_arr(2) = "VALUE_4/DDDD"
my_arr(3) = "VALUE_4/A"
dict.Add "VALUE_4", my_arr
Then when you are looping through, you can keep track of a counter (call it 'i' for example) and then you can just use Replace with a count of 1, increment 'i' by 1, and then use 'i' in each iteration to call on the relevant element of the array stored against VALUE_4 in the dict, like:
For Each key in dict
For i = LBound(dict(key)) to UBound(dict(key))
Replace (<your string>, CStr(key), dict(key)(i), 1, 1)
Next i
Next key
Hopefully you can build from there to what you need? Having reread your original post, I actually think my simplest solution would work (but I'll leave the more complex solution there in case it's of use to you or others), so long as dict is used to store the one-to-one mapping of, for example, "VALUE_1" to "VALUE_1/BB, VALUE_1/A" - you can loop through your original table and build those strings by concatenation - maybe even directly in the dict:
For Each cell in TableCol1 ' assuming it is cells, otherwise use an appropriate form of loop
tmp_str = cell.Value2
If dict.Exists(tmp_str) Then
dict(tmp_str) = dict(tmp_str) + ", " + tmp_str+cell.Offset(0,1).Value2
Else
dict.Add tmp_str, tmp_str + cell.Offset(0,1).Value2
End If
Next cell
I have a list of securities that are pipe delimited that I'm splitting into an array which works well. The issue I'm facing is that the ubound of my array is showing as zero, even though I can expand the array in my watch window and see the list of securities. Hoping someone could assist - code below:
tmpList = Right(tmpList, Len(tmpList) - 1)
arr_SecList = Array(Split(tmpList, Delim))
Debug.Print (tmplist)
For i = LBound(arr_SecList) To UBound(arr_SecList)
Debug.Print (arr_SecList(i))
Next
The result of the debug line is Test1|Test2|Test3
Try this. You can use Split directly to get an array without using Array
tmpList = Right(tmpList, Len(tmpList) - 1)
Debug.Print (tmpList)
arr_SecList = Split(tmpList, Delim)
Debug.Print (UBound(arr_SecList))
For i = LBound(arr_SecList) To UBound(arr_SecList)
Debug.Print (arr_SecList(i))
Next
I'm using LibreOffice Version: 4.4.3.2 Build ID: 40m0(Build:2) Locale: en_AU
I have a Basic Module
At the top of this module before any sub or functions I have
Type InitHeadings
MySort_By As Integer
MyCharacter As Integer
MyInitiative As Integer
MyRolled As Integer
MyTotal As Integer
End Type
...
Global InitiativeColumn As New InitHeadings
But when I run a sub, set a breakpoint and 'watch' the InitiativeColumn Object only the first two fields are shown.
The rest of my code relevant to this struct as the documentation calls them is below. I don't reference it anywhere else. Can anyone tell me why the first two would work but not the rest? I have two other structs in this code and both also ignore the last three fields. Is this a Bug?
Sub Main
'Initialise Doc and Sheet Objects
Dim Doc As Object
Doc = ThisComponent
StatsSheet = Doc.Sheets.getByName("Stats")
InitiativeSheet = Doc.Sheets.getByName("Initiative")
CombatSheet = Doc.Sheets.getByName("Combat")
'LOAD HEADING NAMES
'Initiative Sheet
For Column = 0 to 25 'Columns A to Z
MyHeadingName = InitiativeSheet.getCellByPosition(Column,0).String
Select Case MyHeadingName
Case "Sort By"
InitiativeColumn.MySort_By = Column
Case "Character"
InitiativeColumn.MyCharacter = Column
Case "Initiative"
InitiativeColumn.MyInitiative = Column
Case "Rolled"
InitiativeColumn.MyRolled = Column
Case "Total"
InitiativeColumn.MyTotal = Column
End Select
Next Column
End Sub
Sub MyInitiativeButton
'Iterate over a range of cells:
For Row = 1 To 25 'Rows 2 to 26
'Column 3 is column D the "Rolled" column
InitiativeSheet.getCellByPosition(InitiativeColumn.MyRolled,Row).VALUE = Roledice(1,20,0)
Next Row
End Sub
It looks like a bug, and seems to have been reported here. The problem did not occur when I tested it in a newer version (LO 5.1.0.3).
This is only an issue for the debugger window. The values are still there:
Sub TestStructs
InitiativeColumn.MySort_By = 5
InitiativeColumn.MyCharacter = 5
InitiativeColumn.MyTotal = 5
InitiativeColumn.DoesntExist = 5
End Sub
This code works fine until the line InitiativeColumn.DoesntExist = 5, whereupon it crashes.
Now the Global problem that you mentioned in the comments is really a problem. Considering the standard programming advice that global variables are bad, I think it's wise to consider alternatives.
Instead of a subroutine, could you perhaps use a Function that returns InitiativeColumn? If not, then assigning the variable as you suggested seems a viable workaround. Personally for LO macros I prefer Python or Java since they have classes.
Through VBA code, I shall determine the size of one array, say knownarray(). The size of this array may be different each time. Depending upon its size, I need to initialize another array of the same size.How should I do that
for eg:
knownarray() size = 4
so now i need to create an array of size 4 with name say newarray()
What I have done so far is something as follows:
ReDim NewArray(KnownArray.Size)
I know this must be easy but being a novice in VBA i am stuck. Can someone please help me out?
Thanking in advance
Sub Main()
Dim strArr(2 To 4) As String
strArr(2) = "element1"
strArr(3) = "element2"
ReDim anotherArr(LBound(strArr) To UBound(strArr)) As String
Debug.Print "the size:" = UBound(anotherArr) - LBound(anotherArr)
End Sub
Another way, especially if you want the values of the first array, is to just make a copy of the existing array:
Dim knownArray(2 To 4) As String
Dim copyArr() As String
copyArr() = knownArray()
Here copyArr is now another different array with the same dimensions and the same values. You would only do this if you wanted the values too. If not, the ReDim with bounds answer by #[me how] is the best solution.
I have a piece of code that does not seem to do what it is expected to do. VBA Arrays are mutable by all means, but it seems that when they are stored into a Dictionary as values of some keys, they are not mutable anymore. Any ideas?
Sub foo()
Dim mydict As New Dictionary
mydict.Add "A", Array(1, 2, 3)
MsgBox mydict("A")(1)
''# The above shows 2, which is fine
mydict("A")(1) = 34
MsgBox mydict("A")(1)
''# The above also shows 2, which is not fine
End Sub
It seems you'll need yet to set another var to update the array value.
mArray = mydict.Item(1)
mArray(1) = 34
mydict.Item(1) = mArray
I created a Procedure to solve the same issue, so I could keep it as a "oneliner":
Private Sub pReplaceDicArray(Dic As Object, kEy As Variant, Element As Integer, NewValue)
Dim tempArray As Variant
tempArray = Dic(kEy)
tempArray(Element) = NewValue
Dic(kEy) = tempArray
End Sub
' call as:
' Call mReplaceDicArray(Dic, "A", 1, 8)
I would have written this answer as a comment to Mr. Irizarry's answer, but I'm not allowed. Anyway.... I tried writing that last line of code (below) to assign the array to the first item of the dictionary, but it didn't work. The array in that item remained as it was before.
mydict.items(1) = mArray
Based on what I read elsewhere, it seems to have to do with the instance of the dictionary you're calling upon. I changed it to the following line and it worked.
mydict(mydict.keys(1)) = mArray
I'm still not sure why that is the case, but there it is.
Copy the Array and update the value:
mydict("A") = Array(mydict("A")(0), 34, mydict("A")(2))