I wanted to write a macro that can sum 1000 matrices of the same order (50 rows*30 columns) stacked in a sheet and separated by two empty rows...I did many trials uselessly...Can you provide/refer me to an example or book that deal with such problem? Thanks
Sum Up Matrices
Copy the two codes into a standard module (e.g. Module1).
Only the Sub is run, the Function is called by the Sub.
Adjust the constants including the workbooks.
The Code
Option Explicit
Sub sumUpMatrices()
' Source
Const srcName As String = "Sheet1"
Const srcFirstCell As String = "A1"
' Target
Const tgtName As String = "Sheet2"
Const tgtFirstCell As String = "A1"
' Matrices
Const mRows As Long = 50
Const mCols As Long = 30
Const mCount As Long = 1000
Const mEmpty As Long = 2
' Workbooks
Dim src As Workbook: Set src = ThisWorkbook
Dim tgt As Workbook: Set tgt = ThisWorkbook
' Write values from Source Range to Source Array.
Dim Source As Variant
Source = src.Worksheets(srcName).Range(srcFirstCell) _
.Resize(mCount * (mRows + mEmpty) - mEmpty, mCols)
' Write values from Source Array to Target Array.
Dim Target As Variant
Target = sumUpVerticalMatrices(Source, mRows, mCols, mCount, mEmpty)
' Write values from Target Array to Target Range.
tgt.Worksheets(tgtName).Range(tgtFirstCell).Resize(mRows, mCols) = Target
End Sub
Function sumUpVerticalMatrices(MatricesResult As Variant, _
ByVal RowsCount As Long, _
ByVal ColumnsCount As Long, _
ByVal MatricesCount As Long, _
ByVal Gap As Long) As Variant
Dim rOff As Long: rOff = RowsCount + Gap
Dim Result As Variant: ReDim Result(1 To RowsCount, 1 To ColumnsCount)
Dim i As Long, j As Long, k As Long, CurrVal As Double
For i = 1 To RowsCount
For j = 1 To ColumnsCount
CurrVal = 0
For k = 1 To MatricesCount
CurrVal = CurrVal + MatricesResult(i + (k - 1) * rOff, j)
Next k
Result(i, j) = CurrVal
Next j
Next i
sumUpVerticalMatrices = Result
End Function
Generate Random Data
Sub writeRandomVerticalMatrices()
' Worksheet
Const wsName As String = "Sheet1"
Const FirstCell As String = "A1"
' Matrices
Const mRows As Long = 50
Const mCols As Long = 30
Const mCount As Long = 1000
Const mEmpty As Long = 2
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook
' Write data to Data Array.
Dim Data As Variant
Data = getRandomVerticalMatrices(mRows, mCols, mCount, mEmpty)
' Write from Data Array to Worksheet
wb.Worksheets(wsName).Range(FirstCell) _
.Resize(UBound(Data), UBound(Data, 2)).Value = Data
End Sub
Function getRandomVerticalMatrices(ByVal RowsCount As Long, _
ByVal ColumnsCount As Long, _
ByVal MatricesCount As Long, _
ByVal Gap As Long) As Variant
Dim rOff As Long: rOff = RowsCount + Gap
Dim Result As Variant
ReDim Result(1 To MatricesCount * rOff - Gap, 1 To ColumnsCount)
Dim i As Long, j As Long, k As Long
For i = 1 To RowsCount
For j = 1 To ColumnsCount
For k = 1 To MatricesCount
Result(i + (k - 1) * rOff, j) = Int(500 * Rnd()) + 1
Next k
Next j
Next i
getRandomVerticalMatrices = Result
End Function
As you did not give much information I assume the first matrix starts in A1 and has 50 rows and 30 columns. Then you have two empty rows and then we have the next matrix, i.e. the second matrix starts in A53! Based on these assumptions you could put together the sum of these matrices like that
Option Explicit
Sub SumMat()
' This will build the formula for the first cell
' and copy & paste to the other cells
Const NO_LINES = 50
Const NO_MATRIX = 1000
Const NO_COLUMNS = 30
Const NO_EMPTYROWS = 2
' Const NO_LINES = 3
' Const NO_MATRIX = 5
' Const NO_COLUMNS = 3
' Const NO_EMPTYROWS = 2
Dim rg As Range
Dim offSet As Long
offSet = NO_LINES + NO_EMPTYROWS
' This will build the formula
' It is just A1 + A53 * A105 + A157 + ...
Dim i As Long
Dim formula(1 To NO_MATRIX) As String
Dim counter As Long
counter = 0
For i = 1 To NO_MATRIX
formula(i) = "A" & CStr(1) + counter * offSet
counter = counter + 1
Next i
' First cell of the result matrix is to the right with two empty columns to the source matrices
Set rg = Cells(1, NO_COLUMNS + 2)
' this will put the formula for the sum of the first cells into row 1 and in your case column 32
rg.formula = "=" & Join(formula, "+")
' Copy the formula
rg.Copy
' Resize the range to the size of the matrix
Set rg = rg.Resize(NO_LINES, NO_COLUMNS)
' paste the formula
rg.PasteSpecial xlPasteFormulas
End Sub
Related
I'm getting data in a collection from a web request and I need to put it into a column. While this function does what it needs, it is slow. I have already set xlCalculationManual in an earlier function and ScreenUpdating and EnableEvents did not affect much.
I need to run the data inserts in batches of 15000 because transpose returns only about 17900 first items. But could I build the array in CollectionToArray function in such a way that I don't need to transpose it? I tried to make it return a two dimensional array and put the values to the second dimension, but it did not work. I still needed to transpose it, or all the rows in the column had the first element of the array.
Function replaceList(startCol As Integer, endCol As Integer, values As Collection)
Dim batch As Long
Dim batchStart As Long
Dim batchEnd As Long
Dim b As Long
batch = 15000
With Worksheets("MyData")
Do While b * batch < values.Count
If (b + 1) * batch > values.Count Then
batchEnd = values.Count
Else
batchEnd = (b + 1) * batch
End If
batchStart = b * batch + 1
.Range(.Cells(batchStart + 2, startCol), .Cells(batchEnd + 2, endCol)).Value = Application.Transpose(CollectionToArray(values, batchStart, batchEnd))
b = b + 1
Loop
End With
End Function
Function CollectionToArray(C As Collection, batchStart As Long, batchEnd As Long) As Variant()
Dim a() As Variant: ReDim a(0 To (batchEnd - batchStart))
Dim i As Long
Dim j As Integer
j = 0
For i = batchStart To batchEnd
a(j) = C.item(i)
j = j + 1
Next
CollectionToArray = a
End Function
Write Collection Values to a Worksheet Column
Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Writes the numbers from one to a million to a collection
' and returns the values of the collection in a one-column range.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Calls: 'WriteCollectionToColumnRange'
' 'GetCollection'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub CollectionToColumnExample()
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Worksheets("MyData")
Dim fCell As Range: Set fCell = ws.Range("A2")
' Populate collection with dummy data.
Dim coll As Collection: Set coll = New Collection
Dim n As Long
For n = 1 To 1000000
coll.Add n
Next n
' Write the values to a range.
WriteCollectionToColumnRange fCell, coll
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the values of a collection in a one-column range.
' Calls: 'GetCollection'.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub WriteCollectionToColumnRange( _
ByVal FirstCell As Range, _
ByVal coll As Object)
Dim Data As Variant: Data = GetCollection(coll)
Dim rg As Range: Set rg = FirstCell.Resize(UBound(Data, 1), 1)
rg.Value = Data
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Returns the values from a collection in a 2D one-based one-column array.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetCollection( _
ByVal coll As Collection) _
As Variant
Dim Data As Variant: ReDim Data(1 To coll.Count, 1 To 1)
Dim Item As Variant
Dim r As Long
For Each Item In coll
r = r + 1
Data(r, 1) = Item
Next Item
GetCollection = Data
End Function
I'm trying to make a macro for where a user inputs a number and the even numbers are repeated in an array. I have got the code for repeating the numbers from 0-n (n being the number inputted). However, I don't know how to go about repeating the even numbers twice.
Sub Macro3()
For n = 1 To Worksheets("Sheet1").Cells(1, 2) + 1
Cells(2, 1 + n).Select
ActiveCell.FormulaR1C1 = (n - 1)
Next
End Sub
Below is the output
Current code vs what I really want
Write an Array of Integers
Writes an array of integers between 0 and the specified value in cell B1 to a row range starting from B2. Even numbers are written twice (one worksheet).
Initial Solution
This is a slow solution meant to be educational in understanding object variables (workbook-worksheet-range), ranges (Resize, Offset), loops,...
Option Explicit
Sub WriteArrayOfIntegersRange()
Const ProcTitle As String = "Write Array of Integers Range"
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Worksheets("Sheet1")
' Create a reference to the source cell.
Dim sCell As Range: Set sCell = ws.Range("B1")
' Write the value of the source cell to a variable.
Dim sValue As Variant: sValue = sCell.Value
Dim LastInteger As Long
' Validate the source cell value.
If IsNumeric(sValue) Then ' is a number
LastInteger = Abs(CLng(sValue)) ' positive ('Abs'), whole ('CLng')
Else ' is not a number
MsgBox "The value in cell '" & sCell.Address(0, 0) & "' ('" _
& sValue & "' is not a number.", vbCritical, ProcTitle
Exit Sub
End If
' Create a reference to the first destination cell.
Dim dCell As Range: Set dCell = ws.Range("B2"): dCell.Value = 0
Dim Size As Long: Size = 1
Dim n As Long
' Loop through the numbers and apply alternating row size (1 or 2)
' and column offset (2 or 1) before writing.
For n = 1 To LastInteger
Set dCell = dCell.Offset(, Size) ' define next first cell
Size = 2 - n Mod 2 ' calculate the size (Odd = 1, Even = 2)
dCell.Resize(, Size).Value = n ' write to the resized row range
Next n
' Clear the range to the right of the last cell to remove any previous data.
Dim crrg As Range
With dCell.Offset(, Size) ' define next first cell
' Define the range from the next first to the last worksheet cell
' in the row.
Set crrg = .Resize(, ws.Columns.Count - .Column + 1)
End With
crrg.Clear ' or crrg.ClearContents
MsgBox "Array of numbers written.", vbInformation, ProcTitle
End Sub
Using Arrays
This is a more advanced solution that utilizes the multi-purpose GetArrayOfIntegers function. By modifying the related constants (Function Parameters) in the following procedure, you can easily change the output.
Note that it returns the results in another worksheet (Sheet2).
The last procedure is created for anyone to quickly get a flavor of the GetArrayOfIntegers function. Just add a new workbook, add a new module and copy the codes to it. Modify the function parameters in the last procedure to get different results in the Immediate window (Ctrl+G).
Sub WriteArrayOfIntegers()
' Needs the 'GetArrayOfIntegers' function.
Const ProcTitle As String = "Write Array of Numbers"
' Source
Const sName As String = "Sheet1"
Const sCellAddress As String = "B1"
' Destination
Const dName As String = "Sheet2"
Const dfCellAddress As String = "B2"
' Function Parameters ' experiment with these five parameters
Const EvensCount As Long = 2
Const OddsCount As Long = 1
Const DoReturnRow As Boolean = True
Const IncludeZero As Boolean = True
Const IsZeroOdd As Boolean = True
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Create a reference to the source cell.
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim sCell As Range: Set sCell = sws.Range(sCellAddress)
' Write the value of the source cell to a variable.
Dim sValue As Variant: sValue = sCell.Value
Dim LastInteger As Long
' Validate the source cell value.
If IsNumeric(sValue) Then ' is a number
LastInteger = Abs(CLng(sValue)) ' positive ('Abs'), whole ('CLng')
Else ' is not a number
MsgBox "The value in cell '" & sCell.Address(0, 0) & "' ('" _
& sValue & "' is not a number.", vbCritical, ProcTitle
Exit Sub
End If
' Return the result (an array) of the 'GetArrayOfIntegers' function.
Dim Data As Variant: Data = GetArrayOfIntegers( _
LastInteger, EvensCount, OddsCount, DoReturnRow, IncludeZero, IsZeroOdd)
' Without the constants it would be:
'Data = GetArrayOfIntegers(LastInteger, 2, 1, True, True, True)
If IsEmpty(Data) Then Exit Sub
Dim drCount As Long: drCount = UBound(Data, 1)
Dim dcCount As Long: dcCount = UBound(Data, 2)
' Create a reference to the first destination cell.
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dfCell As Range: Set dfCell = dws.Range(dfCellAddress)
' Clear all cells next to (to the right of) and below
' the first destination cell.
Dim dcrg As Range: Set dcrg = dfCell.Resize( _
dws.Rows.Count - dfCell.Row + 1, dws.Columns.Count - dfCell.Column + 1)
dcrg.Clear ' or dcrg.ClearContents
' Create a reference to the destination range.
Dim drg As Range: Set drg = dfCell.Resize(drCount, dcCount)
' Write the values from the array to the destination range.
drg.Value = Data
MsgBox "Array of numbers written.", vbInformation, ProcTitle
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Author: VBasic2008
' Dates: 20211101
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns an array of integers in a 2D one-based array.
' Remarks: The first element is always 0 or 1.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetArrayOfIntegers( _
ByVal LastInteger As Long, _
Optional ByVal EvensCount As Long = 1, _
Optional ByVal OddsCount As Long = 1, _
Optional ByVal DoReturnRow As Boolean = False, _
Optional ByVal IncludeZero As Boolean = False, _
Optional ByVal IsZeroOdd As Boolean = False) _
As Variant
Dim eoArr() As Long: ReDim eoArr(0 To 1)
eoArr(0) = EvensCount: eoArr(1) = OddsCount
Dim zCount As Long
If IncludeZero Then
If IsZeroOdd Then zCount = OddsCount Else zCount = EvensCount
End If
Dim iMod As Long: iMod = LastInteger Mod 2
Dim eCount As Long: eCount = Int(LastInteger / 2)
Dim oCount As Long: oCount = Int(LastInteger / 2) + iMod
Dim dtCount As Long
dtCount = eCount * EvensCount + oCount * OddsCount + zCount
Dim Data As Variant
Dim dt As Long: dt = 1
Dim n As Long
Dim r As Long
If DoReturnRow Then
ReDim Data(1 To 1, 1 To dtCount)
If zCount > 0 Then
For dt = 1 To zCount: Data(1, dt) = 0: Next dt
End If
For n = 1 To LastInteger
For r = 1 To eoArr(n Mod 2)
Data(1, dt) = n
dt = dt + 1
Next r
Next n
Else
ReDim Data(1 To dtCount, 1 To 1)
If zCount > 0 Then
For dt = 1 To zCount: Data(dt, 1) = 0: Next dt
End If
For n = 1 To LastInteger
For r = 1 To eoArr(n Mod 2)
Data(dt, 1) = n
dt = dt + 1
Next r
Next n
End If
GetArrayOfIntegers = Data
End Function
' This is an unrelated example to play with.
' Note that changing the fourth parameter will make no difference since
' the results are written to the Immediate window (Ctrl+G).
Sub GetArrayOfIntegersTEST()
' Needs the 'GetArrayOfIntegers' function.
Dim Data As Variant: Data = GetArrayOfIntegers(4, 3, 2, False, False, False)
Dim r As Long, c As Long
For r = 1 To UBound(Data, 1)
For c = 1 To UBound(Data, 2)
Debug.Print Data(r, c)
Next c
Next r
End Sub
Fast alternative via ArrayList
Working with an ArrayList (disposing btw of methods like .Sort,.Remove, .Insert, .Reverse) may be a convenient way to manipulate array data in a very readable way. It is not part of VBA, but can be accessed easily via late binding (referring to .Net library mscorlib.dll).
Option Explicit ' code module head
Sub DoubleEvenNumbersGreaterOne()
'a) define upper limit
Dim ws As Worksheet
Set ws = Sheet1 ' << change to project's sheet Code(Name)
Dim Limit As Long
Limit = ws.Range("B1")
'b) declare ArrayList
Dim arr As Object ' late bind .Net mscorlib.dll
Set arr = CreateObject("System.Collections.ArrayList")
'c) populate list array
arr.Add 0 ' start adding with zero
Dim i As Long
For i = 1 To Limit ' loop through sequence 1:Limit
arr.Add i ' add current number
If i Mod 2 = 0 Then arr.Add i ' additional even number
Next
'd) get array
Dim a As Variant: a = arr.ToArray ' change ArrayList object to VBA array
'Debug.Print Join(a, "|") ' optional check in VB Editor's immediate window
'e) write 0-based 1-dim array to ws (here: Sheet1) or declare another target worksheet (e.g. ws2)
With ws.Range("B2")
.EntireRow = vbNullString ' empty target row
.Resize(1, UBound(a) + 1) = a ' write values into correct number of cells
End With
End Sub
A formula oriented approach // late edit as of 11/1 2021
a) A first and very elementary way would be to
enter formula =COLUMN(A1)-INT((COLUMN(A1)+2)/3) into cell B2 and to
copy into the right neighbour cells as long as you get the wanted maximum
b) Refining this approach you can code the following udf accepting the wanted maximum as argument (note that I changed the flat Column reference to a vertical Row reference to simplify calculation of the actual maxima):
Function Sequ(ByVal maxNo As Long)
Dim myFormula As String
myFormula = Replace("=ROW(1:$)-INT((ROW(1:$)+2)/3)", "$", maxNo + maxNo \ 2 + 1)
Sequ = Application.Transpose(Evaluate(myFormula))
End Function
A direct formula entry of e.g. =Sequ(10) into B2 benefitting from the newer dynamic features of vers. 2019+/MS 365 would display all (row) elements automatically in a so called spill range without need of further inputs.
Using VBA to display results in VB Editor's immediate window could be coded as follows: Debug.Print Join(Sequ(10), "|") resulting in
0|1|2|2|3|4|4|5|6|6|7|8|8|9|10|10
or to assign the results to a variable that can be used in further code.
Your code is really ok, just add question is number even and one more variable to see where to write. Also just change n loop from 0:
Sub Macro3()
For n = 0 To Worksheets("Sheet1").Cells(1, 2)
a = a + 1
Cells(2, 2 + a).Select
ActiveCell.FormulaR1C1 = n
'check if number is even and check if a > 1 because we don't want to repeat 0
If n Mod 2 = 0 And a > 1 Then
a = a + 1
Cells(2, 2 + a).Select
ActiveCell.FormulaR1C1 = n
End If
Next
End Sub
Try this code
Sub Test()
Dim v, ws As Worksheet, i As Long, ii As Long, n As Long
Set ws = ThisWorkbook.Worksheets("Sheet1")
v = ws.Range("B1").Value
If Not IsNumeric(v) Or IsEmpty(v) Then MsgBox "Must Be Number", vbExclamation: Exit Sub
ReDim a(1 To (v / 2) + v)
For i = 1 To v
If i Mod 2 = 0 Then
For ii = 1 To 2
n = n + 1: a(n) = i
Next ii
Else
n = n + 1: a(n) = i
End If
Next i
Range("C2").Resize(, UBound(a)).Value = a
End Sub
Sub adress()
Dim s As Long
Dim h As Long
Dim n As Long
Dim i As Long
s = 1
n = 1
h = 1
For n = 1 To 1800
For i = 1 To 2000
If ActiveSheet.Cells(h + 1, 13) = ActiveSheet.Cells(s + 1, 32) Then
ActiveSheet.Cells(h + 1, 48) = ActiveSheet.Cells(s + 1, 36)
ActiveSheet.Cells(h + 1, 51) = ActiveSheet.Cells(s + 1, 37)
End If
s = s + 1
Next i
h = h + 1
i = 1
Next n
End Sub
This code is written to grab a value in a column of an excel spread sheet, then go to the next column and search the whole column for a matching value. Once that is found it will print the value that is in a cell in the same row of the value in the second column it found, into a cell in the same row as the original value it was trying to match.
While the inner loop works and my code will do the correct process when ran, it only does it for one value in the first column. I have tried using ranges in the For Loops, I have tried do while loops and do until loops. If i manually change the value of "h" and run the code it will progress down the column and print the correct information but i cannot get "h" to update on its own.
Nested For Next Loops
Although Exit For and turning off the two application settings are used, the first procedure still takes 45 seconds on my machine (without the 'improvements' it might take half an hour).
In the second code the inner loop is replaced with Application.Match and the operations are performed using arrays. It takes less than a second.
The Code
Option Explicit
Sub loopSlow()
Dim i As Long
Dim k As Long
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
With ActiveSheet
For i = 2 To 1801
For k = 2 To 2001
If .Cells(i, 13).Value = .Cells(k, 32).Value Then
.Cells(i, 48).Value = .Cells(k, 36).Value
.Cells(i, 51).Value = .Cells(k, 37).Value
Exit For
End If
Next k
Next i
End With
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
Sub loopFast()
' Source
Const sName As String = "Sheet2"
Const sColsList As String = "AF,AJ,AK"
Const sFirstRow As Long = 2
' Destination (Lookup)
Const dName As String = "Sheet2"
Const dColsList As String = "M,AV,AY"
Const dFirstRow As Long = 2
' Define workbook.
Dim wb As Workbook: Set wb = ThisWorkbook
' Declare variables.
Dim ws As Worksheet
Dim rg As Range
Dim Cols() As String
Dim cUpper As Long
Dim cOffset As Long
Dim n As Long
' Write values from Source Columns to arrays of Data Array.
Cols = Split(sColsList, ",")
cUpper = UBound(Cols)
Set ws = wb.Worksheets(sName)
Set rg = ws.Cells(ws.Rows.Count, Cols(0)).End(xlUp)
Set rg = ws.Range(ws.Cells(sFirstRow, Cols(0)), rg)
Dim Data As Variant: ReDim Data(0 To cUpper)
For n = 0 To cUpper
cOffset = ws.Columns(Cols(n)).Column - rg.Column
Data(n) = rg.Offset(, cOffset).Value
Next n
' Write values from Lookup Column to Lookup Array of Result Array.
Cols = Split(dColsList, ",")
Set ws = wb.Worksheets(dName)
Set rg = ws.Cells(ws.Rows.Count, Cols(0)).End(xlUp)
Set rg = ws.Range(ws.Cells(dFirstRow, Cols(0)), rg)
Dim Result As Variant: ReDim Result(0 To cUpper)
Result(0) = rg.Value
' Define the (remaining) Write Arrays of Result Array.
Dim ResultNew As Variant: ReDim ResultNew(1 To UBound(Result(0)), 1 To 1)
For n = 1 To cUpper
Result(n) = ResultNew
Next n
' Write values from Data Array to Write Arrays of Result Array.
Dim cIndex As Variant
Dim i As Long
For i = 1 To UBound(Result(0))
cIndex = Application.Match(Result(0)(i, 1), Data(0), 0)
If IsNumeric(cIndex) Then
For n = 1 To cUpper
Result(n)(i, 1) = Data(n)(cIndex, 1)
Next n
End If
Next i
' Write values from Write Arrays of Result Array to Destination Columns.
For n = 1 To cUpper
cOffset = ws.Columns(Cols(n)).Column - rg.Column
rg.Offset(, cOffset).Value = Result(n)
Next n
End Sub
Current i using formula(index and Match) to create matrix i wish using VBA coding, this will make more fast compair to formula. Thanks in advance
enter image description here
Sub columntomatrix
Dim mS As Worksheet
Dim eS As Worksheet
Set mS = ThisWorkbook.Sheets("Matrix")
Set eS = ThisWorkbook.Sheets("Price Entry Book")
Dim Matrix() As String
Dim entryPrice() As String
Dim Product As Range
Dim PriceBook As Range
Set Product = Range("Product")
Set PriceBook = Range("PriceBookName")
With mS.Range("B2")
.Formula = "=IFERROR(INDEX(ListPrice,
MATCH(" & .Offset(0,-1).Address(False, True) & "&" &
.Offset(-1, 0).Address(True, False) & ",ProductKey,0)),"" N/A "")"
Product.Copy
'offset(0,-1) = selected cells move to left 1 column'
.Offset(0, -1).PasteSpecial
PriceBook.Copy
.offset(-1,0) = selected cells move to up 1 row'
.Offset(-1, 0).PasteSpecial Paste:=xlPasteAll, Transpose:=True
With Range(.Offset(0, 0), .Offset(Product.Rows.Count - 2, PriceBook.Rows.Count - 2))
.FillDown
.FillRight
End with
End with
End Sub
Pivot RCV
Copy all four procedures to a standard module, e.g. Module1.
Carefully adjust the values in the Define constants. section of pivotRCV.
Only run the first procedure pivotRCV, the others are being called by it.
The Code
Option Explicit
Sub pivotRCV() ' RCV: Row Labels, Column Labels, and Values
' Define constants.
' Define Source constants.
Const srcName As String = "Price Entry Book"
Const srcFirst As String = "A2"
Const rlCol As Long = 1
Const clCol As Long = 2
Const vCol As Long = 4
Const rlSort As Boolean = False
Const clSort As Boolean = False
' Define Target constants.
Const tgtName As String = "Matrix"
Const tgtFirst As String = "A2"
' Define workbooks.
Dim src As Workbook
Set src = ThisWorkbook
Dim tgt As Workbook
Set tgt = ThisWorkbook
' Define Source Range.
' Define Source Worksheet.
Dim ws As Worksheet
Set ws = src.Worksheets(srcName)
' Define Source Range.
Dim rng As Range
Set rng = defineEndRange(ws.Range(srcFirst))
' Write values from Source Range to arrays.
' Write values from Source Range to 1D Unique Row Labels Array.
Dim rLabels As Variant
rLabels = getUniqueColumn1D(rng.Columns(rlCol).Resize(rng.Rows.Count - 1) _
.Offset(1))
If rlSort Then
sort1D rLabels
End If
' Write values from Source Range to 1D Unique Column Labels Array.
Dim cLabels As Variant
cLabels = getUniqueColumn1D(rng.Columns(clCol).Resize(rng.Rows.Count - 1) _
.Offset(1))
If clSort Then
sort1D cLabels
End If
' Write values from Source Range to 2D Source Array.
Dim Source As Variant
Source = rng.Value
' Prepare to write values from Source Array to Target Array.
' Define Target Array.
Dim Target As Variant
ReDim Target(1 To UBound(rLabels) - LBound(rLabels) + 2, _
1 To UBound(cLabels) - LBound(cLabels) + 2)
' Define counters.
Dim n As Long
Dim i As Long
i = 1
' Write values from Source Arrays to Target Array.
' Write first row/column label.
Target(1, 1) = Source(1, 1)
' Write row labels.
For n = LBound(rLabels) To UBound(rLabels)
i = i + 1
Target(i, 1) = rLabels(n)
Next n
' Write column labels.
Dim j As Long
j = 1
For n = LBound(cLabels) To UBound(cLabels)
j = j + 1
Target(1, j) = cLabels(n)
Next n
' Write values.
For n = 2 To UBound(Source, 1)
i = Application.Match(Source(n, rlCol), rLabels, 0) + 1
j = Application.Match(Source(n, clCol), cLabels, 0) + 1
Target(i, j) = Source(n, vCol)
Next n
' Write values from Target Array to Target Range.
' Define Target Worksheet.
Set ws = tgt.Worksheets(tgtName)
' Define Target First Row Range.
With ws.Range(tgtFirst).Resize(, UBound(Target, 2))
' Clear contents from Target First Row Range to the bottom-most row.
.Resize(ws.Rows.Count - .Row + 1).ClearContents
' Define Target Range.
Set rng = .Resize(UBound(Target, 1))
End With
' Write values from Target Array to Target Range.
rng.Value = Target
' Inform user.
MsgBox "Data transferred.", vbInformation, "Success"
End Sub
' Defines the range from a specified first cell to the last cell
' of its Current Region.
Function defineEndRange(FirstCellRange As Range) _
As Range
' Define Current Region ('rng').
Dim rng As Range
Set rng = FirstCellRange.CurrentRegion
' Define End Range.
Set defineEndRange = FirstCellRange _
.Resize(rng.Rows.Count + rng.Row - FirstCellRange.Row, _
rng.Columns.Count + rng.Column - FirstCellRange.Column)
End Function
' Returns the unique values from a column range.
Function getUniqueColumn1D(ColumnRange As Range, _
Optional ByVal Sorted As Boolean = False) _
As Variant
Dim Data As Variant
Data = ColumnRange.Columns(1).Value
With CreateObject("Scripting.Dictionary")
.CompareMode = vbTextCompare
Dim Key As Variant
Dim i As Long
For i = 1 To UBound(Data, 1)
Key = Data(i, 1)
If Not IsError(Key) And Not IsEmpty(Key) Then
.Item(Key) = Empty
End If
Next i
getUniqueColumn1D = .Keys
End With
End Function
' Sorts a 1D array only if it contains the same data type.
Sub sort1D(ByRef OneD As Variant, _
Optional ByVal Descending As Boolean = False)
With CreateObject("System.Collections.ArrayList")
Dim i As Long
For i = LBound(OneD) To UBound(OneD)
.Add OneD(i)
Next i
.Sort
If Descending Then
.Reverse
End If
OneD = .ToArray
End With
End Sub
Here are the particulars. Let's say you have a 10 X 10 range of cells- A1:J10 will work fine. What I'm trying to find via VBA is how many pairs of non intersecting ranges containing 5 cells and 4 cells can be derived from the main range of A1:J10. For example, A1:A5 (5 cell subset) and A6:A9 (4 cell subset) would be a valid pair. A1:E5 and A1:A4 would not be a valid pair due to the intersection of ranges at A1. I'm mostly just looking for the number of valid pairs, but if somebody can show how to physically list all the pairs on a worksheet as well that would help out.
Thanks in advance for the help!
Range Subset Combinations
Option Explicit
Sub writeRangePairs()
' Source
Const wsName As String = "Sheet1"
Const srcRange = "A1:J10"
Const RC1 As Long = 5
Const RC2 As Long = 4
' Target
Const tgtFirstCell As String = "L1"
' Other
Dim wb As Workbook
Set wb = ThisWorkbook
' Define Source Range.
Dim ws As Worksheet
Set ws = wb.Worksheets(wsName)
Dim rng As Range
Set rng = ws.Range(srcRange)
' Write all possible range addresses
' to Source Arrays ('Source1', 'Source2').
Dim Source1 As Variant
Source1 = getOneRowOneCol(rng, RC1)
Dim Source2 As Variant
Source2 = getOneRowOneCol(rng, RC2)
' Define Target Array. It is too big,
' but It cannot be bigger than UB1 * UB2.
Dim Target As Variant
ReDim Target(1 To UBound(Source1) * UBound(Source2), 1 To 3)
Dim i As Long
Dim k As Long
Dim m As Long
' Write values from Source Arrays to Target Array.
For i = 1 To UBound(Source1)
For k = 1 To UBound(Source2)
If Intersect(ws.Range(Source1(i, 1)), ws.Range(Source2(k, 1))) _
Is Nothing Then
m = m + 1
' Write first range address.
Target(m, 1) = Source1(i, 1)
' Write second range address.
Target(m, 2) = Source2(k, 1)
' Write both addresses as one range address. This column
' will contain duplicates which could be removed
' by using a dictionary.
Target(m, 3) = Union(ws.Range(Source1(i, 1)), _
ws.Range(Source2(k, 1))).Address(0, 0)
End If
Next k
Next i
' Write values from Target Array to Target Range. Since Target Array
' is too big, we are using 'm' instead of 'UBound(Target, 1)'.
ws.Range(tgtFirstCell).Resize(m, UBound(Target, 2)).Value = Target
End Sub
Function getOneRowOneCol(SourceRange As Range, _
ByVal NumberOfCells As Long) _
As Variant
Dim r As Long
r = SourceRange.Rows.Count
Dim c As Long
c = SourceRange.Columns.Count
Dim NoRs As Long
NoRs = r * (c - NumberOfCells + 1)
Dim NoCs As Long
NoCs = c * (r - NumberOfCells + 1)
Dim Data As Variant
ReDim Data(1 To NoRs + NoCs, 1 To 1)
Dim rng As Range
Dim i As Long
Dim j As Long
Dim k As Long
' Rows
For i = 1 To r
For j = 1 To c - NumberOfCells + 1
Set rng = SourceRange.Cells(i, j).Resize(, NumberOfCells)
k = k + 1
Data(k, 1) = rng.Address(0, 0)
Next j
Next i
' Columns
For j = 1 To c
For i = 1 To r - NumberOfCells + 1
Set rng = SourceRange.Cells(i, j).Resize(NumberOfCells)
k = k + 1
Data(k, 1) = rng.Address(0, 0)
Next i
Next j
getOneRowOneCol = Data
' Debug.Print UBound(Data)
' For i = 1 To UBound(Data)
' Debug.Print Data(i, 1)
' Next i
End Function