I'm trying to go through a list with two columns and replace some of the text in the second column. I want to search for values using wildcards in combination with a value inside a 2D Array.
I've a file with all Pokemon cards separated in different worksheets by the set they're in. There are two columns that are called "Name" and "German Name".
I created another worksheet that contains all cards and their corresponding name and German name. Out of that worksheet, I create a 2 dimensional Array. This works.
Then I've loops going on and inside that I've got this line of code.
Worksheets(table).Cells(otherI, 2).Value = Replace(Worksheets(table).Cells(otherI, 2).Value, " * " & allArray(i, 0) & " * ", " * " & allArray(i, 1) & " * ")
Somewhere there is the problem.
E.g. I've the entry "Bulbasaur Lv.5" in both columns and now I want to replace "Bulbasaur" in the second column with its German equivalent "Bisasam" but the "Lv.5" mustn't be touched.
The whole script.
Option Explicit
Sub firstMakro()
'Variables
Dim allSize As Integer
Dim allArray()
Dim allI As Integer
allI = 1
Dim otherSize As Integer
Dim otherI As Integer
otherI = 1
Dim i As Integer
Dim table As Integer
table = 2
'Create Array
allSize = WorksheetFunction.CountA(Worksheets("All_Pokemons").Columns(1))
ReDim allArray(allI To allSize, 1)
Do
allArray(allI, 0) = Worksheets("All_Pokemons").Cells(allI, 1).Value
allArray(allI, 1) = Worksheets("All_Pokemons").Cells(allI, 2).Value
allI = allI + 1
Loop Until allI > allSize
MsgBox ("Array created")
'Replace Entries
For i = LBound(allArray, 1) To UBound(allArray, 1)
MsgBox (allArray(i, 0))
otherSize = WorksheetFunction.CountA(Worksheets(table).Columns(1))
Do
Worksheets(table).Cells(otherI, 2).Value = Replace(Worksheets(table).Cells(otherI, 2).Value, " * " & allArray(i, 0) & " * ", " * " & allArray(i, 1) & " * ")
otherI = otherI + 1
Loop Until otherI > otherSize
otherI = 1
Next i
End Sub
Replace doesn't use, or in this case even need, wildcards. Use
Replace(Worksheets(table).Cells(otherI, 2).Value, allArray(i, 0), allArray(i, 1))
Range Replace
Range.Replace (Microsoft Docs)
Tested only on a small dataset (feedback on efficiency (speed) is appreciated).
It will replace each occurrence of an English name with the associated German name in the whole destination range.
Adjust the values in the constants section.
Option Explicit
Sub Germanize()
Const sName As String = "All_Pokemons"
Const sfRow As Long = 2 ' ??? First Row
Const seCol As String = "A" ' ENG
Const sgCol As String = "B" ' GER
Const dName As String = "Sheet2" ' ??? Worksheet Tab Name
Const dfRow As Long = 2 ' ??? First Row
Const deCol As String = "A" ' ENG
Const dgCol As String = "B" ' GER
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Source (All)
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim serg As Range: Set serg = RefColumn(sws.Cells(sfRow, seCol)) ' ENG
If serg Is Nothing Then Exit Sub ' no data
Dim seData As Variant: seData = GetRange(serg) ' ENG
Dim sgrg As Range: Set sgrg = serg.EntireRow.Columns(sgCol) ' GER
Dim sgData As Variant: sgData = GetRange(sgrg) ' GER
' Destination
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim derg As Range: Set derg = RefColumn(dws.Cells(dfRow, deCol)) ' ENG
If derg Is Nothing Then Exit Sub ' no data
Dim dgrg As Range: Set dgrg = derg.EntireRow.Columns(dgCol) ' GER
Application.ScreenUpdating = False
dgrg.Value = derg.Value ' write ENG column to GER column
Dim seValue As Variant
Dim r As Long
' Replace in GER column.
For r = 1 To UBound(seData, 1)
seValue = seData(r, 1)
If Not IsError(seValue) Then
If Len(seValue) > 0 Then
dgrg.Replace seValue, CStr(sgData(r, 1)), xlPart, , False
End If
End If
Next r
Application.ScreenUpdating = True
MsgBox "German pokemon names updated.", vbInformation
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Creates a reference to the one-column range from the first cell
' of a range ('FirstCell') to the bottom-most non-empty cell
' of the first cell's worksheet column.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefColumn( _
ByVal FirstCell As Range) _
As Range
Const ProcName As String = "RefColumn"
On Error GoTo ClearError
With FirstCell.Cells(1)
Dim lCell As Range
Set lCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If lCell Is Nothing Then Exit Function
Set RefColumn = .Resize(lCell.Row - .Row + 1)
End With
ProcExit:
Exit Function
ClearError:
Debug.Print "'" & ProcName & "' Run-time error '" _
& Err.Number & "':" & vbLf & " " & Err.Description
Resume ProcExit
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the values of a range ('rg') in a 2D one-based array.
' Remarks: If ˙rg` refers to a multi-range, only its first area
' is considered.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetRange( _
ByVal rg As Range) _
As Variant
Const ProcName As String = "GetRange"
On Error GoTo ClearError
If rg.Rows.Count + rg.Columns.Count = 2 Then ' one cell
Dim Data As Variant: ReDim Data(1 To 1, 1 To 1): Data(1, 1) = rg.Value
GetRange = Data
Else ' multiple cells
GetRange = rg.Value
End If
ProcExit:
Exit Function
ClearError:
Debug.Print "'" & ProcName & "' Run-time error '" _
& Err.Number & "':" & vbLf & " " & Err.Description
Resume ProcExit
End Function
Related
I'm writing on a timesheet printing code.
In a table, I was provided with all staff names (column A) and (if timesheet needs to be printed) their initials (column E). If staff doesn't need timesheet, they provided "XXX" as initials.
I now want to print all staff which have initials other than "XXX".
I can print individual timesheet or even the array of all worksheets after "print", but I can't read the array into VBA to print only the ones I need.
Is it because worksheet "XXX" doesn't exist?
What is missing from my code?
Private Sub CommandButton2_Click()
'check if date is a Monday
If Weekday(Range("B2").Value) <> 2 Then
MsgBox "Please alter the date to a Monday.", 0, "Date selected is not a Monday."
Exit Sub
End If
Dim PrintRng As Range
Dim SavePDFT As String
Dim shtArray() As String
Dim i As Integer
Dim x As Long
Dim ws As Worksheet
Set PrintRng = Range("A1:Q34")
SavePDFT = ThisWorkbook.Path & "Timesheets_" & Range("E40").Value & "_" & Format(Now(), "yymmddhhmmss")
x = Worksheets("print").Index
' Enter the sheet names into an array
i = 0
For Each ws In ActiveWorkbook.Worksheets
With ws
If .Index > x Then
ReDim Preserve shtArray(i)
Debug.Print ws.Name
shtArray(i) = ws.Name
'MsgBox "" & ActiveWorkbook.Worksheets(shtArray(i)).Name
i = i + 1
End If
End With
Next ws
Dim TSarray() As Variant
elements = 10 ' needs to be altered
ReDim TSarray(1 To elements) As String
counter = 1
For counter = 1 To elements
f = 4 + counter
If Cells(f, 5).Value <> "XXX" Then
TSarray(counter) = Cells(f, 5).Value
End If
Next counter
'counter = 1 'this loop shows what was filled in
' While counter <= UBound(TSarray)
' MsgBox TSarray(counter)
' counter = counter + 1
' Wend
Sheets(TSarray).Select 'working with Sheets(shtArray).Select, but I don't want to print all worksheets, just selected ones
ActiveSheet.Outline.ShowLevels RowLevels:=1, ColumnLevels:=1
With ActiveSheet.PageSetup
.PrintArea = PrintRng
.Zoom = False
.FitToPagesWide = 1
.FitToPagesTall = 1
End With
ActiveSheet.ExportAsFixedFormat Type:=xlTypePDF, _
Filename:=SavePDFT, Quality:=xlQualityStandard, _
IncludeDocProperties:=True, IgnorePrintAreas:=False, _
OpenAfterPublish:=True
ActiveSheet.Outline.ShowLevels RowLevels:=8, ColumnLevels:=8
End Sub
I have a list of around 200 names (on a sheet) that I need to try and filter (and delete) out of a data sheet. I'm struggling to get down how to set the list of names as an array so that I can filter that array under Range("E:E").AutoFilter Field:=1, Criteria1:=**Array Here**, _ and then later entirerow.delete.
This is my most recent attempt based off of other sources online, but it seems that most of them are lists that only contain 4-5 values, and I'm struggling to find anything that would be useful in putting all of the values in an array and filtering them based off of that, any help/workarounds are appreciated thank you!
Call myArrayRange
Dim rng As Range
Dim pos As Integer
Dim arr As String
Set arr = Worksheets("control").Range("K2:K10000")
Set sht = ws
With sht
Range("E:E").AutoFilter Field:=1, Criteria1:=Array(""), _
Operator:=xlFilterValues
LstRw = .Cells(.Rows.Count, "A").End(xlUp).Row
Set rng = .Range("A2:A" & LstRw).SpecialCells(xlCellTypeVisible)
rng.EntireRow.Delete
.AutoFilterMode = False
End With
End Sub
Sub myArrayRange()
lr = Worksheets("Control").Cells(Rows.Count, 11).End(xlUp).Row
Dim iAmount() As Variant
Dim iNum As Integer
iAmount = Range("K2:K" & lr)
For iNum = 1 To UBound(iAmount)
Debug.Print iAmount(iNum, 1)
Next iNum
End Sub```
EDIT: updated to match your actual use case.
Here's a basic example of how you can do it:
Sub Tester()
Dim arr, rngNames as range, ws As Worksheet
Set ws = ActiveSheet 'for example: the sheet with the data to filter
With ws.Parent.Worksheets("Control")
Set rngNames = .Range("K2:K" & .Cells(.Rows.Count, "K").End(xlUp))
End With
arr = RangeToArray(rngNames) 'get an array from the list of names
ws.Range("E:E").AutoFilter Field:=1, Criteria1:=arr, _
Operator:=xlFilterValues
ws.Autofilter.Range.SpecialCells(xlCellTypeVisible).EntireRow.Delete
ws.AutoFilterMode = False
End Sub
'convert a range to a zero-based 1D array
Function RangeToArray(rng As Range)
Dim r As Long, c As Long, arr, data, i
data = rng.Value 'get the source data
ReDim arr(0 To rng.Cells.Count - 1) 'size the output array
For r = 1 To UBound(data, 1) 'loop over the data from the range
For c = 1 To UBound(data, 2)
arr(i) = data(r, c)
i = i + 1
Next c
Next r
RangeToArray = arr
End Function
Delete Criteria Rows
Criteria
Table Before
Table After
The Code
Carefully adjust the values in the constants section.
Option Explicit
Sub DeleteCriteriaRows()
Const ProcName As String = "DeleteCriteriaRows"
Dim RowsDeleted As Boolean
Dim AnErrorOccurred As Boolean
On Error GoTo ClearError ' enable error-handling routine
' Criteria
Const cName As String = "Control"
Const cFirstCellAddress As String = "K2"
' Table
Const tName As String = "Data"
Const tFirstCellAddress As String = "A2"
Const tColumnIndex As Long = 5
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Criteria
' Reference the criteria worksheet.
Dim cws As Worksheet: Set cws = wb.Worksheets(cName)
' Reference the criteria (one-column) range.
Dim crg As Range: Set crg = RefColumn(cws.Range(cFirstCellAddress))
' Write the values from the criteria range
' to a 2D one-based one-column array.
Dim cData As Variant: cData = GetRange(crg)
' Write the unique valeus from the array to a dictionary
' (exclude error values and blanks).
Dim cDict As Object: Set cDict = DictColumn(cData)
Erase cData ' data is in the dictionary
' Write the values from the dictionary, converted to strings,
' to a 1D zero-based string array.
Dim csArr() As String: csArr = sArrDict(cDict)
Set cDict = Nothing ' data is in the string array
' Table
' Reference the table worksheet.
Dim tws As Worksheet: Set tws = wb.Worksheets(tName)
Application.ScreenUpdating = False
' Clear all table worksheet's filters.
If tws.FilterMode Then tws.ShowAllData
' Reference the table range (has headers).
Dim trg As Range: Set trg = RefCurrentRegion(tws.Range(tFirstCellAddress))
' Reference the table data range (no headers).
Dim tdrg As Range: Set tdrg = trg.Resize(trg.Rows.Count - 1).Offset(1)
' Apply the autofilter on the TABLE RANGE.
trg.AutoFilter tColumnIndex, csArr, xlFilterValues
' Attempt to reference the filtered rows (the visible rows
' of the TABLE DATA RANGE).
Dim tdfrg As Range
On Error Resume Next ' defer error trapping
Set tdfrg = tdrg.SpecialCells(xlCellTypeVisible)
On Error GoTo ClearError ' re-enable error-handling routine
' Turn off the autofilter.
tws.AutoFilterMode = False
' Delete the filtered rows.
If Not tdfrg Is Nothing Then ' there are filtered rows...
tdfrg.Delete xlShiftUp ' ... delete them
RowsDeleted = True
'Else ' there are no filtered rows; do nothing
End If
ProcExit:
On Error Resume Next
If Not Application.ScreenUpdating Then Application.ScreenUpdating = True
' Inform.
If AnErrorOccurred Then
MsgBox "An error occurred.", vbCritical, ProcName
Else
If RowsDeleted Then
MsgBox "Filtered rows deleted.", vbInformation, ProcName
Else
MsgBox "No filtered rows.", vbExclamation, ProcName
End If
End If
On Error GoTo 0
Exit Sub
ClearError: ' Error-Handling Routine
Debug.Print "'" & ProcName & "' Run-time error '" _
& Err.Number & "':" & vbLf & " " & Err.Description
AnErrorOccurred = True
Resume ProcExit
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Creates a reference to the one-column range ('crg') whose first
' cell is defined by the first cell of the range ('FirstCell')
' and whose last cell is the bottom-most non-empty cell
' of the first cell's worksheet column.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefColumn( _
ByVal FirstCell As Range) _
As Range
Const ProcName As String = "RefColumn"
On Error GoTo ClearError
With FirstCell.Cells(1)
Dim lCell As Range
Set lCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If lCell Is Nothing Then Exit Function
Set RefColumn = .Resize(lCell.Row - .Row + 1)
End With
ProcExit:
Exit Function
ClearError:
Debug.Print "'" & ProcName & "' Run-time error '" _
& Err.Number & "':" & vbLf & " " & Err.Description
Resume ProcExit
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the values of a range ('rg') in a 2D one-based array.
' Remarks: If ˙rg` refers to a multi-range, only its first area
' is considered.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetRange( _
ByVal rg As Range) _
As Variant
Const ProcName As String = "GetRange"
On Error GoTo ClearError
If rg.Rows.Count + rg.Columns.Count = 2 Then ' one cell
Dim Data As Variant: ReDim Data(1 To 1, 1 To 1): Data(1, 1) = rg.Value
GetRange = Data
Else ' multiple cells
GetRange = rg.Value
End If
ProcExit:
Exit Function
ClearError:
Debug.Print "'" & ProcName & "' Run-time error '" _
& Err.Number & "':" & vbLf & " " & Err.Description
Resume ProcExit
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the unique values from a column ('ColumnIndex')
' of a 2D array ('Data') in the keys of a dictionary.
' Remarks: Error values and blanks are excluded.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function DictColumn( _
ByVal Data As Variant, _
Optional ByVal ColumnIndex As Variant) _
As Object
Const ProcName As String = "DictColumn"
On Error GoTo ClearError
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare ' case-insensitive
Dim c As Long
If IsMissing(ColumnIndex) Then
c = LBound(Data, 2) ' use first column index
Else
c = CLng(ColumnIndex)
End If
Dim Key As Variant
Dim r As Long
For r = LBound(Data, 1) To UBound(Data, 1)
Key = Data(r, c)
If Not IsError(Key) Then ' exclude error values
If Len(CStr(Key)) > 0 Then ' exclude blanks
dict(Key) = Empty
End If
End If
Next r
If dict.Count = 0 Then Exit Function ' only error values and blanks
Set DictColumn = dict
ProcExit:
Exit Function
ClearError:
Debug.Print "'" & ProcName & "' Run-time error '" _
& Err.Number & "':" & vbLf & " " & Err.Description
Resume ProcExit
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Converts the values of the keys of a dictionary to strings
' and returns the strings in a 1D zero-based string array.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function sArrDict( _
ByVal dict As Object) _
As String()
Const ProcName As String = "sArrDict"
On Error GoTo ClearError
If dict.Count > 0 Then
Dim sArr() As String: ReDim sArr(0 To dict.Count - 1)
Dim Key As Variant
Dim n As Long
For Each Key In dict.Keys
sArr(n) = CStr(Key)
n = n + 1
Next Key
sArrDict = sArr
Exit Function
End If
ProcExit:
' Ensure a 1D zero-based string array is returned (no matter what).
sArrDict = Split("") ' (LB=0, UB=-1)
Exit Function
ClearError:
Debug.Print "'" & ProcName & "' Run-time error '" _
& Err.Number & "':" & vbLf & " " & Err.Description
Resume ProcExit
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns a reference to the range starting with the first cell
' of a range and ending with the last cell of the first cell's
' Current Region.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefCurrentRegion( _
ByVal FirstCell As Range) _
As Range
Const ProcName As String = "RefCurrentRegion"
On Error GoTo ClearError
If FirstCell Is Nothing Then Exit Function
With FirstCell.Cells(1).CurrentRegion
Set RefCurrentRegion = FirstCell.Resize(.Row + .Rows.Count _
- FirstCell.Row, .Column + .Columns.Count - FirstCell.Column)
End With
ProcExit:
Exit Function
ClearError:
Debug.Print "'" & ProcName & "' Run-time error '" _
& Err.Number & "':" & vbLf & " " & Err.Description
Resume ProcExit
End Function
This is a really dangerous way to delete things. You can't really recover the data so make sure that filter works.
Sub Button1_Click()
myArrayRange
End Sub
Sub myArrayRange()
Dim rng As Range
Dim pos As Integer
Dim sht As Worksheet
Set sht = ActiveSheet
With sht
'Your string array that holds names would go here VVVV (According to MS Docs)
Range("E:E").AutoFilter Field:=1, Criteria1:=Array(""), _
Operator:=xlFilterValues
LstRw = .Cells(.Rows.Count, "A").End(xlDown).Row
Set rng = .Range("A2:A" & LstRw).SpecialCells(xlCellTypeVisible)
rng.EntireRow.Delete
.AutoFilterMode = False
End With
End Sub
You'll have to find the sheet you need some way. I used the active sheet. Here's a screenshot of the data before and after.
We are not able to create a formula which will copy 200 rows of a column in a same order and paste it multiple times in the same column and in the same order.
Example: columns A1:A200 have names in a particular order and we want to repeat the same order in the same column for 3000 times.
What is the way to do it without manual dragging?
Multi-Stack a Range Vertically
Sub VMultiStackTEST()
Const SourceRangeAddress As String = "A1:A200"
Const DestinationFirstCellAddress As String = "A1"
Const StackCount As Long = 3000
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim srg As Range: Set srg = ws.Range(SourceRangeAddress)
Dim dfCell As Range: Set dfCell = ws.Range(DestinationFirstCellAddress)
VMultiStack srg, dfCell, StackCount
' or (instead) just e.g.:
'VMultiStack Range("A1:A200"), Range("A1"), 3000
End Sub
Sub VMultiStack( _
ByVal SourceRange As Range, _
ByVal DestinationFirstCell As Range, _
Optional ByVal StackCount As Long = 1)
Const ProcName As String = "VMultiStack"
On Error GoTo ClearError
Dim IsSuccess As Boolean
Dim sData As Variant
Dim srCount As Long
Dim cCount As Long
Dim sAddress As String
With SourceRange.Areas(1)
sAddress = .Address(0, 0)
srCount = .Rows.Count
cCount = .Columns.Count
If srCount + cCount = 2 Then
ReDim sData(1 To 1, 1 To 1): sData(1, 1) = .Value
Else
sData = .Value
End If
End With
Dim dData As Variant: ReDim dData(1 To srCount * StackCount, 1 To cCount)
Dim n As Long
Dim sr As Long
Dim dr As Long
Dim c As Long
For n = 1 To StackCount
For sr = 1 To srCount
dr = dr + 1
For c = 1 To cCount
dData(dr, c) = sData(sr, c)
Next c
Next sr
Next n
Dim dAddress As String
With DestinationFirstCell.Resize(, cCount)
With .Resize(dr)
.Value = dData
dAddress = .Address(0, 0)
End With
.Resize(.Worksheet.Rows.Count - .Row - dr + 1).Offset(dr).Clear
End With
IsSuccess = True
ProcExit:
If IsSuccess Then
MsgBox "Stacked '" & sAddress & "' " & StackCount & " times to '" _
& dAddress & "'.", _
vbInformation, ProcName
Else
If Len(sAddress) > 0 Then
MsgBox "Could not stack '" & sAddress & "' " & StackCount _
& " times. No action taken.", _
vbExclamation, ProcName
Else
MsgBox "The program failed.", vbCritical, ProcName
End If
End If
Exit Sub
ClearError:
Debug.Print "'" & ProcName & "' Run-time error '" _
& Err.Number & "':" & vbLf & " " & Err.Description
Resume ProcExit
End Sub
With Office 365, you can put this into a LET as follows:
=LET( a, A1:A200, mBy, 3000,
r, ROWS( a ),
s, r * mBy,
INDEX( a, MOD(SEQUENCE( s,,0 ),r) + 1 ) )
where a is the column of names and mBy is the multiple (3000).
If you want to simplify it:
= INDEX( A1:A200, MOD(SEQUENCE( ROWS(A1:A200) * 3000,,0 ),ROWS(A1:A200)) + 1 )
I have VBA code in Excel to convert a Range of multiple rows and columns to an Array so that I can convert it to string and store the information as a string:
Sub pruebaString()
Dim str As String
Dim ar As Variant
ar = Hoja2.Names("AREA1").RefersToRange
Debug.Print TypeName(ar(1)) 'GIVES ERROR
str = Join(ar(1)) 'GIVES ERROR
Debug.print ar(1,2) 'outputs the results correctly.
debug.print LBound(ar) & ", " & UBound(ar) 'outputs the expected bounds.
End Sub
Gives me an 'out of bounds' error. The Range is an area of 4x4 so the array should be a bidimensional array.
Trying to do a Join of ar(1) also gives 'out of bounds error'.
The code to convert it to an array, I took it from the internet. Apparently it is the only thing it takes to do the job, by assigning a range to a Variant non array variable.
It seems to have the structure of a bi-dimensional array and accessing it like ar(1,1), ar(1,2) and so on works but when trying to join each of the inner arrays doesn't.
I want to join the inner arrays and then join everything together with a different delimiter, so that I have a string of rows and columns like 1,2,1;4,2,1 and so on.
I'm using Excel 2002
Join Array Row or Column
An easy way to get a row or a column as a 1D array is by using Application.Index with Application.Transpose with its limitations (slow, the number of elements is limited).
Using the row or the column of the range with Application.Transpose may be more efficient.
The most efficient way would be to write a function that will loop through the rows or columns of the 2D array and return a 1D array.
To test this, copy the code to a standard module of a new workbook and put a table of values starting in cell A1 of Sheet1. In the VBE Immediate window (Ctrl+G) monitor the output.
Option Explicit
Sub JoinArrayRowOrColumn()
Dim rg As Range: Set rg = Sheet1.Range("A1").CurrentRegion
Dim Data As Variant: Data = rg.Value ' 2D one-based array
' Join first row (note the 'double transpose').
Dim rArr As Variant: rArr = Application.Transpose( _
Application.Transpose(Application.Index(Data, 1, 0)))
Debug.Print Join(rArr, ", ")
' Join first column.
Dim cArr As Variant
cArr = Application.Transpose(Application.Index(Data, 0, 1))
Debug.Print Join(cArr, ", ")
' Without using the 'indermediate' ('Data') array (probably more efficient):
rArr = Application.Transpose(Application.Transpose(rg.Rows(1).Value))
Debug.Print Join(rArr, ", ")
cArr = Application.Transpose(rg.Columns(1).Value)
Debug.Print Join(cArr, ", ")
End Sub
The Function for a Row
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the values of a row of a 2D array in a 1D array.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function ArrRow( _
ByVal Data As Variant, _
ByVal RowIndex As Long, _
Optional ByVal FirstIndex As Long = 0) _
As Variant
Const ProcName As String = "ArrRow"
On Error GoTo ClearError
If IsEmpty(Data) Then Exit Function
If RowIndex < LBound(Data, 1) Then Exit Function
If RowIndex > UBound(Data, 1) Then Exit Function
Dim LB2 As Long: LB2 = LBound(Data, 2)
Dim UB2 As Long: UB2 = UBound(Data, 2)
Dim cDiff As Long: cDiff = LB2 - FirstIndex
Dim rArr As Variant: ReDim rArr(FirstIndex To FirstIndex + UB2 - LB2)
Dim c As Long
For c = LB2 To UB2
rArr(c - cDiff) = Data(RowIndex, c)
Next c
ArrRow = rArr
' Debug.Print ProcName & ": [LB=" & LBound(ArrRow) _
' & ",UB=" & UBound(ArrRow) & "]"
' Debug.Print ProcName & ": [" & Join(ArrRow, ", ") & "]"
ProcExit:
Exit Function
ClearError:
Debug.Print "'" & ProcName & "': Unexpected Error!" & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
Resume ProcExit
End Function
Sub ArrRowTEST()
Dim rg As Range: Set rg = Sheet1.Range("A1").CurrentRegion
' Note that if rg contains more than one (contiguous) cell,
' rg.Value is actually a 2D one-based array already.
Dim rArr As Variant: rArr = ArrRow(rg.Value, 1)
If Not IsEmpty(rArr) Then
Debug.Print Join(rArr, ", ")
End If
End Sub
I have several excel sheets with tables with a similar format to below:
Where I'd like to copy all the rows where the number under the "Value" header exceeds 10 into a blank tab.
Sub Copy_Criteria()
With Range("A5:P1000")
.AutoFilter Field:=15, Criteria1:=">10"
End With
End Sub
After that I would like to select all the values filtered here and copy them into a blank sheet. Next I'd like to repeat the whole process, but copying the rows based on another header/criteria into a second blank tab.
Thanks!
You can do something like this:
With Range("A5:P1000")
.AutoFilter Field:=15, Criteria1:=">30"
On Error Resume Next 'in case no visible cells
.SpecialCells(xlCellTypeVisible).Copy Sheet2.Range("a1")
On Error Goto 0
.Parent.ShowAllData 'clear filter
End With
Copy By Criteria
Adjust the values in the constants section.
Option Explicit
Sub CopyByCriteria()
' Needs 'RefCurrentRegionBottomRight', 'GetFilteredRange' and 'GetRange'.
Const ProcTitle As String = "CopyByCriteria"
Const sFirst As String = "A5"
Dim swsNames As Variant: swsNames = Array("Sheet1", "Sheet2", "Sheet3")
Const dFirst As String = "A1"
' These three arrays need to have the same number of elements.
Dim dwsNames As Variant: dwsNames = Array("15gt10", "12gt15")
Dim dFields As Variant: dFields = Array(15, 12)
Dim dCriteria As Variant: dCriteria = Array(">10", ">15")
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim sws As Worksheet
Dim srg As Range
Dim dws As Worksheet
Dim drg As Range
Dim dCell As Range
Dim dData As Variant
Dim n As Long
Dim IncludeHeaders As Boolean
For n = LBound(dwsNames) To UBound(dwsNames)
On Error Resume Next
Application.DisplayAlerts = False
wb.Sheets(dwsNames(n)).Delete
Application.DisplayAlerts = True
On Error GoTo 0
Set dws = wb.Worksheets.Add(After:=wb.Sheets(wb.Sheets.Count))
dws.Name = dwsNames(n)
Set dCell = dws.Range(dFirst)
IncludeHeaders = True
For Each sws In wb.Worksheets(swsNames)
Set srg = RefCurrentRegionBottomRight(sws.Range(sFirst))
dData = GetFilteredRange( _
srg, dFields(n), dCriteria(n), IncludeHeaders, IncludeHeaders)
If Not IsEmpty(dData) Then
IncludeHeaders = False ' include only the first time
Set drg = dCell.Resize(UBound(dData, 1), UBound(dData, 2))
drg.Value = dData
Set dCell = dCell.Offset(UBound(dData, 1))
End If
Next sws
Next n
MsgBox "Worksheets created. Values copied.", vbInformation, ProcTitle
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the filtered values of a range ('rg')
' in a 2D one-based array.
' Remarks: If ˙rg` refers to a multi-range, only its first area
' is considered.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetFilteredRange( _
ByVal rg As Range, _
ByVal fField As Long, _
ByVal fCriteria As String, _
Optional ByVal IncludeHeaders As Boolean = True, _
Optional ByVal AllowHeadersOnly As Boolean = False) _
As Variant
' Needs the 'GetRange' function.
Const ProcName As String = "GetFilteredRange"
On Error GoTo ClearError
Dim ws As Worksheet: Set ws = rg.Worksheet
If ws.AutoFilterMode Then
ws.AutoFilterMode = False
End If
rg.AutoFilter fField, fCriteria
Dim frShift As Long: frShift = IIf(IncludeHeaders, 0, 1)
Dim frg As Range
On Error Resume Next
Set frg = rg.Resize(rg.Rows.Count - frShift) _
.Offset(frShift).SpecialCells(xlCellTypeVisible)
On Error GoTo ClearError
ws.AutoFilterMode = False
If Not frg Is Nothing Then
Dim frCount As Long
frCount = Intersect(frg, ws.Columns(frg.Column)).Cells.Count
Dim doContinue As Boolean: doContinue = True
If frShift = 0 Then
If frCount = 1 Then
If Not AllowHeadersOnly Then
doContinue = False
End If
End If
End If
If doContinue Then
Dim fcCount As Long: fcCount = frg.Columns.Count
Dim dData As Variant: ReDim dData(1 To frCount, 1 To fcCount)
Dim tData As Variant
Dim arg As Range
Dim ar As Long
Dim c As Long
Dim dr As Long
Dim trCount As Long
For Each arg In frg.Areas
tData = GetRange(arg)
For ar = 1 To arg.Rows.Count
dr = dr + 1
For c = 1 To fcCount
dData(dr, c) = tData(ar, c)
Next c
Next ar
Next arg
GetFilteredRange = dData
'Else ' frShift = 0: frCount = 1: AllowHeadersOnly = False
End If
'Else ' no filtered range
End If
ProcExit:
Exit Function
ClearError:
Debug.Print "'" & ProcName & "': Unexpected Error!" & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
Resume ProcExit
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the values of a range ('rg') in a 2D one-based array.
' Remarks: If ˙rg` refers to a multi-range, only its first area
' is considered.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetRange( _
ByVal rg As Range) _
As Variant
If rg Is Nothing Then Exit Function
If rg.Rows.Count + rg.Columns.Count = 2 Then ' one cell
Dim Data As Variant: ReDim Data(1 To 1, 1 To 1): Data(1, 1) = rg.Value
GetRange = Data
Else ' multiple cells
GetRange = rg.Value
End If
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns a reference to the range starting with a given cell
' and ending with the last cell of its Current Region.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefCurrentRegionBottomRight( _
ByVal FirstCellRange As Range) _
As Range
If FirstCellRange Is Nothing Then Exit Function
With FirstCellRange.Cells(1).CurrentRegion
Set RefCurrentRegionBottomRight = _
FirstCellRange.Resize(.Row + .Rows.Count - FirstCellRange.Row, _
.Column + .Columns.Count - FirstCellRange.Column)
End With
End Function