I have a working code that loops through the rows of an array then store the values into another array. No problem at the code in fact but I am trying to improve my skills and learn new skills
This is the code
Sub Test()
Dim a, i As Long, j As Long, n As Long
a = Cells(1).CurrentRegion.Value
ReDim b(1 To UBound(a, 1) * UBound(a, 2))
For i = LBound(a) To UBound(a)
If i Mod 2 = 1 Then
For j = LBound(a, 2) To UBound(a, 2)
n = n + 1
If a(i, j) <> Empty Then b(n) = a(i, j)
Next j
Else
For j = UBound(a, 2) To LBound(a, 2) Step -1
n = n + 1
If a(i, j) <> Empty Then b(n) = a(i, j)
Next j
End If
Next i
Range("M2").Resize(n).Value = Application.Transpose(b)
End Sub
What I am trying to do is to compact the nested loop and this is my try
For j = iif(i mod2=1,LBound(a, 2) To UBound(a, 2),UBound(a, 2) To LBound(a, 2) Step -1)
But this seems not to ne valid. Any ideas?
This works well and I can figure it out with the help of the experts
Thanks a lot for everyone
Sub Test()
Dim a, i As Long, j As Long, n As Long
a = Cells(1).CurrentRegion.Value
ReDim b(1 To UBound(a, 1) * UBound(a, 2))
For i = LBound(a) To UBound(a)
For j = IIf(i Mod 2 = 1, LBound(a, 2), UBound(a, 2)) To IIf(i Mod 2 = 1, UBound(a, 2), LBound(a, 2)) Step IIf(i Mod 2 = 1, 1, -1)
n = n + 1
If a(i, j) <> Empty Then b(n) = a(i, j)
Next j
Next i
Range("N2").Resize(n).Value = Application.Transpose(b)
End Sub
Compacting Code
Option Explicit
Sub Test()
Dim a: a = Cells(1).CurrentRegion.Value
Dim ub1 As Long, ub2 As Long: ub1 = UBound(a): ub2 = UBound(a, 2)
ReDim b(1 To ub1 * ub2)
Dim i As Long, j As Long, n As Long, md As Long
For i = 1 To ub1
md = i Mod 2
For j = Abs(md - 1) * ub2 + md To md * ub2 + Abs(md - 1) _
Step 2 * (md - 1) + 1
n = n + 1
If a(i, j) <> Empty Then b(n) = a(i, j)
Next j
Next i
Range("M2").Resize(n).Value = Application.Transpose(b)
End Sub
Related
The datasets are small (10-25 lines), but I have to run a lot of them and it's getting to be a waste of time.
The original data looks like this:
The output needs to look like this:
Sub x()
Dim v, vOut(), i As Long, j As Long, k As Long, w
v = Sheet8.Range("A1").CurrentRegion.Value
ReDim vOut(1 To UBound(v, 1) * 10, 1 To 2)
For i = LBound(v, 1) To UBound(v, 1)
w = Split(v(i, 2), Chr(10))
For j = LBound(w) To UBound(w)
k = k + 1
vOut(k, 1) = v(i, 1)
vOut(k, 2) = w(j)
Next j
Next i
Sheet9.Range("A1").Resize(k, 2) = vOut
End Sub
I managed to separate the rows.
I'm lost on how to efficiently automate the rest of the formatting. I could do it through a bunch of Splits but feel there's probably a better way.
Messy, but eventually figured it out:
Sub VisitInfo()
Dim v, vOut(), i As Long, j As Long, k As Long, w, z
v = Sheet8.Range("A1").CurrentRegion.Value
ReDim vOut(1 To UBound(v, 1) * 10, 1 To 4)
For i = LBound(v, 1) To UBound(v, 1)
w = Split(v(i, 2), Chr(10))
For j = LBound(w) To UBound(w)
k = k + 1
vOut(k, 1) = v(i, 1)
z = Split(w(j), "]")
a = Split(w(j), "] ")
c = Split(a(1), " o")
b = Split(w(j), "=")
vOut(k, 2) = (z(0) + "]")
vOut(k, 3) = (c(0))
vOut(k, 4) = LTrim(b(1))
Next j
Next i
Sheet3.Range("A2").Resize(k, 4) = vOut
End Sub
I have a 3 by 3 matrix, where elements (1,1), (2,1), (2,2), (3,1), (3,2), (3,3) are given:
X . .
X X .
X X X
I need to write a program that writes out the missing elements, where (1,2)=(2,1), (1,3)=(3,1) and (2,3)=(3,2). I have written the following code:
Function kiegeszito(a)
For i = 1 To 3
For j = 1 To 3
If i < j Then
a(i, j) = a(j, i)
Else
a(i, j) = a(i, j)
End If
Next j
Next i
kiegeszito = a
End Function
However, this does not seem to work, could anybody help me why is this not working?
Just remove the Else condition:
Function kiegeszito(a)
For i = 1 To 3
For j = 1 To 3
If i < j Then a(i, j) = a(j, i)
Next j
Next i
kiegeszito = a
End Function
Get twin data in 2-dim matrix avoiding extra n*(n-1)/2 condition checks
The following approach
reduces the number of unnecessary condition checks by incrementing the 2nd loop starts
accepts any wanted base of 2-dim data:
Sub CompleteMatrix(ByRef data)
'count row|=column elements
Dim cnt As Long: cnt = UBound(data) - LBound(data) + 1
'fill missing twin data (identified by inverted indices)
Dim i As Long, j As Long
For i = LBound(data) To cnt - 1
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'next column starts from incremented row index
'(thus avoiding n*(n-1)/2 IF-conditions)
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For j = i + 1 To UBound(data, 2)
data(i, j) = data(j, i) ' assign twin data
Next j
Next i
End Sub
An example call creating e.g. a 1-based 2-dim datafield array might be
Sub ExampleCall()
Dim v: v = Tabelle3.Range("A1:C3").Value
CompleteMatrix v
End Sub
Further link
A practical example using such a mirrored array might be a distance array; a related post demonstrates how to apply the FilterXML() function thereon.
Fill Array
Using a method (fillArray) you could modify the array 'in place':
The Code
Option Explicit
Sub fillArrayTEST()
Dim Data As Variant: Data = Range("A1:C3").Value
debugPrint2D Data
fillArray Data
debugPrint2D Data
End Sub
Sub fillArray(ByRef Data As Variant)
Dim cCount As Long: cCount = UBound(Data, 2)
Dim i As Long, j As Long
For i = 1 To UBound(Data, 1)
For j = 1 To cCount
If i < j Then Data(i, j) = Data(j, i)
Next j
Next i
End Sub
Sub debugPrint2D(ByVal Data As Variant)
Dim i As Long, j As Long
For i = LBound(Data, 1) To UBound(Data, 1)
For j = LBound(Data, 2) To UBound(Data, 2)
Debug.Print "[" & i & "," & j & "]", Data(i, j)
Next j
Next i
End Sub
A Homage to T.M.'s Brilliant Solution
Sub completeMatrix(ByRef Data As Variant)
Dim rLower As Long: rLower = LBound(Data, 1)
Dim cLower As Long: cLower = LBound(Data, 2)
Dim iDiff As Long: iDiff = cLower - rLower
Dim cStart As Long: cStart = iDiff + 1
Dim cUpper As Long: cUpper = UBound(Data, 2)
Dim r As Long, c As Long
For r = rLower To UBound(Data, 1) - rLower
For c = cStart + r To cUpper
Data(r, c) = Data(c - iDiff, r + iDiff)
Next c
Next r
End Sub
Sub completeMatrixTEST()
Dim Data As Variant: ReDim Data(0 To 2, 2 To 4)
Data(0, 2) = 1
Data(1, 2) = 2
Data(1, 3) = 3
Data(2, 2) = 4
Data(2, 3) = 5
Data(2, 4) = 6
debugPrint2D Data
completeMatrix Data
'Range("G1").Resize(UBound(Data, 1) - LBound(Data, 1) + 1, _
UBound(Data, 2) - LBound(Data, 2) + 1).Value = Data
Debug.Print
debugPrint2D Data
End Sub
I am trying to slice the first dimension of 2D array. The array variable is y (1 to 36, 1 to 19)
I need to make it y(1 to 12, 1 to 19)
To do such task I have to loop like that
If rw > 0 Then
ListBox1.AddItem
ReDim v(1 To n, 1 To UBound(y, 2))
Dim i As Long, j As Long
For i = 1 To n
For j = 1 To UBound(y, 2)
v(i, j) = y(i, j)
Next j
Next i
ListBox1.List = v()
n = 0
End If
Is there an easier way to do such task without too much loops?
You can slice an array as below. Most of this code is just to populate the first array so you won't need:
Sub x()
Dim v1(1 To 6, 1 To 3), i As Long, j As Long, v2()
'this loop is just to populate the first array
For i = 1 To UBound(v1, 1)
For j = 1 To UBound(v1, 2)
v1(i, j) = i * j
Next j
Next i
'this is just to show the contents
Range("A1").Resize(UBound(v1, 1), UBound(v1, 2)) = v1
'this does the slicing (first three rows
v2 = Application.Index(v1, Evaluate("row(1:3)"), Application.Transpose(Evaluate("row(1:" & UBound(v1, 2) & ")")))
'this shows the sliced array
Range("F1").Resize(UBound(v2, 1), UBound(v2, 2)) = v2
End Sub
you could use Tranpose():
Dim v As Variant, v1 As Variant
v = Range("A1").Resize(36, 19).Value' just to fill a 36x19 array
v1 = Application.Transpose(v)
ReDim Preserve v1(1 To UBound(v, 2), 1 To 12)
v = Application.Transpose(v1)
I am trying to use a formula to show the 1st, 2nd, and 3rd most common text string in a column. This formula works but only if I designate a specific range with no blank cells. The issue with this is that the list is often updated by adding a line at the bottom so the range needs to be dynamic (or the entire column which is what I am trying to do).
=IFERROR(INDEX(C:C,MODE(IF(COUNTIF(U$1:U1,C:C)=0,MATCH(C:C,C:C,0)+{0,0}))),"")
Any insight is greatly appreciated.
First enter this VBA code in a standard module:
Public Function MostCommon(rng As Range) As Variant
Dim rng2 As Range, r As Range, C As Collection, arr(), arr2
Dim cKount As Long, i As Long, Kaller As Range, HowBig As Long
Set rng2 = Intersect(rng, rng.Parent.UsedRange)
Set C = New Collection
Set Kaller = Application.Caller
For Each r In rng2
If r.Value <> "" Then
On Error Resume Next
C.Add r.Value, CStr(r.Value)
On Error GoTo 0
End If
Next r
cKount = C.Count
ReDim arr(1 To cKount, 1 To 2)
For i = 1 To cKount
arr(i, 1) = C.Item(i)
arr(i, 2) = Application.WorksheetFunction.CountIf(rng2, arr(i, 1))
Next i
Call VBA_Sort(arr)
HowBig = Application.WorksheetFunction.Max(cKount, Kaller.Rows.Count)
ReDim arr2(1 To HowBig, 1 To 2)
For i = 1 To HowBig
arr2(i, 1) = ""
arr2(i, 2) = ""
Next i
For i = 1 To cKount
arr2(i, 1) = arr(i, 1)
arr2(i, 2) = arr(i, 2)
Next i
MostCommon = arr2
End Function
Public Sub VBA_Sort(InOut())
Dim i As Long, J As Long, Low As Long, _
Hi As Long, Temp As Variant
Low = LBound(InOut, 1)
Hi = UBound(InOut, 1)
J = (Hi - Low + 1) \ 2
Do While J > 0
For i = Low To Hi - J
If InOut(i, 2) < InOut(i + J, 2) Then
Temp = InOut(i, 2)
InOut(i, 2) = InOut(i + J, 2)
InOut(i + J, 2) = Temp
Temp = InOut(i, 1)
InOut(i, 1) = InOut(i + J, 1)
InOut(i + J, 1) = Temp
End If
Next i
For i = Hi - J To Low Step -1
If InOut(i, 2) < InOut(i + J, 2) Then
Temp = InOut(i, 2)
InOut(i, 2) = InOut(i + J, 2)
InOut(i + J, 2) = Temp
Temp = InOut(i, 1)
InOut(i, 1) = InOut(i + J, 1)
InOut(i + J, 1) = Temp
End If
Next i
J = J \ 2
Loop
End Sub
Then select a two-column block (like E1 through F50) and array-enter the following:
=MostCommon(C:C)
As you see, the function returns a short frequency table with the most frequent items at the top.
Array formulas must be entered with Ctrl + Shift + Enter rather than just the Enter key. If this is done correctly, the formula will appear with curly braces around it in the Formula Bar.
I need to join two arrays vd and vd1 into vdu.
ReDim vdu(1 To (UBound(vd, 1) + UBound(vd1, 1)), 1 To 1)
For i = 1 To UBound(vd, 1)
vdu(i, 1) = vd(i, 1)
Next i
For j = i To UBound(vdu, 1)
vdu(j, 1) = vd1(j - i + 1, 1)
Next j
First, I get and "out of range" error.
Second, in the end I will have 18 arrays that I'll need to join, so I don't know if this is the best idea for joining them.
try with
ReDim vdu(LBound(vd) To UBound(vd) + UBound(vd1), 1 To 1)
For i = LBound(vdu) To UBound(vdu)
If i <= UBound(vd) Then
vdu(i, 1) = vd(i, 1)
Else
vdu(i, 1) = vd1(i - UBound(vd), 1)
End If
Next i
Update for second part of question
I'd convert your merge code into a function
Public Function MergeArrays(arr1 As Variant, arr2 As Variant) As Variant
Dim i As Long
Dim arr As Variant
ReDim arr(LBound(arr1, 1) To UBound(arr1, 1) + UBound(arr2, 1), 1 To 1)
For i = LBound(arr, 1) To UBound(arr, 1)
If i <= UBound(arr1, 1) Then
arr(i, 1) = arr1(i, 1)
Else
arr(i, 1) = arr2(i - UBound(arr1, 1), 1)
End If
Next i
MergeArrays = arr
End Function
And then pass each array to it 1 at a time e.g.
arr = MergeArrays(vd1, vd2)
arr = MergeArrays(arr, vd3)
arr = MergeArrays(arr, vdx)
You could loop through this by storing your arrays in an array or dictionary and looping through that instead as well
Other option
Public Function MergeArrays(ParamArray arrays() As Variant) As Variant
Dim i As Long, j As Long, cnter As Long, UBoundArr As Long, OldUBoundArray As Long
Dim arr() As Variant
For j = LBound(arrays) To UBound(arrays)
UBoundArr = UBoundArr + UBound(arrays(j), 1)
Next j
ReDim arr(1 To UBoundArr, 1 To 1)
For j = LBound(arrays) To UBound(arrays)
For i = LBound(arrays(j)) To UBound(arrays(j))
arr(i + OldUBoundArray, 1) = arrays(j)(i, 1)
Next i
OldUBoundArray = OldUBoundArray + UBound(arrays(j), 1)
Next j
MergeArrays = arr
End Function
This method uses a ParamArray. If you're not sure what that is look it up but effectively you're able to pass an unspecified amount of arguments to the function. Therefore with this function you can combine any amount of arrays (of the same shape and same base i.e. x to x, 1 to 1) and it will combine them. Call like
arr = MergeArrays(vd, vd1, vd2,....,vd18)
getMasterArray will return an array that combines up to 60 different 2d arrays into one. getMasterArray also give you the option of returning a 0 or based array.
Sub TestgetMasterArray()
Dim data
data = getMasterArray(False, Range("List1").Value, Range("List2").Value, Range("List3").Value, Range("List4").Value)
Worksheets("Result").Range("A1").Resize(UBound(data), UBound(data, 2)).Value = data
End Sub
Function getMasterArray(Base0 As Boolean, ParamArray Arrays() As Variant)
Dim result As Variant, v As Variant
Dim Count As Long, Count2 As Long, lowBound As Integer, lOffset As Integer, x As Long, x1 As Long, y As Long
For Each v In Arrays
Count = Count + UBound(v) + IIf(LBound(v) = 0, 1, 0)
y = UBound(v, 2) + IIf(LBound(v, 2) = 0, 1, 0)
If y > Count2 Then Count2 = y
Next
lowBound = IIf(Base0, 0, 1)
ReDim result(lowBound To Count, lowBound To Count2)
For Each v In Arrays
If LBound(v, 2) > LBound(result, 2) Then
lOffset = -1
ElseIf LBound(v, 2) < LBound(result, 2) Then
lOffset = 1
End If
For x = LBound(v) To UBound(v)
For y = LBound(v, 2) To UBound(v, 2)
result(lowBound, y + lOffset) = v(x, y)
Next
lowBound = lowBound + 1
Next
Next
getMasterArray = result
End Function
Sample data generated by ockaroo.com