Pasting VBA Array into Excel Range - arrays

I know this question has been asked multiple times, but I am kinda stuck adopting the suggested solutions to my problem.
I have an array(0 to 4), that gets filled multiple times in a loop and should be pasted each time into a new line in excel.
Expected Output:
A | B | C | D | E
1 X1 | X2 | X3 | X4 | X5
My code:
r i = 0 To iVal
Dim infoarr(0 To 4) As Variant
infoarr(0) = ws_src_agv.Cells(ref + i + 3, 2).Value
infoarr(1) = ws_src_agv.Cells(ref + i + 4, 2).Value
infoarr(2) = ws_src_agv.Cells(ref + i + 3, 1).Value
infoarr(3) = ws_src_agv.Cells(ref + i + 3, 3).Value
infoarr(4) = ws_src_agv.Cells(ref + i + 3, 7).Value
lastR = ws_tgt_agv.Rows(Rows.Count).End(xlUp).Row
'First attempt:
ws_tgt_agv.Range(ws_tgt_agv.Cells(lastR + 1, 1), ws_tgt_agv.Cells(lastR + 1, 5)).Value = WorksheetFunction.Transpose(infoarr)
Output:
A | B | C | D | E
1 X1 | X1 | X1 | X1 | X1
2nd attempt:
ws_tgt_agv.Cells(lastR + 1, 1).Resize(UBound(infoarr, 1) + 1).Value = WorksheetFunction.Transpose(infoarr)
Ouput:
A | B | C | D | E
1 X1 | | | |
2 X2
3 X3
4 X4
5 X5
if leaving the transpose argument at the end the same range gets filled with sloley X1.
Thanks for your help!

A 1-D array (both zero based and one based) is aligned like a single row with multiple columns. You don't need to transpose in order to put te array's values into the worksheet; you only need the correct size of target.
with ws_tgt_agv
.Range(.Cells(lastR + 1, 1), .Cells(lastR + 1, 5)).Value = infoarr
end with
If you want to put the array's values into a single column of multiple rows then you need to transpose.
with ws_tgt_agv
.Range(.Cells(lastR + 1, 1), .Cells(lastR + 6, 1)).Value = Application.Transpose(infoarr)
end with

Related

How to transform array column cells in items rows to count occurrences with pySpark?

