Greetings:
Looking for some help in adding some functionality to my worksheet (and possibly making it more efficient). I've cobbled together these sort algorithms from various authors and am struggling with adapting them for 2D arrays.
End goal is to explain year-over-year changes for specific financial statement lines by pulling in project details from various reports. Each year will have different projects driving the changes, and the changes could be positive or negative. I currently generate a 1D array of numerical changes from the detail report, sort it from high to low, loop through first 10 items in array to print to summary sheet, then add an INDEX MATCH on the numerical change to pull in the project name. I realize this will generate an error if two projects have the same change, which is why I want to update this to a 2D array.
I currently can print my array in either ascending or descending order, but I would like to see the top ten largest ABSOLUTE changes (while still displaying the original sign of the change--positive or negative). I feel like it would be easy to populate my array with only absolute values, but then sorting and printing the top values would not make logical sense.
Features to add in order of priority:
Current summary - can only sort asc or desc
Goal: sorted by largest absolute change but still retains original sign
Looking for some help in adding some functionality to my worksheet (and possibly making it more efficient). I've cobbled together these sort algorithms from various authors and am struggling with adapting them for 2D arrays.
End goal is to explain year-over-year changes for specific financial statement lines by pulling in project details from various reports. Each year will have different projects driving the changes, and the changes could be positive or negative. I currently generate a 1D array of numerical changes from the detail report, sort it from high to low, loop through first 10 items in array to print to summary sheet, then add an INDEX MATCH on the numerical change to pull in the project name. I realize this will generate an error if two projects have the same change, which is why I want to update this to a 2D array.
I currently can print my array in either ascending or descending order, but I would like to see the top ten largest ABSOLUTE changes (while still displaying the original sign of the change--positive or negative). I feel like it would be easy to populate my array with only absolute values, but then sorting and printing the top values would not make logical sense.
Features to add in order of priority:
- function to return array of 10 largest absolute values while maintaining sign
- 2D array of changes where "column" 1 is the project and "column" 2 is the change
- 2D array sort algorithms to sort change "column" as described above
VBA Code:
Sub TestPrintArray()
Application.DisplayAlerts = False
Application.ScreenUpdating = False
Application.Calculation = xlManual
Dim wsS15fx As Worksheet
Set wsS15fx = Worksheets("XXXXXXXXXXXXXXXX")
Dim wsReconfx As Worksheet
Set wsReconfx = Worksheets("YYYYYYYYYYYYY")
Dim arTestArray() As Variant
Dim i As Integer
arTestArray = GetLargestAbsoluteChanges(wsS15fx.Range("$J$548:$J$5000"), _
wsReconfx.Range("B1"), wsReconfx.Range("B4"))
For i = LBound(arTestArray) To 9
wsReconfx.Range("G" & (i + 30)) = arTestArray(i)
wsReconfx.Range("G" & (i + 30)).Offset(0, -1).Formula = "=INDEX('S15 - Depr by Plant Acct'!$D:$D,MATCH(G" & _
(i + 30) & ",'S15 - Depr by Plant Acct'!$J:$J,0))"
wsReconfx.Range("G" & (i + 30)).Offset(0, -1).Copy
wsReconfx.Range("G" & (i + 30)).Offset(0, -1).PasteSpecial Paste:=xlPasteValues
wsReconfx.Range("G" & (i + 30)).Value = wsReconfx.Range("G" & (i + 30)) / 1000
Next
arTestArray = GetLargestAbsoluteChanges(wsS15fx.Range("$K$548:$K$5000"), _
wsReconfx.Range("B1"), wsReconfx.Range("B4"))
For i = LBound(arTestArray) To 9
wsReconfx.Range("I" & (i + 30)) = arTestArray(i)
wsReconfx.Range("I" & (i + 30)).Offset(0, -1).Formula = "=INDEX('S15 - Depr by Plant Acct'!$D:$D,MATCH(I" & _
(i + 30) & ",'S15 - Depr by Plant Acct'!$K:$K,0))"
wsReconfx.Range("I" & (i + 30)).Offset(0, -1).Copy
wsReconfx.Range("I" & (i + 30)).Offset(0, -1).PasteSpecial Paste:=xlPasteValues
wsReconfx.Range("I" & (i + 30)).Value = wsReconfx.Range("I" & (i + 30)) / 1000
Next
arTestArray = GetLargestAbsoluteChanges(wsS15fx.Range("$L$548:$L$5000"), _
wsReconfx.Range("B1"), wsReconfx.Range("B4"))
For i = LBound(arTestArray) To 9
wsReconfx.Range("K" & (i + 30)) = arTestArray(i)
wsReconfx.Range("K" & (i + 30)).Offset(0, -1).Formula = "=INDEX('S15 - Depr by Plant Acct'!$D:$D,MATCH(K" & _
(i + 30) & ",'S15 - Depr by Plant Acct'!$L:$L,0))"
wsReconfx.Range("K" & (i + 30)).Offset(0, -1).Copy
wsReconfx.Range("K" & (i + 30)).Offset(0, -1).PasteSpecial Paste:=xlPasteValues
wsReconfx.Range("K" & (i + 30)).Value = wsReconfx.Range("K" & (i + 30)) / 1000
Next
arTestArray = GetLargestAbsoluteChanges(wsS15fx.Range("$M$548:$M$5000"), _
wsReconfx.Range("B1"), wsReconfx.Range("B4"))
For i = LBound(arTestArray) To 9
wsReconfx.Range("M" & (i + 30)) = arTestArray(i)
wsReconfx.Range("M" & (i + 30)).Offset(0, -1).Formula = "=INDEX('S15 - Depr by Plant Acct'!$D:$D,MATCH(M" & _
(i + 30) & ",'S15 - Depr by Plant Acct'!$M:$M,0))"
wsReconfx.Range("M" & (i + 30)).Offset(0, -1).Copy
wsReconfx.Range("M" & (i + 30)).Offset(0, -1).PasteSpecial Paste:=xlPasteValues
wsReconfx.Range("M" & (i + 30)).Value = wsReconfx.Range("M" & (i + 30)) / 1000
Next
arTestArray = GetLargestAbsoluteChanges_LTH(wsS15fx.Range("$N$548:$N$5000"), _
wsReconfx.Range("B1"), wsReconfx.Range("B4"))
For i = LBound(arTestArray) To 9
wsReconfx.Range("O" & (i + 30)) = arTestArray(i)
wsReconfx.Range("O" & (i + 30)).Offset(0, -1).Formula = "=INDEX('S15 - Depr by Plant Acct'!$D:$D,MATCH(O" & _
(i + 30) & ",'S15 - Depr by Plant Acct'!$N:$N,0))"
wsReconfx.Range("O" & (i + 30)).Offset(0, -1).Copy
wsReconfx.Range("O" & (i + 30)).Offset(0, -1).PasteSpecial Paste:=xlPasteValues
wsReconfx.Range("O" & (i + 30)).Value = wsReconfx.Range("O" & (i + 30)) / 1000
Next
Application.DisplayAlerts = True
Application.ScreenUpdating = True
Application.Calculation = xlAutomatic
End Sub
Public Function GetLargestAbsoluteChanges(ByRef Data As Range, InputScenario As Range, InputUtility As Range) As Variant()
Dim cell As Range
Dim wsS15fx As Worksheet
Set wsS15fx = Worksheets("S15 - Depr by Plant Acct")
Dim arLargestChanges() As Variant
ReDim arLargestChanges(0)
For Each cell In Data
If Not IsEmpty(cell) And cell.Value <> 0 Then 'And (cell.Value > 400000 Or cell.Value < -400000)
If wsS15fx.Range("E" & cell.Row).Value = InputScenario.Value And _
wsS15fx.Range("C" & cell.Row).Value = InputUtility.Value Then
If IsEmpty(arLargestChanges(LBound(arLargestChanges))) Then
arLargestChanges(LBound(arLargestChanges)) = cell.Value
Else
ReDim Preserve arLargestChanges(UBound(arLargestChanges) + 1)
arLargestChanges(UBound(arLargestChanges)) = cell.Value
End If
End If
End If
Next
Call QuickSort(arLargestChanges, 0, UBound(arLargestChanges))
Call ReverseArray(arLargestChanges)
GetLargestAbsoluteChanges = arLargestChanges
End Function
Public Function GetLargestAbsoluteChanges_LTH(ByRef Data As Range, InputScenario As Range, InputUtility As Range) As Variant()
Dim cell As Range
Dim wsS15fx As Worksheet
Set wsS15fx = Worksheets("S15 - Depr by Plant Acct")
Dim arLargestChanges() As Variant
ReDim arLargestChanges(0)
For Each cell In Data
If Not IsEmpty(cell) And cell.Value <> 0 Then 'And (cell.Value > 400000 Or cell.Value < -400000)
If wsS15fx.Range("E" & cell.Row).Value = InputScenario.Value And _
wsS15fx.Range("C" & cell.Row).Value = InputUtility.Value Then
If IsEmpty(arLargestChanges(LBound(arLargestChanges))) Then
arLargestChanges(LBound(arLargestChanges)) = cell.Value
Else
ReDim Preserve arLargestChanges(UBound(arLargestChanges) + 1)
arLargestChanges(UBound(arLargestChanges)) = cell.Value
End If
End If
End If
Next
Call QuickSort(arLargestChanges, 0, UBound(arLargestChanges))
'Call ReverseArray(arLargestChanges)
GetLargestAbsoluteChanges_LTH = arLargestChanges
End Function
Public Sub QuickSort(vArray As Variant, inLow As Long, inHi As Long)
Dim pivot As Variant
Dim tmpSwap As Variant
Dim tmpLow As Long
Dim tmpHi As Long
tmpLow = inLow
tmpHi = inHi
pivot = vArray((inLow + inHi) \ 2)
While (tmpLow <= tmpHi)
While (vArray(tmpLow) < pivot And tmpLow < inHi)
tmpLow = tmpLow + 1
Wend
While (pivot < vArray(tmpHi) And tmpHi > inLow)
tmpHi = tmpHi - 1
Wend
If (tmpLow <= tmpHi) Then
tmpSwap = vArray(tmpLow)
vArray(tmpLow) = vArray(tmpHi)
vArray(tmpHi) = tmpSwap
tmpLow = tmpLow + 1
tmpHi = tmpHi - 1
End If
Wend
If (inLow < tmpHi) Then QuickSort vArray, inLow, tmpHi
If (tmpLow < inHi) Then QuickSort vArray, tmpLow, inHi
End Sub
Public Sub ReverseArray(vArray As Variant)
'Reverse the order of an array, so if it's already sorted from smallest to largest, it will now be sorted from largest to smallest.
Dim vTemp As Variant
Dim i As Long
Dim iUpper As Long
Dim iMidPt As Long
iUpper = UBound(vArray)
iMidPt = (UBound(vArray) - LBound(vArray)) \ 2 + LBound(vArray)
For i = LBound(vArray) To iMidPt
vTemp = vArray(iUpper)
vArray(iUpper) = vArray(i)
vArray(i) = vTemp
iUpper = iUpper - 1
Next i
End Sub
Current summary - can only sort asc or desc
Goal: sorted by largest absolute change but still retains original sign