AWE_SheetArray is an Excel Class Module that can assist you in updating large spreadsheets quickly and easily, with just a few calls. With its ability to manipulate up to a million rows of data in seconds, AWE_SheetArray is an excellent tool to accelerate your Worksheet automation.
Behind the scenes, AWE_SheetArray converts sheet ranges to arrays, allowing for a fast search, update, and even writing back to the sheet. It also sorts multiple column keys that are searched using pattern syntax. If array data is updated, it can be written back to the sheet.
AWE_SheetArray can be used as a stand-alone class or as a complementary tool for the AWE_Sheet class.
Find and Update Data
FAST- Designed to store multiple column ranges (can be unioned together) for up to one million rows in arrays. This allows for quick retrieval and updating of data that can then be easily written back to the sheet.
Multi-Column Keys - Create multi-column keys that are sorted, making it easier to locate data in the array.
Performance- This method took 15 seconds to process 1 million rows:
Initialize - 0 seconds
CreatedKeys - 12 seconds
FindRows & FindCells - 0 seconds
WriteRows - 3 seconds
Conventional VBA Worksheet methods would have taken minutes.
Tips - Call "CreateKeys" and "WriteArray" functions at the beginning and end of your processing.
PublicSubFindAndUpdateData()DimvRowAsVariant,AWEArrAsNewAWE_SheetArray'--- Initialize the AWE_SheetArray ---
WithActiveSheetAWEArr.Initialize3,Union(.Range("$I$4","$I$1000004"),_.Range("$B$4","$B$1000004"),.Range("$J$4","$J$1000004"))EndWith'--- Define keys that are used to find data in your array ---
AWEArr.CreateKeys"Task ID","Date"'--- Find Data, the below finds all TasksIDs="Task-101" in Feb 2021 ---
ForEachvRowInAWEArr.FindRows("Task-101","2021/02/*")'--- Update data in the array ---
AWEArr.FindCells(vRow,"Note")="Found"'--- Retrieve data from the array ---
Debug.PrintAWEArr.FindCells(vRow,"Email Address")&", "&AWEArr.FindCells(vRow,"Date")NextvRow'--- Write the updated array back to the sheet ---
AWEArr.WriteArrayEndSub
Function definitions, parameters and examples are included in the comments before each property or function.
Visual Basics Class IntelliSense will provide a code completion aid for the AWE_Sheet Class Module by listing its functions and parameters as you type your code.
'=====================================================================================================
' AWE_SheetArray: AWE_SheetArray is an Excel Class Module that can assist you in updating large
' spreadsheets quickly and easily, within just a few calls. With its ability to manipulate up to
' one million rows of data in seconds, AWE_SheetArray is an excellent tool for
' automating your work.
'
' This class module converts sheet ranges to arrays, allowing for a fast search, update, and
' even writing back to the sheet. It also sorts multiple column keys that are searched using
' pattern syntax. If array data is updated, it can be written back to the sheet.
'
' AWE_SheetArray can be used as a stand-alone class or as a complementary tool to the
' AWE_Sheet class.
'
' SITE: www.AutomationWithExcel.com
' Author: Mike Libby
'=====================================================================================================
'=== PRIVATE VARIABLES ===============================================================================
'----------------------------------- WORKSHEET VARIABLES
Privatem_WSAsWorksheet' Worksheet
Privatem_ColRngAsRange' Range Columns (can be unioned together)
Privatem_HdrRowNbAsLong' Header Row Number
Privatem_MaxRowAsLong' Maximum number of rows
'----------------------------------- KEY ARRAY VARIABLES
Privatem_KeyRowValArrAsVariant' Stores Key Row Values
Privatem_KeyRowNbrArrAsVariant' Stores the actual sheet Key Row Numbers
Privatem_KeyColNamArrAsVariant' Key Column Name Array
Privatem_KeyColFmtArrAsVariant' Key Column Format Array
'----------------------------------- SHEET.RANGE.VALUE ARRAY VARIABLES
Privatem_RngValArrAsVariant' Range Value Array
Privatem_RngHdrArrAsVariant' Range Header Array
'----------------------------------- TIMER VARIABLES
Privatem_TimeAsLong' Records the elapsed time in seconds
'----------------------------------------------------------------------------------------------------
' ElapsedTime: Start and retrieve a timer for use in measuring performance speed.
' Example: AWESh.ElapedTime or Debug.Print AWESh.ElapsedTime
'----------------------------------------------------------------------------------------------------
PublicFunctionElapsedTime(OptionalTmrAsLong=0)IfTmr<>0Orm_Time=0Thenm_Time=TimerElapsedTime=Format((Timer-m_Time)/86400,"hh:mm:ss")EndFunction'----------------------------------------------------------------------------------------------------
' Initialize: Store multi-column range headers and values in arrays for fast retrieval and
' updating. Non-congruent ranges can be unioned together.
' Params:
' - HdrRowNb As Long, The header's row number
' - ColRng As Range, multi-column range that is to be stored in an array.
' - MaxRows As Long - Specifies the maximum number of rows if the range is to be resized.
' Example: Store a range of 10 columns by 1,000 rows in an array and resize it to 1,000,000 rows.
' Dim AWEArr As New AWE_SheetArray
' AWEArr.Initialize 3, ActiveSheet.Range("$A$4", "$J$1004"), 1,000,000
'----------------------------------------------------------------------------------------------------
PublicFunctionInitialize(HdrRowNbAsLong,ColRngAsRange,OptionalMaxRowsAsLong=0)DimrColAsRange,aColAsRange,vColAsVariant,iColAsLong,iColCntAsLongDimiRowAsLong,rHdrAsRange,iHdrNmAsLong,sNbFmtAsStringDimaKeyArrAsVariant,HdrArrAsVariant,vHdrNmAsVariantSetm_WS=ColRng.Parent:Setm_ColRng=ColRng:m_HdrRowNb=HdrRowNb:m_MaxRow=MaxRowsOnErrorResumeNext'=== Store Column Header and Values ===
ForEachrColInm_ColRng.Columns:iColCnt=iColCnt+rCol.Areas.Count:NextrColReDimm_RngValArr(1ToiColCnt,1To2)ReDimm_RngHdrArr(1ToiColCnt)iCol=0ForEachrColInm_ColRng.ColumnsForEachaColInrCol.Columns:iCol=iCol+1SetrHdr=m_WS.Cells(m_HdrRowNb,aCol.Column)IfMaxRows>0ThenSetaCol=aCol.Resize(MaxRows)m_RngHdrArr(iCol)=rHdr.Value:m_RngValArr(iCol,1)=rHdr.Columnm_RngValArr(iCol,2)=aCol.ValueNextaColNextrColEndFunction'----------------------------------------------------------------------------------------------------
' CreateKeys - The function "CreateKeys" generates an array of row keys and their corresponding
' numbers in a sorted order. To create each row key, the function concatenates the row
' column values in the order they appear. The order of the columns also determines the
' sort order of the array.
' Params:
' - ParamArray KeyColNms() As Variant, The "KeyColNms" parameter defines the columns to concatenate
' for row keys and array sort order. Note that these columns aren't required in the
' "Initialize" function's "ColRng" parameter..
' Example:
' - Create row keys for column names, "Task ID" and "Date".
' Dim AWEArr As New AWE_SheetArray: AWEArr.Initialize 3, ActiveSheet.Range("$A$4", "$J$1000004")
' AWEArr.CreateKeys "Task ID", "Date"
'----------------------------------------------------------------------------------------------------
PublicFunctionCreateKeys(ParamArrayKeyColNms()AsVariant)DimrColAsRange,vColAsVariant,iColAsLong,iRowAsLong,iHdrNmAsLong,sNbFmtAsStringDimaKeyArrAsVariant,HdrArrAsVariant,vHdrNmAsVariantOnErrorResumeNextIfIsArray(m_KeyRowValArr)ThenErasem_KeyRowValArrIfIsArray(m_KeyRowNbrArr)ThenErasem_KeyRowNbrArrIfIsArray(m_KeyColNamArr)ThenErasem_KeyRowValArrIfIsArray(m_KeyColFmtArr)ThenErasem_KeyRowValArrWithm_WS'=== Create Lookup Key Arrays ===
ReDimm_KeyColNamArr(1ToUBound(KeyColNms)+1)ReDimm_KeyColFmtArr(1ToUBound(KeyColNms)+1)ReDimaKeyArr(1Tom_ColRng.Rows.Count+1,1To2)HdrArr=.Range(.Cells(m_HdrRowNb,1),.Cells(m_HdrRowNb,_.Cells(m_HdrRowNb,.Columns.Count).End(xlToLeft).Column))'--- Loop through each key column saving values to arrays ---
ForEachvHdrNmInKeyColNms:iHdrNm=iHdrNm+1'--- Get the sheet column number and range based the column location in the HdrArr ---
iCol=-1:iCol=Application.WorksheetFunction.Match(vHdrNm,HdrArr,0)'--- Display column not found error and end processing ---
IfiCol=-1ThenMsgBoxm_WS.Name&"::AWE_LookupArray::Initialize Error."_&vbLf&vbLf&"Missing Column, """&vHdrNm&".""",vbCritical:EndSetrCol=.Range(.Cells(m_ColRng.Row,iCol),_.Cells(m_ColRng.Row+m_ColRng.Rows.Count,iCol))'--- Capture column number formatting ---
m_KeyColNamArr(iHdrNm)=rCol.EntireColumn.Cells(m_HdrRowNb).ValuesNbFmt=rCol.NumberFormatIfInStr(1,sNbFmt,"yy",vbTextCompare)ThensNbFmt="yyyy/mm/dd"m_KeyColFmtArr(iHdrNm)=sNbFmt'--- Create\save the key ---
vCol=rCol.ValueForiRow=LBound(vCol)ToUBound(vCol)IfsNbFmt="General"ThenaKeyArr(iRow,1)=aKeyArr(iRow,1)&vCol(iRow,1)&"|"_ElseaKeyArr(iRow,1)=aKeyArr(iRow,1)&Format(vCol(iRow,1),sNbFmt)&"|"NextiRowNextvHdrNm'--- Save each key's row number and then sort the entire array ---
ForiRow=LBound(aKeyArr)ToUBound(aKeyArr):aKeyArr(iRow,2)=iRow:NextiRowQSort2DArraKeyArr,1,LBound(aKeyArr),UBound(aKeyArr)'--- Save the key and row number to their respective arrays to speed match calls ---
m_KeyRowValArr=Application.WorksheetFunction.Index(aKeyArr,0,1)m_KeyRowNbrArr=Application.WorksheetFunction.Index(aKeyArr,0,2)EndWithEndFunction'----------------------------------------------------------------------------------------------------
' FindRows: Retrieve the row numbers that match the specified column keys.
'
' Note: The function "FindRows" makes use of Column Keys which are created by concatenating them
' together, storing them in an array and sorting them in the "CreateKeys" function. The "FindRows"
' function uses "match" to find the first row and "like" to find subsequent rows.
'
' It's crucial to consider the order of the keys used in the "CreateKeys" function as it
' determines the sort order used when searching for subsequent rows. If an expected subsequent
' row is not retrieved, it's likely because the columns specified in the "CreateKeys" function
' are out of order.
'
' Use match and like string patterns to find data:
' - ?, Any single character
' - *, Zero or more characters
' - [charlist], any single character in the charlist
' - [!charlist], any single hcaracter not in charlist
' For more information on string patterns, see Microsoft article...
' https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/operators/like-operator
'
' Params: ParamArray Keys() as Variant - Keys concatenated together delimated by '|'. Can use
' Example: List all employees (in a 1,000,000 row sheet) that billed to "Task-101" in December, 2024.
' Dim vRow As Variant, AWEArr As New AWE_SheetArray
' AWEArr.Initialize 3, ActiveSheet.Range("$A$4", "$J$1000004")
' AWEArr.CreateKeys "Task ID", "Date"
' For Each vRow In AWEArr.FindRows("Task-101", "2021/12/*")
' Debug.Print AWEArr.FindCells(vRow, "Employee Nbr") & ", " & vRow
' Next vRow
'----------------------------------------------------------------------------------------------------
PublicFunctionFindRows(ParamArrayKeys()AsVariant)AsCollectionDimiRowAsLong,iKeyAsLong,vKeyAsVariant,sNbFmtAsString,sKeyValAsVariantDimvarAsVariant,iColAsLong,collectnAsNewCollection:OnErrorResumeNext'--- Create the key ---
ForiKey=LBound(Keys)ToUBound(Keys):vKey=Keys(iKey)sNbFmt=m_KeyColNamArr(iKey+1)IfsNbFmt=""ThensKeyVal=sKeyVal&vKey&"|"ElsesKeyVal=sKeyVal&Format(vKey,sNbFmt)&"|"NextiKey'--- Find the first occurance ---
iRow=-1:iRow=Application.WorksheetFunction.Match(sKeyVal,m_KeyRowValArr,0)'--- Update return value, collectn and FoundValues collections ---
DoWhileiRow<>-1collectn.Addm_KeyRowNbrArr(iRow,1)+(m_HdrRowNb)iRow=iRow+1:IfiRow>UBound(m_KeyRowNbrArr)OrNot(m_KeyRowValArr(iRow,1)LikesKeyVal)ThenExitDoLoopSetFindRows=collectnEndFunction'----------------------------------------------------------------------------------------------------
' FindCells: The "FindCells" Get Property returns a value in the array based on row number and
' column name or number.
' Params:
' - RowNb as Variant - The Array Row that contains the data
' - ColNmOrNb as Variant - The Column name or number that contains the data. Note, use column
' number to improve speed on huge spreadsheets.
' Example: List all employees (in a 1,000,000 row sheet) that billed to "Task-101" in Dec, 2024.
' Dim vRow As Variant, AWEArr As New AWE_SheetArray
' AWEArr.Initialize 3, ActiveSheet.Range("$A$4", "$J$1000004")
' AWEArr.CreateKeys "Task ID", "Date"
' For Each vRow In AWEArr.FindRows("Task-101", "2021/12/*")
' AWEArr.FindCells(vRow, "Note") = "Found"
' Next vRow
'----------------------------------------------------------------------------------------------------
PublicPropertyGetFindCells(RowNbAsVariant,ColNmOrNbAsVariant)AsVariantDimiColAsLongOnErrorResumeNextSelectCaseTypeName(ColNmOrNb)Case"Long","Integer":iCol=CLng(ColNmOrNb)Case"String":'--- Find the column name's column number ---
iCol=-1:iCol=Application.WorksheetFunction.Match(ColNmOrNb,m_RngHdrArr,0)IfiCol=-1ThenMsgBoxm_WS.Name&"::AWE_LookupArray::FindCells Error."&vbLf&vbLf&_"Missing Column, """&ColNmOrNb&".""",vbCritical:EndEndSelect'--- Return the array column value ---
FindCells=m_RngValArr(iCol,2)(RowNb-m_HdrRowNb,1)EndProperty'----------------------------------------------------------------------------------------------------
' FindCells: The "FindCells Let Property updates an array value based on row number and column name
' or number.
' Params:
' - RowNb as Variant - The Array Row that contains the data
' - ColNmOrNb as Variant - The Column name or number that contains the data. Note, use column
' number to improve speed on huge spreadsheets.
' Example: Update the note column to "Found" for employees that billed to "Task-101" in Dec, 2024.
' Dim vRow As Variant, AWEArr As New AWE_SheetArray
' AWEArr.Initialize 3, ActiveSheet.Range("$A$4", "$J$1000004")
' AWEArr.CreateKeys "Task ID", "Date"
' For Each vRow In AWEArr.FindRows("Task-101", "2021/12/*")
' AWEArr.FindCells(vRow, "Note") = "Found"
' Next vRow
' AWEArr.WriteArray
'----------------------------------------------------------------------------------------------------
PublicPropertyLetFindCells(RowNbAsVariant,ColNmOrNbAsVariant,ValueAsVariant)DimiColAsLongOnErrorResumeNextSelectCaseTypeName(ColNmOrNb)Case"Long","Integer":iCol=CLng(ColNmOrNb)Case"String":'--- Find the column name's column number ---
iCol=-1:iCol=Application.WorksheetFunction.Match(ColNmOrNb,m_RngHdrArr,0)IfiCol=-1ThenMsgBoxm_WS.Name&"::AWE_LookupArray::FindCells Error."&vbLf&vbLf&_"Missing Column, """&ColNmOrNb&".""",vbCritical:EndEndSelect'--- Set the array value ---
m_RngValArr(iCol,2)(RowNb-m_HdrRowNb,1)=ValueEndProperty'----------------------------------------------------------------------------------------------------
' WriteArray: The function "WriteArray" writes all the values stored in the array back to the range
' that was defined in the "Initialize" function.
' Example: Update the note column to "Found" for employees that billed to "Task-101" in Dec, 2024.
' Dim vRow As Variant, AWEArr As New AWE_SheetArray
' AWEArr.Initialize 3, ActiveSheet.Range("$A$4", "$J$1000004")
' AWEArr.CreateKeys "Task ID", "Date"
' For Each vRow In AWEArr.FindRows("Task-101", "2021/12/*")
' AWEArr.FindCells(vRow, "Note") = "Found"
' Next vRow
' AWEArr.WriteArray
'----------------------------------------------------------------------------------------------------
PublicFunctionWriteArray()DimaColAsRange,rColAsRange,iColAsLong'--- Write each column back to the sheet ---
ForEachrColInm_ColRng.ColumnsForEachaColInrCol.Columns:iCol=iCol+1rCol.Value=m_RngValArr(iCol,2)NextaColNextrColEndFunction'==== Private Functions ==============================================================================
PrivateFunctionQSort2DArr(arrAsVariant,SortColAsLong,FirstAsLong,LastAsLong)DimvMidValAsVariant,vTempAsVariant,lTempLowAsLong,lTempHiAsLong,i2DAsLonglTempLow=First:lTempHi=Last'--- Find midpoint, divide and conquer ---
vMidVal=arr((First+Last)\2,SortCol)DoWhilelTempLow<=lTempHi'--- Find low occurance ---
DoWhilearr(lTempLow,SortCol)<vMidValAndlTempLow<Last:lTempLow=lTempLow+1:Loop'--- Find high occurance ---
DoWhilevMidVal<arr(lTempHi,SortCol)AndlTempHi>First:lTempHi=lTempHi-1:LoopIflTempLow<=lTempHiThen'--- Swap 2D array elements ---
Fori2D=LBound(arr,2)ToUBound(arr,2)vTemp=arr(lTempLow,i2D)arr(lTempLow,i2D)=arr(lTempHi,i2D)arr(lTempHi,i2D)=vTempNexti2DlTempLow=lTempLow+1:lTempHi=lTempHi-1EndIfLoop'--- Recursive ---
IfFirst<lTempHiThenQSort2DArrarr,SortCol,First,lTempHiIflTempLow<LastThenQSort2DArrarr,SortCol,lTempLow,LastEndFunction