New to VBA. I'm trying to create an array of rows.
Basically, I have an entire sheet and want to take all the rows that start with a certain value ("MA") in column 8.
I eventually want to manipulate that array (as if it were a range), and paste it somewhere else in the sheet. Can anyone help? Here's my code so far:
Dim top0M As Variant
ReDim top0M(1 To 1) As Variant
For i = 4 To Rows.Count
If Cells(i, 8).Value Like "MA*" Then
top0M(UBound(top0M)) = Rows(i)
ReDim Preserve top0M(1 To UBound(top0M) + 1) As Variant
End If
Next i
This code runs but I'm not sure how to debug it to know if I even have the right rows inside. Can I paste these rows as if they were a range?
This sets the range and loads the whole into an array then it loads a different array with the lines that you want:
With ActiveSheet 'This should be changed to the name of the worksheet: Worksheets("MySheet")
Dim rng As Range
Set rng = .Range(.Cells(4, 1), .Cells(.Cells(.Rows.Count, 1).End(xlUp).Row, .Cells(4, .Columns.Count).End(xlToLeft).Column))
Dim tot As Variant
tot = rng.Value
Dim top0M As Variant
ReDim top0M(1 To Application.CountIf(.Range("H:H"), "MA*"), 1 To UBound(tot, 2)) As Variant
Dim k As Long
k = 1
Dim i As Long
For i = LBound(tot, 1) To UBound(tot, 1)
If tot(i, 8) Like "MA*" Then
Dim j As Long
For j = LBound(tot, 2) To UBound(tot, 2)
top0M(k, j) = tot(i, j)
Next j
k = k + 1
End If
Next i
End With
'to print to a sheet just assign the values:
Worksheets("sheet1").Range("A1").Resize(UBound(top0M, 1), UBound(top0M, 2)).Value = top0M
Try this code
Sub Test()
Dim x As Variant
x = ActiveSheet.Range("A4").CurrentRegion.Value
x = FilterArray(x, 8, "MA*", True)
ActiveSheet.Range("K14").Resize(UBound(x, 1), UBound(x, 2)).Value = x
End Sub
Function FilterArray(ByVal myRefArr As Variant, ByVal col As Integer, ByVal refValue As String, ByVal equal As Boolean) As Variant
Dim a As Variant
Dim i As Long
Dim j As Long
Dim n As Long
On Error Resume Next
n = 1
If refValue = "" Then
FilterArray = myRefArr
Else
ReDim a(1 To UBound(myRefArr, 1), 1 To UBound(myRefArr, 2))
For i = 1 To UBound(a, 1)
If IIf(equal, UCase(myRefArr(i, col)) Like UCase(refValue), Not (UCase(myRefArr(i, col)) Like UCase(refValue))) Then
For j = 1 To UBound(a, 2)
a(n, j) = myRefArr(i, j)
Next j
n = n + 1
End If
Next i
a = Application.Transpose(a)
ReDim Preserve a(1 To UBound(a, 1), 1 To n - 1)
a = Application.Transpose(a)
FilterArray = a
End If
On Error GoTo 0
End Function
Related
Credit for code is for few editors in Mr . Excel forum. This code works like a charm, but I need it to copy the entire row of the new data, rather than only values from column A. Now I tried to play with true and false statements and etc. but to no avail, I believe it is out of my scope and id like so suggestions or assistance how to achieve my mission. I have simple values, no formulas, just some named columns and thousands of rows in original file and extract file.
Sub AddMissingItems()
Dim Dic As Object
Dim Arr() As Variant, outArr() As Variant
Dim i As Long, k As Long, iRow As Long
Dim c as long
Set Dic = CreateObject("Scripting.dictionary")
With Sheets("Sheet1")
c = .Cells(1, Columns.Count).End(xlToLeft).Column
Arr = .Range("A1:A" & .Range("A" & .Rows.Count).End(xlUp).Row).Value
For i = 1 To UBound(Arr, 1)
If Dic.exists(Arr(i, 1)) = False Then
Dic.Add (Arr(i, 1)), ""
End If
Next
End With
With Workbooks("ExtractFile").Worksheets("Sheet1")
Arr = .Range("A1:A" & .Range("A" & .Rows.Count).End(xlUp).Row).Value
ReDim outArr(1 To UBound(Arr), 1 To 1)
For i = 1 To UBound(Arr)
If Dic.exists(Arr(i, 1)) = False Then
k = k + 1
outArr(k, 1) = Arr(i, 1)
End If
Next
End With
iRow = Sheets("Sheet1").Range("A" & Rows.Count).End(3).Row + 1
If k <> 0 Then
Sheets("Sheet1").Range("A" & iRow).Resize(k).Value = outArr
k = 0
End If
End Sub
Tried adding Entirerow statement to several places, but to no avail.
Please, try the next adapted code. I commented where I input new variables/code lines:
Sub AddMissingItems()
Dim Dic As Object, Arr() As Variant, outArr() As Variant
Dim i As Long, k As Long, iRow As Long, c As Long
Dim r As Long, j As Long
Set Dic = CreateObject("Scripting.dictionary")
With Sheets("Sheet1")
Arr = .Range("A1:A" & .Range("A" & .rows.count).End(xlUp).row).Value
For i = 1 To UBound(Arr, 1)
If Dic.Exists(Arr(i, 1)) = False Then
Dic.Add (Arr(i, 1)), ""
End If
Next
End With
With Workbooks("ExtractFile.xlsx").Worksheets("Sheet1")
c = .cells(1, Columns.count).End(xlToLeft).column
r = .Range("A" & .rows.count).End(xlUp).row 'calculate the last row in A:A, too
Arr = .Range("A1", .cells(r, c)).Value 'place in the array all existing columns
ReDim outArr(1 To UBound(Arr), 1 To c) 'extend the redimmed array to all columns
For i = 1 To UBound(Arr)
If Dic.Exists(Arr(i, 1)) = False Then
k = k + 1
For j = 1 To c 'iterate between all array columns:
outArr(k, j) = Arr(i, j) 'place the value from each column
Next j
End If
Next
End With
iRow = Sheets("Sheet1").Range("A" & rows.count).End(3).row + 1
If k <> 0 Then
Sheets("Sheet1").Range("A" & iRow).Resize(k, UBound(Arr, 2)).Value = outArr 'resize by columns, too
k = 0
End If
End Sub
Please, send some feedback after testing it.
I've been using stackoverflow as a great reference tool for VBA.
I've got 2, 2-column sets of data as shown below.
My goal is to have a user input data into those 2 columns, create a single 2-column array with that info, and remove blank rows from that array, and then create a drop-down containing the info from the first column of the combined array. The second column will be used for voltage references. (the header not being part of the array.)
What i've done is create 2 arrays at first, and combine them. I'm not sure if this is the best method, I need something that will work fast and I'm not sure how to properly remove the rows.
The code is below:
Sub test1()
Dim CombinedArray As Variant
Dim SWGRArray As Variant
Dim MCCArray As Variant
SWGRArray = Sheets("Worksheet").Range(Cells(3, 8), Cells(19, 9)).value
MCCArray = Sheets("Worksheet").Range(Cells(3, 10), Cells(19, 11)).value
CombinedArray = MergeArrays(SWGRArray, MCCArray)
End Sub
Public Function MergeArrays(ParamArray Arrays() As Variant) As Variant
' merges multiple arrays into a single array.
' ParamArray is an array listing other arrays
' Thanks to 'Tom' via https://stackoverflow.com/questions/46051448/excel-vba-joining-two-arrays
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
Stack Arrays
I was about to post this on the 16th, when right in front of my nose the post got deleted. So I'm sorry there are no comments, it was a long time ago.
Option Explicit
Function getStackedArrays(ByVal FirstIndex As Long, _
ParamArray Arrays() As Variant) _
As Variant
' Define Lower-Upper Array.
Dim UB As Long: UB = UBound(Arrays)
Dim LU As Variant: ReDim LU(3)
Dim lub As Variant
Dim i As Long
For i = 0 To 3: ReDim lub(0 To UB): LU(i) = lub: Next i
' Populate Lower-Upper Array and calculate dimensions of Result Array.
Dim uCount As Long, uCurr As Long
Dim lMax As Long, lCurr As Long
For i = 0 To UB
If IsArray(Arrays(i)) Then
GoSub calcIsArray
Else
GoSub calcNotArray
End If
GoSub countnMax
Next i
If lMax = 0 Or uCount = 0 Then Exit Function
' Define Result Array.
Dim UB1 As Long: UB1 = FirstIndex + uCount - 1
Dim UB2 As Long: UB2 = FirstIndex + lMax - 1
Dim Result As Variant: ReDim Result(FirstIndex To UB1, FirstIndex To UB2)
' Populate Result Array.
Dim k As Long, l As Long, m As Long, n As Long
m = FirstIndex
For i = 0 To UB
If IsArray(Arrays(i)) Then
GoSub writeResult
End If
Next i
' Write Result Array to Function Result.
getStackedArrays = Result
Exit Function
' Subroutines
calcIsArray:
If LBound(Arrays(i)) <= UBound(Arrays(i)) Then
LU(0)(i) = LBound(Arrays(i)): LU(1)(i) = UBound(Arrays(i))
On Error Resume Next
LU(3)(i) = LBound(Arrays(i), 2): LU(3)(i) = UBound(Arrays(i), 2)
On Error GoTo 0
End If
Return
calcNotArray:
If Not IsEmpty(Arrays(i)) Then
ReDim lub(0): lub(0) = Arrays(i): Arrays(i) = lub
LU(0)(i) = 0: LU(1)(i) = 0
End If
Return
countnMax:
uCurr = LU(1)(i) - LU(0)(i) + 1: uCount = uCount + uCurr
On Error Resume Next
lCurr = LU(3)(i) - LU(2)(i) + 1
If lCurr > lMax Then lMax = lCurr
On Error GoTo 0
Return
writeResult:
If Not IsEmpty(LU(0)(i)) And Not IsEmpty(LU(2)(i)) Then ' 2D
For k = LU(0)(i) To LU(1)(i)
n = FirstIndex
For l = LU(2)(i) To LU(3)(i)
Result(m, n) = Arrays(i)(k, l)
n = n + 1
Next l
m = m + 1
Next k
Else ' 1D
For k = LU(0)(i) To LU(1)(i)
Result(m, FirstIndex) = Arrays(i)(k)
m = m + 1
Next k
End If
Return
End Function
Is there a way to have this script form the entire array based off the rows I want it to extract based on the IF Statement?
I know this finds a name on the Mgrs worksheet, and finds those rows in the Data worksheet, but then it directly prints it after forming the array. Can I have this code store all of the data, and then wait to print the data on a template that I format myself?
Option Explicit
Sub CIB_Cuts()
Dim j As Long, k As Long, x As Long
Dim varArray() As Variant
Dim varArray2() As Variant
ReDim varArray(1 To 19, 1 To 1)
Dim strManager As String, strEC As String, strLogin As String
Dim BASEPATH As String, strNewPath As String, strFileName As String
Dim Wb As Workbook
Dim mgrRow As Long
Dim colManager As Long
colManager = 3
Dim colLogin As Long
colLogin = 4
Dim colEC As Long
colEC = 5
BASEPATH = "M:\Final Files\"
Call speedupcode(True)
For mgrRow = 2 To ThisWorkbook.Worksheets("Mgrs").UsedRange.Rows.Count
If ThisWorkbook.Worksheets("Mgrs").Cells(mgrRow, 3) <> "" Then
strManager = ThisWorkbook.Worksheets("Mgrs").Cells(mgrRow, 3)
With ThisWorkbook.Worksheets("Data")
ReDim varArray(1 To UBound(varArray, 1), 1 To 1)
x = 1
For k = 1 To UBound(varArray, 1)
varArray(k, x) = .Cells(1, k)
Next
For j = 2 To .UsedRange.Rows.Count + 1
If strManager = .Cells(j, colManager) Then
x = x + 1
ReDim Preserve varArray(1 To UBound(varArray, 1), 1 To x)
For k = 1 To UBound(varArray, 1)
If k = 1 Then
varArray(1, x) = CStr(Format(.Cells(j, k), "000000000"))
Else
varArray(k, x) = .Cells(j, k)
End If
strEC = .Cells(j, colEC)
strManager = .Cells(j, colManager)
strLogin = .Cells(j, colLogin)
Next
End If
Next
End With
strFileName = strLogin & " - " & strManager & " - " & "Shift Differential Validation" & ".xlsx"
ReDim varArray2(1 To UBound(varArray, 2), 1 To UBound(varArray, 1))
Set Wb = Workbooks.Add(XlWBATemplate.xlWBATWorksheet)
With Wb
With .Worksheets("Sheet1")
.Columns(1).NumberFormat = "#"
.Columns(15).NumberFormat = "0%"
For j = 1 To UBound(varArray, 2)
For k = 1 To UBound(varArray, 1)
varArray2(j, k) = varArray(k, j)
Next
Next
.Range(.Cells(1, 1), .Cells(UBound(varArray, 2), UBound(varArray, 1))) = varArray2
Call DataValidation
Call Header
.Range("C2").Select
ActiveWindow.FreezePanes = True
.Cells.EntireColumn.AutoFit
.Rows("1:1").Font.Bold = True
Call protect
End With
.SaveAs strNewPath & strFileName, Password:="password", FileFormat:=51
.Saved = True
.Close
End With
Set Wb = Nothing
End If
Next
Call speedupcode(False)
End Sub
You could store the array each time in an overarching array or a collection and loop that at the end...
Public Sub test()
Dim varArray2() As Variant, results As Collection
'other code..
Set results = New Collection
results.Add varArray2
End Sub
You could also use Select Case , or something distinctive during the loop, to determine a key and populate a dictionary with the arrays as values which might make retrieval of specific items easier.
I am trying to make a function that takes in a 1D array, filters out by empty cells, and then condenses the array and returns it.
Example: [1][2][3][""][4] returns [1][2][3][4]
I keep getting #Value! when I try to call this new array via index().
Function BlankRemover(ArrayToCondense As Variant) As Variant
Dim ArrayWithoutBlanks() As Variant
Dim CellsInArray As Long
Dim ArrayWithoutBlanksIndex As Long
ArrayWithoutBlanksIndex = 1
For CellsInArray = LBound(ArrayToCondense) To UBound(ArrayToCondense)
If ArrayToCondense(CellsInArray) <> "" Then
ArrayWithoutBlanks(ArrayWithoutBlanksIndex) = ArrayToCondense(CellsInArray).Value
ArrayWithoutBlanksIndex = ArrayWithoutBlanksIndex + 1
End If
Next CellsInArray
ReDim Preserve ArrayWithoutBlanks(LBound(ArrayToCondense) To ArrayWithoutBlanksIndex)
ArrayWithoutBlanks = Application.Transpose(ArrayWithoutBlanks)
BlankRemover = ArrayWithoutBlanks
End Function
Try this:
Function BlankRemover(ArrayToCondense As Variant) As Variant()
Dim ArrayWithoutBlanks() As Variant
Dim CellsInArray As Variant
ReDim ArrayWithoutBlanks(1 To 1) As Variant
For Each CellsInArray In ArrayToCondense
If CellsInArray <> "" Then
ArrayWithoutBlanks(UBound(ArrayWithoutBlanks)) = CellsInArray
ReDim Preserve ArrayWithoutBlanks(1 To UBound(ArrayWithoutBlanks) + 1)
End If
Next CellsInArray
ArrayWithoutBlanks = Application.Transpose(ArrayWithoutBlanks)
BlankRemover = Application.Transpose(ArrayWithoutBlanks)
End Function
Try below:
Notes:
You should define BlankRemover as an array: Variant()
.Value not needed at end of ArrayToCondense(CellsInArray)
The code:
Function BlankRemover(ArrayToCondense As Variant) As Variant()
Dim ArrayWithoutBlanks() As Variant
Dim CellsInArray As Long
Dim ArrayWithoutBlanksIndex As Long
ArrayWithoutBlanksIndex = 0
For CellsInArray = LBound(ArrayToCondense) To UBound(ArrayToCondense)
If ArrayToCondense(CellsInArray) <> "" Then
ReDim Preserve ArrayWithoutBlanks(ArrayWithoutBlanksIndex)
ArrayWithoutBlanks(ArrayWithoutBlanksIndex) = ArrayToCondense(CellsInArray)
ArrayWithoutBlanksIndex = ArrayWithoutBlanksIndex + 1
End If
Next CellsInArray
'ArrayWithoutBlanks = Application.Transpose(ArrayWithoutBlanks)
BlankRemover = ArrayWithoutBlanks
End Function 'BlankRemover
You declared the function
Function BlankRemover(ArrayToCondense As Variant) As Variant
so that ArrayToCondense is not an array, to make it an array you switch ArrayToCondense with ArrayToCondense() so the final code will be:
Function BlankRemover(ArrayToCondense As Variant) As Variant()
There are a couple of issues with your code itself. Make the new array initially equal to the size of the original array; then do one "ReDim Preserve" at the end. Also, don't use a value like "1", arrays can have multiple starting indices. Here's what the code would ideally look like for doing this with arrays (though as I'll note below, I don't think that's actually what you want):
Function blankRemover(arr As Variant) As Variant
If Not IsArray(arr) Then
Exit Function
End If
ReDim newArr(LBound(arr) To UBound(arr))
Dim i As Long
Dim j As Long
j = LBound(arr)
For i = LBound(arr) To UBound(arr)
If Not arr(i) = "" Then
newArr(j) = arr(i)
j = j + 1
End If
Next
ReDim Preserve newArr(LBound(arr) To j - 1)
blankRemover = newArr
End Function
But based on your comments, it sounds like you're not actually passing this function an array: you're passing it a range. So you'd actually want to use something like this:
Function blankRemoverRng(rng As Range) As Variant
If Not ((rng.Rows.Count = 1) Xor (rng.Columns.Count = 1)) Then
Exit Function
End If
Dim arr As Variant
arr = narrow2dArray(rng.Value)
ReDim newArr(LBound(arr) To UBound(arr))
Dim i As Long
Dim j As Long
j = LBound(arr)
For i = LBound(arr) To UBound(arr)
If Not arr(i) = "" Then
newArr(j) = arr(i)
j = j + 1
End If
Next
ReDim Preserve newArr(LBound(arr) To j - 1)
blankRemoverRng = newArr
End Function
Function narrow2dArray(ByRef arr As Variant, Optional ByVal newBase As Long = 1) As Variant
'Takes a 2d array which has one dimension of size 1 and converts it to a 1d array with base newBase
'IE it takes an array with these dimensions:
'Dim arr(1 To 10, 1 To 1)
'And turns it into an array with these dimensions:
'Dim arr(1 To 10)
On Error GoTo exitStatement
Dim bigDim As Integer
If Not IsArray(arr) Then
Dim smallArr(1 To 1) As Variant
smallArr(1) = arr
narrow2dArray = smallArr
Exit Function
ElseIf LBound(arr, 1) = UBound(arr, 1) Then
bigDim = 2
ElseIf LBound(arr, 2) = UBound(arr, 2) Then
bigDim = 1
Else
GoTo exitStatement
End If
ReDim tempArr(newBase To UBound(arr, bigDim) - LBound(arr, bigDim) + newBase) As Variant
Dim i As Long
Dim j As Long
j = LBound(arr, bigDim)
If bigDim = 2 Then
For i = LBound(tempArr) To UBound(tempArr)
If IsObject(arr(1, j)) Then
Set tempArr(i) = arr(1, j)
Else
tempArr(i) = arr(1, j)
End If
j = j + 1
Next
Else
For i = LBound(tempArr) To UBound(tempArr)
If IsObject(arr(j, 1)) Then
Set tempArr(i) = arr(j, 1)
Else
tempArr(i) = arr(j, 1)
End If
j = j + 1
Next
End If
On Error GoTo 0
narrow2dArray = tempArr
Exit Function
exitStatement:
MsgBox "Error: One of array's dimensions must have size = 1"
On Error GoTo 0
Stop
End Function
For those who are coming later looking for a simple answer:
Filter(arrayElement, "", False)
I need to do the following:
lift the range C2:AU264 into an 2D array
create another 1D array, (1 To 11880)
fill second array with values from the first one ("transpose")
write array 2 back to the sheet
Here is the code I am using:
Private Ws As Worksheet
Private budgets() As Variant
Private arrayToWrite() As Variant
Private lastrow As Long
Private lastcol As Long
Private Sub procedure()
Application.ScreenUpdating = False
Set Ws = Sheet19
Ws.Activate
lastrow = Ws.Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).row
lastcol = Ws.Cells.Find("*", searchorder:=xlByColumns, searchdirection:=xlPrevious).Column
ReDim budgets(1 To lastrow - 1, 1 To lastcol - 2)
budgets= Ws.Range("C2:AU265")
ReDim arrayToWrite(1 To (lastCol - 2) * (lastRow - 1))
k = 0
For j = 1 To UBound(budgets, 2)
For i = 1 To UBound(budgets, 1)
arrayToWrite(i + k) = budgets(i, j)
Next i
k = k + lastrow - 1
Next j
Set Ws = Sheet6
Ws.Activate
Ws.Range("E2").Resize(UBound(arrayToWrite)).Value = arrayToWrite
'For i = 1 To UBound(arrayToWrite)
'Ws.Range(Cells(i + 1, 5).Address).Value = arrayToWrite(i)
'Next i
Application.ScreenUpdating = True
End Sub
This just writes the first value from the range C2:AU264 (the first element of the first array) through the whole range E2:E11881. If however, I un-comment the For loop just before the end of my script and do it that way, it does work, but is slow. How can I write the array correctly using the first statement?
If you want to write an array to a range, the array must have two dimensions. Even if you only wish to write a single column.
Change
ReDim arrayToWrite(1 To (lastCol - 2) * (lastRow - 1))
to
ReDim arrayToWrite(1 To (lastCol - 2) * (lastRow - 1), 1 To 1)
and
arrayToWrite(i + k) = budgets(i, j)
to
arrayToWrite(i + k, 1) = budgets(i, j)
simply use transpose... change
Ws.Range("E2").Resize(UBound(arrayToWrite)).Value = arrayToWrite
to
Ws.Range("E2").Resize(UBound(arrayToWrite)).Value = Application.Transpose(arrayToWrite)
Hint: there is no need for ReDim budgets(1 To lastrow - 1, 1 To lastcol - 2).
If budgets is a variant then budgets = Ws.Range("C2:AU265") will automatically set the ranges (upper left cell (in this case C2) will be (1, 1)).
EDIT
Assuming you only want to write down all columns (one after another) below each other, you can shorten the macro a bit like that:
Private Sub procedure()
Dim inArr As Variant, outArr() As Variant
Dim i As Long, j As Long, k As Long
With Sheet19
.Activate
inArr = .Range(, .Cells(2, 3), .Cells(.Cells.Find("*", , , , 1, 2).Row, .Cells.Find("*", , , , 2, 2).Column)).Value
End With
ReDim outArr(1 To UBound(inArr) * UBound(inArr, 2))
k = 1
For j = 1 To UBound(inArr, 2)
For i = 1 To UBound(inArr)
k = k + 1
arrayToWrite(k) = budgets(i, j)
Next i
Next j
Sheet6.Range("E2:E" & UBound(arrayToWrite)).Value = Application.Transpose(arrayToWrite)
End Sub
And if you want each row transposed and below each other than simply switch the two For...-lines. (Still the code does basically the same like before)