I have a dataset with multiple values in one column and I would like to count all occurrences of each values within all rows of the dataset.
initial dataset (example)
+----------------+
+ answers +
+----------------+
+ val1#val2#val3 +
+ val2 +
+ val3#val5 +
+----------------+
Values are separated by #.
goal:
+---------+-------+
+ values + count +
+---------+-------+
+ val1 + 1 +
+ val2 + 2 +
+ val3 + 2 +
+ val5 + 1 +
+---------+-------+
The code is in pySpark
I used the split function to get an array of values.
df.withColumn("new_col", F.split("answers", "\#").
I have a dataset with an array column but I don't find how to use it properly.
I looked for many samples and functions which could help me but I didn't find and due to the fact that pySpark dataset are immutable, I didn't find a way to transform the array and compute occurrences.
You can use a combination of split and explode to first get the values column.
Applying a groupBy on top of it would help achieve the desired counts.
from pyspark.sql.functions import split, explode
df2 = df.withColumn("values", explode(split("answers", "\#")))
df2.groupBy("values").count().show()
Output:
+------+-----+
|values|count|
+------+-----+
| val1| 1|
| val3| 2|
| val5| 1|
| val2| 2|
+------+-----+

Accessing an array with Pointer this way? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Its basically passing an array to a function via pointer. Then it prints out each element in the array. Im trying to understand a very specific line of pointers since my class has never done a example like this before. After finishing intro I never encountered this in the pointers topic, hence why Im confused. thank you.
main( )
{
inta[3][4] = {
1, 2, 3, 4,
5, 6, 7, 8,
9, 0, 1, 6
} ;
display ( a, 3, 4 ) ;
show ( a, 3, 4 ) ;
print ( a, 3, 4 ) ;
}
display ( int*q, introw, intcol )
{
inti, j ;
for ( i= 0 ; i< row ; i++ )
{
for ( j = 0 ; j < col ; j++)
printf("%d ",*(q+i*col+j)); // THIS is the line Im trying to understand
printf( "\n" ) ;
}
printf("\n" ) ;
}
Let's view the 2D array as a single array:
-------------------------------------------------
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 6 |
-------------------------------------------------
^
index 0
And if we take a closer look at the expression (q+i*col+j):
q + (0 * 4) + 0 = 0th index
q + (0 * 4) + 1 = 1st index
q + (0 * 4) + 2 = 2nd index
q + (0 * 4) + 3 = 3rd index
^ ^
i j
After the first row has been printed, i becomes 1:
-------------------------------------------------
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 6 |
-------------------------------------------------
^
4th index
q + (1 * 4) + 0 = 4
q + (1 * 4) + 1 = 5
q + (1 * 4) + 2 = 6
q + (1 * 4) + 3 = 7
^ indices
As you can see, this points to the next row.
i becomes 2:
-------------------------------------------------
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 6 |
-------------------------------------------------
^
q + (2 * 4) + 0 = 8
q + (2 * 4) + 1 = 9
q + (2 * 4) + 2 = 10
q + (2 * 4) + 3 = 11
^ indices
And so on. Hope this helps
*(q+i*col+j) is equivalent to q[i*col+j].
When using pointer arithmetic, adding a number x to a pointer evaluates to an address x bytes times the size of the pointer type ahead of the initial address (offset).

How to add a counter column to existing matrix in VBA?

How to get a new matrix in VBA with a counter value in the first "column". Suppose we have a VBA matrix which values we get from cells. The value of A1 cell is simply "A1".
Dim matrix As Variant
matrix = Range("A1:C5").value
Input matrix:
+----+----+----+
| A1 | B1 | C1 |
+----+----+----+
| A2 | B2 | C2 |
+----+----+----+
| A3 | B3 | C3 |
+----+----+----+
| A4 | B4 | C4 |
+----+----+----+
| A5 | B5 | C5 |
+----+----+----+
I would like to get new matrix with the counter value in the first column of VBA matrix.
Here are desired results:
+----+----+----+----+
| 1 | A1 | B1 | C1 |
+----+----+----+----+
| 2 | A2 | B2 | C2 |
+----+----+----+----+
| 3 | A3 | B3 | C3 |
+----+----+----+----+
| 4 | A4 | B4 | C4 |
+----+----+----+----+
| 5 | A5 | B5 | C5 |
+----+----+----+----+
One way to do it is looping. Would there be any other more elegant way to do it? We are dealing here with large data sets, so please mind the performance.
If your main concern is the performance, then use Redim Preserve to add a new column at the end and use the OS API to shift each column directly in the memory:
Private Declare PtrSafe Sub MemCpy Lib "kernel32" Alias "RtlMoveMemory" ( _
ByRef dst As Any, ByRef src As Any, ByVal size As LongPtr)
Private Declare PtrSafe Sub MemClr Lib "kernel32" Alias "RtlZeroMemory" ( _
ByRef src As Any, ByVal size As LongPtr)
Sub AddIndexColumn()
Dim arr(), r&, c&
arr = [A1:F1000000].Value
' add a column at the end
ReDim Preserve arr(LBound(arr) To UBound(arr), LBound(arr, 2) To UBound(arr, 2) + 1)
' shift the columns by 1 to the right
For c = UBound(arr, 2) - 1 To LBound(arr, 2) Step -1
MemCpy arr(LBound(arr), c + 1), arr(LBound(arr), c), (UBound(arr) - LBound(arr) + 1) * 16
Next
MemClr arr(LBound(arr), LBound(arr, 2)), (UBound(arr) - LBound(arr) + 1) * 16
' add an index in the first column
For r = LBound(arr) To UBound(arr)
arr(r, LBound(arr, 2)) = r
Next
End Sub
Method 1
This method inserts cells to the left of the range and set the new cells formula to calculate the counter =ROWS($A$1:$A5). Note: this pattern is also used to calculate a running total.
Usage
InsertCounter Worksheets("Sheet1").Range("A1:C5")
Sub InsertCounter(Target As Range)
Dim counterCells As Range
Target.Columns(1).Insert Shift:=xlToRight
Set counterCells = Target.Columns(1).Offset(0, -1)
counterCells.Formula = "=Rows(" & counterCells.Cells(1, 1).Address(True, True) & ":" & counterCells.Cells(1, 1).Address(False, True) & ")"
End Sub
Method 2
This method copies the Ranges' Values into an array, creates a new array with 1 extra column and then copies the data and a counter over to the new array. The difference in this Method is that it doesn't insert any cells.
Usage
AddCounterToMatrix Worksheets("Sheet1").Range("A1:C5")
Sub AddCounterToMatrix(Target As Range)
Dim x As Long, y As Long
Dim Matrix1 As Variant, NewMatrix1 As Variant
Matrix1 = Target.Value
ReDim NewMatrix1(LBound(Matrix1) To UBound(Matrix1), LBound(Matrix1, 2) To UBound(Matrix1, 2) + 1)
For x = LBound(Matrix1) To UBound(Matrix1)
NewMatrix1(x, 1) = x - LBound(Matrix1) + 1
For y = LBound(Matrix1, 2) To UBound(Matrix1, 2)
NewMatrix1(x, y + 1) = Matrix1(x, y)
Next
Next
Target.Resize(UBound(NewMatrix1) - LBound(Matrix1) + 1, UBound(NewMatrix1, 2) - LBound(NewMatrix1, 2) + 1).Value = NewMatrix1
End Sub
using a Dynamic variant is fast.
Sub test()
Dim matrix As Variant, newMatrix()
Dim i As Long, n As Long, c As Long, j As Long
matrix = Range("A1:C5").Value
n = UBound(matrix, 1)
c = UBound(matrix, 2)
ReDim newMatrix(1 To n, 1 To c + 1)
For i = 1 To n
newMatrix(i, 1) = i
For j = 2 To c + 1
newMatrix(i, j) = matrix(i, j - 1)
Next j
Next i
Range("a1").Resize(n, c + 1) = newMatrix
End Sub
excel based solution are ok for u?
Columns("A:A").Select
Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Range("A1") = "1"
Range("A2") = "2"
Range("A1:A2").Select
Selection.AutoFill Destination:=Range("A1:A5")
Dim matrix As Variant
matrix = Range("A1:D5").Value
Why not a compromise between household remedies and pure array scripting by inserting a temporary column and doing the rest within the array's first column.
Code
Option Explicit
Public Sub test_CounterCol2()
Dim matrix As Variant, newMatrix()
Dim i As Long, n As Long
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("CounterCol") ' <== user defined sheet
' a) insert column temporarily]
ws.Columns("A:A").Insert Shift:=xlToRight
' b) get values
matrix = ws.Range("A1:D5").value
' c) only loop within array counter column
n = UBound(matrix, 1)
For i = 1 To n
matrix(i, 1) = i
Next i
' d) delete temporary insertion
ws.Columns("A:A").Delete (xlShiftToLeft)
End Sub
Additional note: Maybe you can find something via API (CopyMemory).

How to speed up a join and sum product query in SQL Server 2008?

I have two tables A and B. A has float variables X1, X2, X3, ... , X9, sumprod and 14,000 rows. B has float variables X1, X2, X3, ... , X9, a text variable 'Model' with values such as 'Model 1', 'Model 2' and so on, and 50 rows.
I am trying to join and performs a sumproduct operation using the following code:
Update A set a.sumprod = a.X1*b.X1 + a.X2*b.X2 + ... + a.X9*b.X9
from a left join b
on b.Model = 'Model 2';
I have multiple such queries with different tables as A, and corresponding different join conditions on the Model variable in table B. I have identified these queries as taking the longest time in my stored procedure and am looking for a way to make them faster.
I have tried variants of this query like below without any material changes in runtime:
Variant 1:
Update A
set a.sumprod = a.X1*b.X1 + a.X2*b.X2 + ... + a.X9*b.X9
from a left join b
on 1 = 1
where b.Model = 'Model 2';
Variant 2:
merge A
using (select X1, X2, ..., X9 from B where Model = 'Model 2') C
on 1 = 1
when matched then update
set sumprod = a.X1*c.X1 + a.X2*c.X2 + ... + a.X9*c.X9;
Edit for greater clarity:
There are multiple table A's: A1, A2, A3, ... Each table A# contains explanatory variables (X1, X2 etc) for a model (corresponding to the model number in table B).
So table A1 may be:
X1 | X2 | X3 | X4 | Sumprod
6 | 7 | 3 | 5 |
5 | 3 | 4 | 4 |
...
Table A2 would have a different number of explanatory variables, and the explanatory variables themselves would be different. Also, the number of rows would be different from A1.
Table B has model coefficients for each model like so:
Model | X1 | X2 | X3 | X4 | X5 | X6 | X7 | X8 | X9
Model 1 | 3 | 2 | 5 | 9 | 0 | 0 | 0 | 0 | 0
Model 2 | 4 | 7 | 8 | 3 | 5 | 8 | 0 | 0 | 0
...
Model 1 has four explanatory variables, so the Model 1 row in table B has zero coefficients for columns X5 onwards.
What I want to do in the sumprod column of each table A is take the sum product of the explanatory variables and the coefficients from the correct row in table B. There is no common row identifier between the table A's and the coefficient table B. I am taking the sum product of EACH row in A1 with a SINGLE row in B.
After the join, I want the sumprod column of table A1 to be populated as below:
X1 | X2 | X3 | X4 | Sumprod
1 | 7 | 3 | 5 | 6*3 + 7*2 + 3*5 + 5*9 = 92
5 | 3 | 4 | 4 | 5*3 + 3*2 + 4*5 + 4*9 = 77
...
Values for the explanatory variables are fixed but values for the model coefficients are user inputs and are expected to change fairly often.
From the initial comments, it seems that this is not a good database structure for what I want to do. Any suggestions for how I can make this faster?
FROM a LEFT JOIN b ON b.Model = 'Model 2'? I have no idea what behavior you're expecting from this join, and I suspect the query engine is equally confused. Do you actually want a CROSS JOIN? You should just say CROSS JOIN then.
Here's what I would do:
UPDATE a
SET a.sumprod = a.X1 * b1.X1 + a.X2 * b1.X2 +...+ a.X9 * b1.X9
FROM a
CROSS JOIN (
SELECT Model, X1, X2, ..., X9 FROM b where Model = 'Model 2'
) b1
WHERE a.sumprod <> a.X1 * b1.X1 + a.X2 * b1.X2 +...+ a.X9 * b1.X9
OR a.sumprod is NULL;
Is there a reason that you have to CROSS JOIN? Is there truly no relation between a and b? That seems like a design problem. You want to make everything in a.sumprod be a function of what's in one row of b? Are you planning to change that repeatedly? You've abstracted too far to tell what you're trying to accomplish.
Personally, I would create a VIEW that returned the necessary product sums rather than updating a field in a as storing aggregates is a generally poor idea, but if you're already having performance issues that may not be wise.

redim nested array

i have an array path... this array contains a list of nested array paths. it could look something like this:
path( 0 1 2 3 4 5 6 7 8 )
"1" | 1, 1, 1, 1, 1, 1, 1, 1, 1 |
"2" | 4, 3, 1, 4, 2, 3, 4, 3, 2 |
"3" | 1, 1, , 2, 1, 2, 3, 3, 2 |
"Val" A, B, C, D, E, F, G, H, I
now i have a small loop to get the maximum of the second row.
x = 1
For c = 0 To UBound(path)
If IsArray(path(c)) Then
If CInt(path(c)(x)) <= maxDimension1 Then
maxDimension1 = CInt(path(c)(x))
End If
End If
Next
redim preserve pathValues(maxDimension1 - 1)
i must now find the maximum number of elements for the elements in row "2" and redim the array-Element in pathValues to this.
i tried:
For Dimension2 = 1 To maxDimension1
For c = 0 To UBound(Path)
If IsArray(Path(c)) Then
If CInt(Path(c)(x)) = Dimension2 Then
If CInt(Path(c)(2)) >= maxDimension2 Then
maxDimension2 = CInt(Path(c)(2))
End If
End If
End If
redim PathValues(c)(maxDimension2) //Syntax Error
next
next
is there a way to avoid a workaround with multidimensional array?
for explanation: the pathValues would look like this in the end:
PathValues() = (C,(E, I),(B, F, H),(A, D, G))
I fixed it by recursively calling a function that uses x as "depth" and the full path to create one single array containing empty elements for the values that get written in later.
one just needs to add a statement sorting out the upper bounds you do not want because they belong to other arrays. for all else it works fine
Function iterate_Path(path As Variant, x As Integer, value_x As Variant) As Variant
Dim insideArray, returnPath
returnPath = Array()
For c = 0 To UBound(path)
If IsArray(path(c)) Then
If CInt(path(c)(x)) = value_x Then
If x <> UBound(path(c)) Then
If CInt(path(c)(x)) > UBound(returnPath) + 1 Then
ReDim Preserve returnPath(CInt(path(c)(x)) - 1)
End If
returnPath(path(c)(x) - 1) = iterate_Path(path, x + 1, path(c)(x))
ElseIf CInt(path(c)(x)) > UBound(returnPath) + 1 Then
ReDim Preserve returnPath(CInt(path(c)(x)) - 1)
End If
ElseIf CInt(path(c)(x)) > UBound(returnPath) + 1 Then
ReDim Preserve returnPath(CInt(path(c)(x)) - 1)
If x + 1 = UBound(path(c)) Then
returnPath(CInt(path(c)(x)) - 1) = iterate_Path(path, x + 1, path(c)(x))
End If
ElseIf x + 1 = UBound(path(c)) Then
If CInt(path(c)(x)) > UBound(returnPath) + 1 Then
ReDim Preserve returnPath(CInt(path(c)(x)) - 1)
End If
returnPath(CInt(path(c)(x)) - 1) = iterate_Path(path, x + 1, path(c)(x))
End If
Else
returnPath(CInt(path(c)(x)) - 1) = Empty
End If
Next
iterate_Path = returnPath
End Function

Resources