Invalid Qualifier error with Slope function - vba

New to VBA and got this qualifier error for a code that's supposed to calculate the slope of two arrays and place it in a column with the given address.
Beats me so far, so it'd be nice to have some help! The sizes of the arrays are matching.
The compile error is given at the .Slope within the TargetSheet.Cells(n, (MyRange.Columns.Count) + 1).Value = Application.WorksheetFunction.Slope(TargetSheet.Range(TargetSheet.Cells(n, 5), TargetSheet.Cells(n, MyRange.Columns.Count)), TargetSheet.Range(TargetSheet.Cells(1, 5), TargetSheet.Cells(1, MyRange.Columns.Count))).Value
Dim n As Long
Dim MyRange As Range
Set MyRange = ActiveSheet.UsedRange
Dim TargetSheet As Worksheet, SourceSheet As Worksheet
Dim TargetBook As Workbook
Set TargetBook = Application.ActiveWorkbook
Set TargetSheet = Application.ActiveSheet
For n = 3 To MyRange.Rows.Count
TargetSheet.Cells(n, (MyRange.Columns.Count) + 1).Value = Application.WorksheetFunction.Slope(TargetSheet.Range(TargetSheet.Cells(n, 5), TargetSheet.Cells(n, MyRange.Columns.Count)), TargetSheet.Range(TargetSheet.Cells(1, 5), TargetSheet.Cells(1, MyRange.Columns.Count))).Value
Next n
End With
End Sub
A version before this one (can't undo till that point :/) was working up till a point where it stopped and gave the qualifier error. I used Excel's SLOPE function on the data set and compared alongside: it was giving this error at a #DIV/0 result (but the VBA didn't show that).

As stated, removing .Value from the slope call allows it to pass the compiler.
Otherwise, it seems to work.
This input ...
Provided this output ...
Using this code (slightly modified to get the correct columns) ...
Sub main()
Dim n As Long
Dim MyRange As Range
Set MyRange = ActiveSheet.UsedRange
Dim TargetSheet As Worksheet, SourceSheet As Worksheet
Dim TargetBook As Workbook
Set TargetBook = Application.ActiveWorkbook
Set TargetSheet = Application.ActiveSheet
For n = 2 To MyRange.Rows.Count
TargetSheet.Cells(n, (MyRange.Columns.Count) + 1).Value = _
Application.WorksheetFunction.Slope(TargetSheet.Range(TargetSheet.Cells(n, 1), TargetSheet.Cells(n, MyRange.Columns.Count)), _
TargetSheet.Range(TargetSheet.Cells(1, 1), TargetSheet.Cells(1, MyRange.Columns.Count)))
Next n
End Sub
Below is revised code to capture case when only 1 Y value is present to avoid divide by zero. This assumes that row 1 in the worksheet will have nothing to the right of the headers.
Option Explicit
Sub main()
Dim n As Long
Dim MyRange As Range
Dim nRows As Long, nCols As Long, ColCount As Long
Set MyRange = ActiveSheet.UsedRange
Dim TargetSheet As Worksheet, SourceSheet As Worksheet
Dim TargetBook As Workbook
Set TargetBook = Application.ActiveWorkbook
Set TargetSheet = Application.ActiveSheet
nRows = MyRange.Rows.Count
nCols = MyRange.Rows(1).End(xlToRight)
For n = 2 To nRows
ColCount = Application.CountIf(TargetSheet.Range(TargetSheet.Cells(n, 1), Sheet1.Cells(n, nCols)), """<>""""""")
If ColCount > 1 Then
TargetSheet.Cells(n, (MyRange.Columns.Count) + 1).Value = _
Application.Slope(TargetSheet.Range(TargetSheet.Cells(n, 1), TargetSheet.Cells(n, MyRange.Columns.Count)), _
TargetSheet.Range(TargetSheet.Cells(1, 1), TargetSheet.Cells(1, MyRange.Columns.Count)))
End If
Next n
End Sub

Thanks #OldUgly! Managed to fix the error. Reason for VBA: automate for multiple data sheets in the same format with varying no. of columns and rows.
WorksheetFunction.Slope doesn't like it when there's only one data point and the rest of one array is empty.
I created another loop to count non-empty cells within the For loop for each row. If the no. of non-empty cells is < 2 for a particular row, the output would be "Insufficient Data".
If not, then the slope value would be calculated.
Another, less painstaking way to get around this issue is to simply use On Error Resume Next for this specific case.
Dim n As Long, o As Range, CurrentRow As Range, NonEmptyCellCountRow As Integer
For n = 3 To MyRange.Rows.Count `Within each row, counting non-empty cells
Set CurrentRow = TargetSheet.Range(TargetSheet.Cells(n, 5), TargetSheet.Cells(n, MyRange.Columns.Count))
NonEmptyCellCountRow = 0
For Each o In CurrentRow
If o.Value <> "" Then NonEmptyCellCountRow = NonEmptyCellCountRow + 1
Next o
If NonEmptyCellCountRow < 2 Then _
TargetSheet.Cells(n, (MyRange.Columns.Count) + 1) = "Insufficient Data"
Else
TargetSheet.Cells(n, (MyRange.Columns.Count) + 1) = Application.WorksheetFunction.Slope(TargetSheet.Range(TargetSheet.Cells(n, 5), TargetSheet.Cells(n, MyRange.Columns.Count)), TargetSheet.Range(TargetSheet.Cells(1, 5), TargetSheet.Cells(1, MyRange.Columns.Count)))
End If
Next n

Related

VBA finding value and put it in specific column

Hope you you can help me here. I have a repetitive task every week, which I could do the same way every single time through Excel formulas, but I am looking for a more automated way of going about this.
What I want to achieve is to set-up a dynamic range that will look for multiple key words such as in this case "OA" & "SNC" and if it matches it will return the value in the column G & H respectively. At the same time it has to skip blank rows. What is the best way to go about this?
I figured it shouldn't be too hard, but I cannot figure it out.
As per image above, I want to consolidate the charges per category (OA & SNC) in the designated columns ("G" & "H") on row level.
My approach to the task
Procedure finds data range, loops through it's values, adding unique values to the dictionary with sum for specific row and then loads all these values along with sums per row.
Option Explicit
Sub CountStuff()
Dim wb As Workbook, ws As Worksheet
Dim lColumn As Long, lRow As Long, lColTotal As Long
Dim i As Long, j As Long
Dim rngData As Range, iCell As Range
Dim dictVal As Object
Dim vArr(), vArrSub(), vArrEmpt()
'Your workbook
Set wb = ThisWorkbook
'Set wb = Workbooks("Workbook1")
'Your worksheet
Set ws = ActiveSheet
'Set ws = wb.Worksheets("Sheet1")
'Number of the first data range column
lColumn = ws.Rows(1).Find("1", , xlValues, xlWhole).Column
'Number of the last row of data range
lRow = ws.Cells(ws.Rows.Count, lColumn).End(xlUp).Row
'Total number of data range columns
lColTotal = ws.Cells(1, lColumn).End(xlToRight).Column - lColumn + 1
'Data range itself
Set rngData = ws.Cells(1, lColumn).Resize(lRow, lColTotal)
'Creating a dictionary
Set dictVal = CreateObject("Scripting.Dictionary")
'Data values -> array
vArr = rngData.Offset(1, 0).Resize(rngData.Rows.Count - 1, _
rngData.Columns.Count).Value
'Empty array
ReDim vArrEmpt(1 To UBound(vArr, 1))
'Loop through all values
For i = LBound(vArr, 1) To UBound(vArr, 1)
For j = LBound(vArr, 2) To UBound(vArr, 2)
'Value is not numeric and is not in dictionary
If Not IsNumeric(vArr(i, j)) And _
Not dictVal.Exists(vArr(i, j)) Then
'Add value to dictionary
dictVal.Add vArr(i, j), vArrEmpt
vArrSub = dictVal(vArr(i, j))
vArrSub(i) = vArr(i, j - 1)
dictVal(vArr(i, j)) = vArrSub
'Value is not numeric but already exists
ElseIf dictVal.Exists(vArr(i, j)) Then
vArrSub = dictVal(vArr(i, j))
vArrSub(i) = vArrSub(i) + vArr(i, j - 1)
dictVal(vArr(i, j)) = vArrSub
End If
Next j
Next i
'Define new range for results
Set rngData = ws.Cells(1, lColumn + lColTotal - 1). _
Offset(0, 2).Resize(1, dictVal.Count)
'Load results
rngData.Value = dictVal.Keys
For Each iCell In rngData.Cells
iCell.Offset(1, 0).Resize(lRow - 1).Value _
= Application.Transpose(dictVal(iCell.Value))
Next
End Sub
I've used a simple custom function, possibly overkill as this could be done with worksheet formulae, but given that your ranges can vary in either direction...
Function altsum(r As Range, v As Variant) As Variant
Dim c As Long
For c = 2 To r.Columns.Count Step 2
If r.Cells(c) = v Then altsum = altsum + r.Cells(c - 1)
Next c
If altsum = 0 Then altsum = vbNullString
End Function
Example below, copy and formula in F2 across and down (or apply it one go with another bit of code).

copy a row of varied length, transpose it, and paste at the end of a column

I am working on a macro to copy a varied number of cells to a row, transpose and paste into a different sheet, in the next empty cell in a column. Then the idea is to match each transposed item with the ID from the row it originated from. The number of rows in the ID column will vary as well.
Looking at the example below, ID 1 is associated with Co D and Co R. Transposing would create the need for ID 1 to be copied into the two cells adjacent to the destination. This example I created has them on the same sheet, but for the code itself it will be on a different sheet.
The problem appears in copying the range to be transposed. I can't seem to figure out how to grab the whole row. The macro correctly pastes the value in the next available cell in the destination, but the version of the code I have now only copies the last result in the row, and not the whole row which is my intent. I haven't even gotten to the part of matching the ID to the Co in the Destination column, but I am dreading it already. The code I have is as follows;
Sub Testing()
Dim TearS As Worksheet: Set TearS = Worksheets(1)
Dim FeeS As Worksheet: Set FeeS = Worksheets(2)
Dim EntryS As Worksheet: Set EntryS = Worksheets(3)
Dim Stage2 As Worksheet: Set Stage2 = Worksheets(4)
Dim Stage3 As Worksheet: Set Stage3 = Worksheets(5)
Dim Bbg As Range: Set Bbg = EntryS.Range("F4:T199")
Dim TDest As Range: Set TDest = Stage2.Range("F5:T200")
Dim DateA As Range: Set DateA = Stage2.Range("G5:G200")
Dim DateB As Range: Set DateB = TearS.Range("E5:E200")
Dim DesA As Range: Set DesA = Stage2.Range("J5:J200")
Dim DesB As Range: Set DesB = TearS.Range("O5:O200")
Dim DesC As Range: Set DesC = Stage3.Range("C5:C200")
Dim CpnMatA As Range: Set CpnMatA = Stage2.Range("Y5:Y200")
Dim CpnMatB As Range: Set CpnMatB = TearS.Range("P5:P500")
Dim SettA As Range: Set SettA = Stage2.Range("I5:I200")
Dim SettB As Range: Set SettB = TearS.Range("Q5:Q200")
Dim MinA As Range: Set MinA = Stage2.Range("AA5:AA200")
Dim MinB As Range: Set MinB = Stage3.Range("D5:D200")
Dim MWOB As Range: Set MWOB = TearS.Range("N5:N200")
Dim Cel As Range
For Each Cel In DesC
If IsEmpty(Cel) = False Then
Cel.Offset(0, 1).End(xlToRight).Copy
TearS.Range("N3").End(xlDown).Offset(1).PasteSpecial Paste:=xlPasteAll, _
Operation:=xlNone, SkipBlanks:=False, Transpose:=True
End If
Next Cel
End Sub
Edit: Jeeped's solution that you can see in the answer below works swimmingly. Make sure that there are no errors in the source data, or you may get a run-time error 13.
Try transposing within a 2-D array before passing the values back to the worksheet.
Sub rewrite()
Dim lr As Long, a As Long, b As Long, val As Variant, vals As Variant
With Worksheets("sheet6")
.Range("F:G").Clear
lr = Application.Max(.Cells(.Rows.Count, "B").End(xlUp).Row, _
.Cells(.Rows.Count, "C").End(xlUp).Row, _
.Cells(.Rows.Count, "D").End(xlUp).Row, _
.Cells(.Rows.Count, "E").End(xlUp).Row)
vals = .Range(.Cells(2, "A"), .Cells(lr, "E")).Value2
For a = LBound(vals, 1) To UBound(vals, 1)
ReDim val(1 To UBound(vals, 2), 1 To 2)
For b = LBound(val, 1) To UBound(val, 1) - 1
If CBool(Len(vals(a, b + 1))) Then
val(b, 1) = vals(a, 1)
val(b, 2) = vals(a, b + 1)
End If
Next b
.Cells(.Rows.Count, "F").End(xlUp).Offset(1, 0).Resize(UBound(val, 1), UBound(val, 2)) = val
Next a
End With
End Sub

Trying to copy and paste filtered data over using best practices

I'm writing a very simple bit of code to move data from one workbook to another. I'm trying to avoid using select and copy-paste, since it's widely considered to not be optimal. Ok, challenge accepted. I've gotten just about everything written, and I've suddenly realized - I don't know how to define a range of filtered data as a range, ignoring the parts that are filtered out. I've done some searching, but I'm not quite there. Current code as follows:
Sub CSReport()
Dim CabReport As Workbook
Dim ExCashArchive As Workbook
Dim CABReconFilePath As String
Dim ExCashPath As String
Dim HoldingsTabName As String
Dim IMSHoldingsTabName As String
Dim HoldingsTab As Worksheet
Dim IMSHoldingsTab As Worksheet
Dim LastRowHoldings As Integer
Dim LastRowIMSHoldings As Integer
Dim RngHoldings As Range
Dim RngIMS As Range
Dim dt As Date
dt = Range("Today")
'Today is a named range with the date, just incase I need to be manually changing it
CABReconFilePath = Range("CABReconFilePath")
ExCashPath = Range("ExcessCashArchiveFilePath")
'What are the files we care about
HoldingsTabName = Range("HoldingTieOutTabName")
IMSHoldingsTabName = Range("IMSHoldingsTabName")
'What are the tab names we care about
Workbooks.Open Filename:=CABReconFilePath
Set CabReport = ActiveWorkbook
Workbooks.Open Filename:=ExCashPath
Set ExCashArchive = ActiveWorkbook
'Opening and defining the workbooks we're dealing with
HoldingsTab = ExCashArchive.Sheets(HoldingsTabName)
IMSHoldingsTab = ExCashArchive.Sheets(IMSHoldingsTabName)
'Defining the tabs
LastRowHoldings = HoldingsTab.Range("A" & Rows.Count).End(xlUp).Row
LastRowIMSHoldings = IMSHoldingsTab.Range("A" & Rows.Count).End(xlUp).Row
'Defining the edges of the data
'Filter goes here
RngHoldings = HoldingsTab.Range("A3:K" & LastRowHoldings)
RngIMS = IMSHoldingsTab.Range("A3:P" & LastRowIMSHoldings)
'Or maybe it goes here?
CABReconFilePath.Sheets("Holdings_TieOut").Range("A3").Resize(CopyFrom.Rows.Count).Value = RngHoldings.Value
CABReconFilePath.Sheets("IMS_Holdings").Range("A3").Resize(CopyFrom.Rows.Count).Value = RngIMS.Value
'Getting the values in
CABReconFilePath.Sheets("Recon Summary").Range("B1").Value = Text(dt, "MM/DD/YYYY")
'And setting the date manually, just incase we're running prior/future reports
ExCashArchive.Close savechanges:=False
CabReport.SaveAs Filename = CABReconFilePath & Text(dt, "MM.DD.YY")
CabReport.Close
End Sub
Now, what I've previously done is fairly clumsy things like:
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.AutoFilter
ActiveSheet.Range("$A$1:$W$71").AutoFilter Field:=1, Criteria1:="=*1470*", Operator:=xlFilterValues
Selection.Copy
CABReconFilePath.Sheets("CS").Range("C1").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
This has been my method until now of "Filter data, copy it, paste it somewhere else" - but I'm trying to learn better programming methods, and I keep hearing about "Don't use select" and "Try to avoid copy-pasting - move stuff into a range and use that instead!". But I'm stuck at this point.
Edit: .SpecialCells(xlCellTypeVisible) is the qualifier I needed to add.
Sub CopyFilterRange()
Dim i As Long
Dim j As Long
Dim lRow As Long
Dim cnt As Long
Dim UB1 As Long
Dim UB2 As Long
Dim rng1 As Range
Dim rng2 As Range
Dim arr1() As Variant
Dim arr2() As Variant
Dim WS1 As Worksheet
Dim WS2 As Worksheet
Set WS1 = ThisWorkbook.Sheets("Sheet1")
Set WS2 = ThisWorkbook.Sheets("Sheet2") 'this can be a different sheet in a different workbook
'Find last row in column A
With WS1
lRow = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
'Define range
Set rng1 = WS1.Range("A1:A" & lRow)
'Define array out of range
arr1 = rng1
'Redim array 2 rows based on the columns of array 1
'We will define it with one column and rows equal to the same number of columns in array 1
'The reason is that in arrays only the last index can be flexible and the other indices should stay fixed
UB1 = UBound(arr1, 1)
UB2 = UBound(arr1, 2)
ReDim arr2(1 To UB2, 1 To 1)
'Loop throug arr1 and filter
cnt = 0
For i = 1 To UB1
For j = 1 To UB2
If arr1(i, j) = "A" Or arr1(i, j) = "B" Then
cnt = cnt + 1
ReDim Preserve arr2(1 To UB2, 1 To cnt) 'here we can add one column to array while preserving the data
bResizeArray = False 'resizing array should happen only once in the inner loop
arr2(j, cnt) = arr1(i, j)
End If
Next j
Next i
'Transpose arr2
arr2 = TransposeArray(arr2)
'Paste arr2 value in the destination range
'Define the size of destination range
Set rng2 = WS2.Range("A1")
Set rng2 = rng2.Resize(UBound(arr2, 1), UBound(arr2, 2))
rng2.Value = arr2
End Sub
Public Function TransposeArray(myarray As Variant) As Variant
Dim X As Long
Dim Y As Long
Dim Xupper As Long
Dim Yupper As Long
Dim tempArray As Variant
Xupper = UBound(myarray, 2)
Yupper = UBound(myarray, 1)
ReDim tempArray(1 To Xupper, 1 To Yupper)
For X = 1 To Xupper
For Y = 1 To Yupper
tempArray(X, Y) = myarray(Y, X)
Next Y
Next X
TransposeArray = tempArray
End Function

Excel vba copying and pasting based on cell value code not working

So im pretty new to VBA coding, and I'm trying to setup a commandbutton that when activated copys everything from sheet "Opties" to the sheet "BOM" from the column D and F that has a 1 in column with the titel "Totaal" and remove blanks.
So far this is my code
Sub Copy()
Dim c As Integer
Dim x As Integer
Dim y As Integer
Dim ws1 As Worksheet: Set ws1 = Sheets("Optie")
Dim ws2 As Worksheet: Set ws2 = Sheets("BOM")
Dim colNum As Integer
colNum = Worksheetfuntion.Match("Totaal", ws1.Range("A1:ZZ1"), 0)
c = 1
x = -4 + colNum
y = -6 + colNum
Set rng1 = ws1.Column(colNum)
Set rng2 = ws2.Range("C5:C25000")
For Each c In ws1.rng1
rng1.Offset(0, x).Copy
rng2.Offset(0, 1).PasteSpecial xlPasteValues
rng1.Offset(0, y).Copy
rng2.Offset(0, 2).PasteSpecial xlPasteValues
Next c
End Sub
I think the following code should work:
'Try to avoid using names that Excel uses - you will sometimes "block" the
'native function, so call the subroutine "myCopy" or something else, but
'preferably not "Copy"
Sub myCopy()
Dim r1 As Long ' Use Long rather than Integer, because Excel
Dim r2 As Long ' now allows for more than 65536 rows
'Use Worksheets collection for worksheets, and only use the Sheets
' collection if you need to process Charts as well as Worksheets
Dim ws1 As Worksheet: Set ws1 = Worksheets("Optie") 'Should this be "Opties"?
Dim ws2 As Worksheet: Set ws2 = Worksheets("BOM")
Dim colNum As Long
colNum = WorksheetFunction.Match("Totaal", ws1.Range("A1:ZZ1"), 0)
r2 = 5 ' I guessed that 5 is the first row you want to write to
'Loop through every row until the last non-empty row in Totaal column
For r1 = 1 To ws1.Cells(ws1.Rows.Count, colNum).End(xlUp).Row
'See if value in Totaal column is 1
If ws1.Cells(r1, colNum).Value = 1 Then
'I have guessed that your destination columns are D & E based on
'your Offset(0, 1) and Offset(0, 2) from column C
'I have guessed that your source columns are F & D based on the
'question mentioning those columns, and the offsets of -4 and -6
'in your current code - I assume based on "Totaal" being column J
'Change my guesses as necessary
'Copy values to destination from source
ws2.Cells(r2, "D").Value = ws1.Cells(r1, "F").Value
ws2.Cells(r2, "E").Value = ws1.Cells(r1, "D").Value
'Increment row counter for destination sheet
r2 = r2 + 1
End If
Next
End Sub

Creating new column and insert value base on another column

I need help.
Currently I need to optimize the way i code and the process. Is there an alternative way for me to do this? The only way to differeniate the value is by the the first to digits. And there are hundred over values. As you can see in the codes, 99 will be assign value of 1042, 95 will be assign 261 and this goes on. How do I make it easier so that I have to input the values manually.Thanks in advance guys
Sub Netting()
Dim Found As Range
Dim LR As Long
Dim ws As Worksheet
Dim cell As Range
Dim a As Variant, v As Variant
Set ws = Sheets("PAYABLES - OUTFLOWS")
Set Found = ws.Rows(1).Find(What:="Invoice amount", _
LookIn:=xlValues, lookat:=xlWhole)
If Found Is Nothing Then Exit Sub
a = [{"HI99162152",1042;"HI99162159",1042;"99162161",1042;"HI95400159",261; "HI95400164", 261; "HI97500493",3004;"HI97500497", 3004 }] 'create 2-d lookup array
LR = ws.Cells(ws.Rows.Count, Found.Column).End(xlUp).Row
Found.Offset(0, 1).EntireColumn.Insert
ws.Cells(1, Found.Column + 1).Value = "Netting"
For Each cell In ws.Range(ws.Range("C2"), ws.Cells(LR, 3))
v = Application.VLookup(cell.Value, a, 2, False)
cell.EntireRow.Cells(Found.Column + 1).Value = IIf(IsError(v), "", v)
Next cell
End Sub
Sub Netting()
Dim Found As Range
Dim LR As Long
Dim ws As Worksheet
Dim cell As Range
Dim a As Variant, v As Variant, num
Set ws = Sheets("PAYABLES - OUTFLOWS")
Set Found = ws.Rows(1).Find(What:="Invoice amount", _
LookIn:=xlValues, lookat:=xlWhole)
If Found Is Nothing Then Exit Sub
a = [{"99",1042;"95",261;"97",3004}] 'create 2-d lookup array
LR = ws.Cells(ws.Rows.Count, Found.Column).End(xlUp).Row
Found.Offset(0, 1).EntireColumn.Insert
ws.Cells(1, Found.Column + 1).Value = "Netting"
For Each cell In ws.Range(ws.Range("C2"), ws.Cells(LR, 3))
num = Cstr(Mid(cell.Value, 3, 2))
v = Application.VLookup(num, a, 2, False)
cell.EntireRow.Cells(Found.Column + 1).Value = IIf(IsError(v), "", v)
Next cell
End Sub
If there are a large number of values in your array then it might be easier to manage that as a table on a worksheet.