I'm trying to populate part of an array with 0's and wondering if there was a better way then to loop through it.
I know I could use Dim tempArr as double to do this but the first column in the array contains strings.
I'm currently using
Dim tempArr as Variant
ReDim tempArr(1 To 6, 1 To 1 + (EndWeek - (BeginWeek - 1)))
tempArr(1, 1) = "Monday - Friday"
tempArr(2, 1) = "Saturday"
tempArr(3, 1) = "Sunday"
tempArr(4, 1) = "Bank Holiday"
tempArr(5, 1) = "Annual Leave"
tempArr(6, 1) = "Apprentice"
For i = 1 To 6
For j = 2 To UBound(tempArr, 2)
tempArr(i, j) = CDbl(0)
Next j
Next i
But surely there's a better way?
Related
Dim HighScoreOneHourData() As Integer
Dim HighScoreOneHourDates() As String
ReDim HighScoreOneHourData(1 To UBound(SA, 1) - 3)
ReDim HighScoreOneHourDates(1 To UBound(SA, 1) - 3)
For j = 4 To UBound(SA, 1)
HighScoreOneHourData(j - 3) = CInt(Val(SA(j, PositionInArray + DataColumn + 2)))
HighScoreOneHourDates(j - 3) = SA(j, 1)
Next j
SortSheet.Range("A1:A" & UBound(HighScoreOneHourDates)) = HighScoreOneHourDates
SortSheet.Range("B1:B" & UBound(HighScoreOneHourData)) = HighScoreOneHourData
When these last two lines in the example above are executed all the cells in the sheets are filled with the first element from the arrays.
HighScoreOneHourDates is an array filled with consecutive dates. Still only the first date is printed to the sheet.
I've stopped the code and checked the state of the arrays and the they are correctly filled.
Anyone knows why the cells are filled with the first element?
It's been explained why 1D arrays don't work for you. A better fix is to Dim them as 2D
ReDim HighScoreOneHourData(1 To UBound(SA, 1), 1 To 1) As Integer
ReDim HighScoreOneHourDates(1 To UBound(SA, 1), 1 To 1) As String
For j = 4 To UBound(SA, 1)
HighScoreOneHourData(j - 3, 1) = CInt(Val(SA(j, PositionInArray + DataColumn + 2)))
HighScoreOneHourDates(j - 3, i) = SA(j, 1)
Next j
SortSheet.Range("A1:A" & UBound(HighScoreOneHourDates, 1)) = HighScoreOneHourDates
SortSheet.Range("B1:B" & UBound(HighScoreOneHourData, 1)) = HighScoreOneHourData
A 1D array always wants to be placed on a sheet in a row, not a column. That's why you only get the first element repeated. You need to re-orient the array to put it in a column, or make your arrays 2D (1 To numHere, 1 To 1)
Note there is a limit to the array size you can pass to Transpose of around 63-64k elements.
Assuming your arrays are 1-based you can do this:
SortSheet.Range("A1:A" & UBound(HighScoreOneHourDates)) = _
Application.Transpose(HighScoreOneHourDates)
for example.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
In Excel or Excel Macro, I am trying to figure out an alghorithm/formula to reach a number with using highest ones as possible but there are some limitations. Let me explain with an example;
Let's say I have 5 numbers in 5 cells (line by line) to use (2,5,10,20,50) and I need to reach 98.
In this example, I should only use 50 + 20 + 20 + 5 + 2 + 2. Even if I can't reach exact number (like reaching 98), it should be the results with minimum exceeds (like 99). It can't be lower than the target number.
I will copy these rows (as fetching their numbers in their first column) and paste to another sheet from the list. Please think these numbers to select rows, so I could find the appropriate rows as finding the right algorithm / formula for it.
How about this:
Put your data for example (2,5,10,20,50) into A1:A5 at the top of sheet1.
Put the below code into a vba code module, and run Algorithm.
I wrote the result into column C, but modify as needed.
Sub Algorithm()
Dim Tgt As Double, currVal As Double
Dim iRow As Integer, oRow As Integer 'input and output row
Dim ary, sortedAry 'input arrays
Dim aResult(1 To 100, 1 To 1) 'assuming result is less than 100 rows long
Tgt = 98 'Target number
ary = Sheet1.Range("A1").CurrentRegion 'put data into an array
sortedAry = BubbleSort(ary, False) 'sort in descending order
iRow = 1
oRow = 1
currVal = 0
Do Until iRow > UBound(sortedAry)
If currVal + sortedAry(iRow, 1) > Tgt Then
iRow = iRow + 1 'goto next smallest number
Else
aResult(oRow, 1) = sortedAry(iRow, 1)
currVal = currVal + sortedAry(iRow, 1)
oRow = oRow + 1
End If
Loop
'one more so it goes over Tgt
aResult(oRow, 1) = sortedAry(UBound(sortedAry), 1)
'put results back into spreadsheet
Sheet1.Range("C1").Resize(100, 1) = aResult
End Sub
Function BubbleSort(myArray As Variant, Optional Ascending = True)
'suitable to sort a small list that is in a 1 column array
Dim i As Long, j As Long
Dim Temp As Variant
If Ascending = True Then
For i = LBound(myArray, 1) To UBound(myArray, 1) - 1
For j = i + 1 To UBound(myArray)
If myArray(i, 1) > myArray(j, 1) Then
Temp = myArray(j, 1)
myArray(j, 1) = myArray(i, 1)
myArray(i, 1) = Temp
End If
Next j
Next i
Else
For i = LBound(myArray, 1) To UBound(myArray, 1) - 1
For j = i + 1 To UBound(myArray)
If myArray(i, 1) < myArray(j, 1) Then
Temp = myArray(j, 1)
myArray(j, 1) = myArray(i, 1)
myArray(i, 1) = Temp
End If
Next j
Next i
End If
BubbleSort = myArray
End Function
I'm using the =Filter() function to produce a dynamic array that I am referencing in another cell. Where the output is of the format: example1
However, I'd like to return the array in the format: example2 with a gap before each record where the "type" changes.
I've tried using a helper column and using an IF statement to enter a blank if the previous record had a different value in column A, and then concatenating the cell reference using the =COUNTBLANK() function to refer to correct record. However, this resulted in a different number of blank rows depending on the order of column A.
Is there a better way of achieving this result?
Thanks,
-L.
First put this User Defined Function in a standard module:
Option Explicit
Public Function separate(rng As Range)
Dim arr
arr = rng
Dim U As Long, i As Long, j As Long
Dim L As Long
arr = rng
U = UBound(arr, 1)
L = LBound(arr, 1)
ReDim arrbig(1 To U * 2, 1 To 2)
j = 2
arrbig(1, 1) = arr(1, 1)
arrbig(1, 2) = arr(1, 2)
For i = 2 To U
If arr(i, 1) <> arr(i - 1, 1) Then
arrbig(j, 1) = ""
arrbig(j, 2) = ""
j = j + 1
End If
arrbig(j, 1) = arr(i, 1)
arrbig(j, 2) = arr(i, 2)
j = j + 1
Next i
j = j - 1
ReDim temp(1 To j, 1 To 2)
For i = 1 To j
temp(i, 1) = arrbig(i, 1)
temp(i, 2) = arrbig(i, 2)
Next i
separate = temp
End Function
If we start with raw data in cols A and B like:
We create a dynamic array in cols D and E by putting:
=FILTER(A1:B15,(A1:A15<>"junk")*(A1:A15<>"Peaches"),"X")
in D1:
We create the formatted array in cols G and H by putting this formula in G1:
=separate(D1#)
I have the following code throwing a Type MisMatch Error on the line highlighted below. The code is supposed to simply identify empty elements in the input array (stat5) and replace them with a single period in the output array (ArrayOut). However, when it encounters the empty element in position (9,2) of stat5, it throws the Type MisMatch error:
From what I have read about the Type MisMatch error, it sounds like data assigned to a variable does not match the type of the variable. However, in my case, I am encountering the error EVEN when I attempt to simply call the value of stat5(9,1) in the Debug.Print statement below. It hangs on the Print statement (which is not an assignment statement, so I can't figure out what is mismatching with what):
Here is the code:
ReDim ArrayOut(1 To X1N, 1 To 1)
For i2 = 1 To X1N
If Len(Trim(stat5(i2, 2))) = 0 Then
ArrayOut(i2, 1) = "."
Else
ArrayOut(i2, 1) = stat5(i2, 2)
End If
Next i2
Here is the data in the input array (In this particular case, the non-missing values of the 2nd column of the array are constant, but this is not always the case). These are the values of stat5 when the error occurs in iteration 9 of the loop:
1 0.500285714
2 0.500285714
3 0.500285714
4 0.500285714
5 0.500285714
6 0.500285714
7 0.500285714
8 0.500285714
9
10 0.500285714
=========================================================
Here is the code that sets up stat5. All of this code runs without error:
Dim stat1 As Variant
Dim stat2 As Variant
Dim stat3 As Variant
Dim stat4 As Variant
Dim stat5 As Variant
'ReCombine Results for Final Printing
'Add ObsNum to each
'Prediction Set Results
ReDim stat1(1 To X3N, 1 To 2)
For i2 = 1 To X3N
stat1(i2, 1) = X3(i2, 1)
If Pred = True Then
If NumberOfArrayDimensions(ArrayPredIn) = 1 Then
stat1(i2, 2) = ArrayPredIn(i2)
Else
stat1(i2, 2) = ArrayPredIn(i2, 1)
End If
Else
stat1(i2, 2) = ""
End If
Next i2
'Estimation Set ResultS
ReDim stat2(1 To UBound(X4, 1), 1 To 2)
If NumberOfArrayDimensions(ArrayEstIn) = 1 Then
For i2 = 1 To UBound(X5, 1)
stat2(i2, 1) = X4(i2, 1)
stat2(i2, 2) = ArrayEstIn(i2)
Next i2
Else
For i2 = 1 To UBound(X5, 1)
stat2(i2, 1) = X4(i2, 1)
stat2(i2, 2) = ArrayEstIn(i2, 1)
Next i2
End If
'Concatenate stat1, stat2, stat3
ReDim stat5(1 To X1N, 1 To 2)
stat5 = Combine(stat1, stat2)
QuickSortArray stat5, , , 1
Here is the Combine function:
Function Combine(a As Variant, b As Variant, Optional stacked As Boolean = True) As Variant
'assumes that A and B are 2-dimensional variant arrays
'if stacked is true then A is placed on top of B
'in this case the number of rows must be the same,
'otherwise they are placed side by side A|B
'in which case the number of columns are the same
'LBound can be anything but is assumed to be
'the same for A and B (in both dimensions)
'False is returned if a clash
Dim lb As Long, m_A As Long, n_A As Long
Dim m_B As Long, n_B As Long
Dim m As Long, N As Long
Dim C As Variant
If TypeName(a) = "Range" Then a = a.value
If TypeName(b) = "Range" Then b = b.value
lb = LBound(a, 1)
m_A = UBound(a, 1)
n_A = UBound(a, 2)
m_B = UBound(b, 1)
n_B = UBound(b, 2)
If stacked Then
m = m_A + m_B + 1 - lb
N = n_A
If n_B <> N Then
Combine = False
Exit Function
End If
Else
m = m_A
If m_B <> m Then
Combine = False
Exit Function
End If
N = n_A + n_B + 1 - lb
End If
ReDim C(lb To m, lb To N)
For i = lb To m
For j = lb To N
If stacked Then
If i <= m_A Then
C(i, j) = a(i, j)
Else
C(i, j) = b(lb + i - m_A - 1, j)
End If
Else
If j <= n_A Then
C(i, j) = a(i, j)
Else
C(i, j) = b(i, lb + j - n_A - 1)
End If
End If
Next j
Next i
Combine = C
End Function
=========================================================================
UPDATE: When I check the type of each element of stat5, the type of element (9,2) is 8204 (while all the other (i2,2) elements are 5 = vbdouble). However, I'm not sure what this means: Has a multi-element array been saved to the (9,2) element of the array stat5? Is that possible?
I tried wrapping all of the calls to stat5(i2,2) with CStr(), hoping to convert the info in (9,2) to a string, but this also gives the same Type MisMatch error.
Locals window output:
===============================================================================
Results from the Locals window:
However, the variable type according to VarType() for elements (1-8,2) is vbDouble ("5") which seems to contradict the results in the Locals window:
===============================================================================
UPDATE: I was able to reproduce the error with stat1 BEFORE the Combine function was used to create stat5. Therefore, I do not believe that the problem is with the Combine function.
In Excel when we try to find some phrase we can put asterisk * inside as any other character. But how to do it inside VBA macro? For example below;
I want to find the secName by searching the value of firName with asterisk but id doesn't work. I suppose the problem is that VBA thinks that i want to find exactly * as normal character instead of anything.
Dim firName, secName As String
firName = "Da*"
secName = "Daniel"
search = InStr(1, secName, firName, vbTextCompare)
MsgBox (search)
Is it possible to use asterisk * in the way I described?
You can either do a FuzzySearch like: Matching similar but not exact text strings in Excel VBA projects, …
… or you can use the The Levenshtein Distance to find out how similar 2 strings are which is probably more accurate but needs O(n*m) time for calculation. So don't use it on very long strings.
Function Levenshtein(str1 As String, str2 As String) As Long
Dim arrLev As Variant, intLen1 As Long, intLen2 As Long, i As Long
Dim j As Long, arrStr1 As Variant, arrStr2 As Variant, intMini As Long
intLen1 = Len(str1)
ReDim arrStr1(intLen1 + 1)
intLen2 = Len(str2)
ReDim arrStr2(intLen2 + 1)
ReDim arrLev(intLen1 + 1, intLen2 + 1)
arrLev(0, 0) = 0
For i = 1 To intLen1
arrLev(i, 0) = i
arrStr1(i) = Mid(str1, i, 1)
Next i
For j = 1 To intLen2
arrLev(0, j) = j
arrStr2(j) = Mid(str2, j, 1)
Next j
For j = 1 To intLen2
For i = 1 To intLen1
If arrStr1(i) = arrStr2(j) Then
arrLev(i, j) = arrLev(i - 1, j - 1)
Else
intMini = arrLev(i - 1, j) 'deletion
If intMini > arrLev(i, j - 1) Then intMini = arrLev(i, j - 1) 'insertion
If intMini > arrLev(i - 1, j - 1) Then intMini = arrLev(i - 1, j - 1) 'deletion
arrLev(i, j) = intMini + 1
End If
Next i
Next j
Levenshtein = arrLev(intLen1, intLen2)
End Function
The smaller the returned number is the more similar are the strings.
For example:
Debug.Print Levenshtein("OFFICE CLUB, S.A.", "OFFICE CLUB SA") 'returns 3
Debug.Print Levenshtein("OFFICE CLUB, S.A.", "OFFICE CLUB S.A.") 'returns 1
The second strings are more similar than the first ones.