Type mismatch when passing an array - vba

I keep on getting an error when I try and pass this value to an array, essentially I am working with exchange rates, I have the currency in the second dimension of the array ary() and I am looking to retrieve a data point in a table that is 2 cells away from the element stored in the array.
Now I believe the problem has something to do with the value being a number and the array being a variant, however I cannot solve the problem.
Option Explicit
Dim fxary(), y
ws.Select
Set Rng = Sheets("BEXR").Range("C2:C" & Sheets("BEXR").Range("A1").End(xlDown).Row)
For i = 1 To UBound(ary, 2)
If ary(2, i) <> ary(2, UBound(ary, 2)) Then
For Each y In Rng
If Sheets("BEXR").Range(y.Address).Value = ary(2, UBound(ary, 2)) And Sheets("BEXR").Range(y.Address).Offset(0, 1).Value = ary(2, i) Then
fxary() = Sheets("BEXR").Range(y.Address).Offset(0, 2).Value ' error occurring here
fxary = ary(1, i)
End If
Next y
End If
Next

Your comments say that I have omitted all the lines not related to this specific problem, therefore I will post an answer based on the assumption that all the lines that are related to the problem have been included in the code in the question.
The following comments should be made about your code:
You have used Option Explicit but nowhere do you declare the size or type of the variables i and ary.
Nowhere do you specify values for the variable ary.
Your line saying fxary() = Sheets("BEXR").Range(y.Address).Offset(0, 2).Value is trying to assign a scalar to a vector, and will generate a Type Mismatch error.
Your line saying fxary() = Sheets("BEXR").Range(y.Address).Offset(0, 2).Value is immediately followed by a line (fxary = ary(1, i)) replacing the value of fxary with something else, so the first line is redundant.
All your references to Sheets("BEXR").Range(y.Address) can be replaced with y.
You are executing through two loops (from 1 to whatever the second dimension of the undefined variable ary is and, within that loop, through every cell in column C of your sheet), but you are effectively only setting fxary once, i.e. to the value in the undefined variable ary where the first dimension's value is 1 and the second dimension's value is i. (But I haven't been able to wrap my mind around the code enough to be able to work out which value of i that will end up being - I think it's worked out by looking at each row of your sheet that has a different value in column C than in column D, and the value in column C is equal to ary(2, highest value), and then determining the highest value of i such that ary(2, i) will equal the value in column D.)
I believe you need to look at each of the above items before your code will do anything meaningful, but to resolve the specific problem you say you are having:
1) Change your declaration of fxary to
Dim fxary, y
2) Simply remove the line saying
fxary() = Sheets("BEXR").Range(y.Address).Offset(0, 2).Value
as that line is redundant (as I said in one of my earlier points).

Related

VBA How to Check if Cell Value is valid and exists?

I have this cell.Offset(0, -2) cell that I need to check if it exists inside my VBA loop.
My idea is that if the cell has an .Offset(0, -2) that does not exist (e.g. say cell = Column B and cell.Offset(0, -2) = Column A-1, cell.Offset(0, -2) is supposed to be invalid), I want to be able to check that it doesn't exist in the if else statement.
Right now when it doesn't exist, I'm getting a “Runtime Error 1004: Application defined or object defined error”.
I've tried IsErr(cell.Offset(0, -2)), cell.Offset(0, -2).Value, If cell.Offset(0, -2) Is Nothing Then, cell.Offset(0, -2) = "", but none of them seem to work... can anyone point me in the right direction? I'm pretty new to VBA and it seems like different variable types have different ways of checking if the value exists.
If you can use your offset amount as a variable, or some method to evaluate against the cell's column, you don't need to defend against errors. Consider below...
Sub IsvalidARea()
Dim OffsetAmount As Integer: OffsetAmount = -2
Dim Cell As Range
If Cell.Column + OffsetAmount < 1 Then
MsgBox "Oh FALSE!"
Else
'EVERYTHING IS OKAY!
End If
End Sub
A solution for this is by using the constantly misused On Error Resume Next, coupled with a If IsError. Here's an example:
On Error Resume Next
If IsError(ActiveCell.Offset(, -2).Select) = True Then
'This is indicative of an error. Meaning you are on Column B, and you ran out of space.
Else
'This means an error did not occur. Meaning you were at least on Column C or above.
End If
There may be a better solution, in fact I am sure there is. But what this will do is allow you to move past the actual 1004 Application error, and you can actually identify, and use the error that was really returned, instead of just forcing your macro/script to end.
To avoid using the error check, you can always check the current column index (B is 2) and make sure whatever you are offsetting by, once subtracted by your index is greater than 0. For example, column B is 2. Your offset is 2. 2 - 2 = 0, so it should not attempt it. Column C is 3, 3 - 2 = 1, which is greater than 0 so it would be valid. Both are explorable methods.

Paste to next empty column

I already have this code that paste to the next empty row. It works perfectly but now I have a new VBA project that i would like to do the same thing but columns instead of rows
Current Code:
intLast = shtAlpha.Cells(Rows.Count, "C").End(xlUp)
intNext = intLast + 5 - (intLast + 1) Mod 5
Set rngDest = shtAlpha.Cells(intNext, "C").Resize(5, 3)
I tried this but I got Compile Error Method or data member not found, It highlights
.Cells
/
intLast = Omega.Cells(Columns.Count, "1").End(xlRight).Column
intNext = intLast + 5 - (intLast + 5) Mod 5
Set rngDest = Omega.Cells(intNext, "A").Resize(30000, 3)
To get the last column in row 1 with anything in it you should be doing
intLast = Omega.Cells(1,Omega.Columns.Count).End(xlToLeft).Column
intNext = intLast + 5 - (intLast + 5) Mod 5
Set rngDest = Omega.Cells(1,intNext).Resize(30000, 3)
Let's go through the changes one at a time:
Cells takes two parameters. The first indicates the row the second indicates the column. You want to start on the last column of the first row. So the first parameter should be 1. The second should be the number of columns in your worksheet.
Note that it is generally a good practice to specify which worksheet/workbook you are working on. This helps prevent errors if there are multiple books open or the wrong worksheet is selected.
.End will take you to the boundary of the region you are in. Since you are at the last cell on the first row you are at the far-right of the sheet. So you have tomove to the left to find the last column with values in it. hence .End(xlToLeft)
Note The documentation tells us that the parameter must be one of these values.
Now for your error message. You can often refer to this handy VBA error list for more info on what could be causing your error. I reccomend searching the error number if it is given in the error alert.
It sounds like your error is caused because Omega is not the correct type. VBA is telling you that you are trying to access a member called Cells but it cannot find it in the object. I suggest adding Omega to the watch and look at its members when the error occurs. With a little debugging you should be able to find the source of the problem!
Hopefully this helps :)
The Range() function accepts only cell references as text Strings e.g. Range("A1").
The Cells() function accepts only numeric row/column references e.g. Cells(1, 1).
In this case, you'd need to change
intLast = shtAlpha.Cells(Rows.Count, "C").End(xlUp)
to
intLast = shtAlpha.Cells(Rows.Count, 3).End(xlUp)

Subtract two columns, with each other as a condition, and append values

I need to subtract two columns from a large array and see which ones are positive and of those positive values I need to find the positive values row and append a few things onto that value.
Here is the general concept I'm thinking so far
While < 8000
if (cell(i,1).Value - cell(i,2) > 0)
print in another sheet cell(i,3).value (cell(i,2).Value-cell(i,4)) cell.value(i,4)
for example...
suppose I have something like this
[2 2 hi yo]
[3 2 go mo]
this macro would return "go 1 mo" in another sheet.
Sub Leaves()
Dim i As Integer
Dim g As Integer
Dim Quantity As Integer
Dim Executed As Integer
Dim Leaves As Integer
i = 1
g = 1
Do While i < 8000
Quantity = Worksheets("Sheet1").Cells(i, 3).value
Executed = Worksheets("Sheet1").Cells(i, 5).value
Leaves = Quantity - Executed
If Leaves > 0 Then
Worksheets("Sheet2").Cells(g, 1).value = _
Worksheets("Sheet1").Cells(i, 9).value & _
Worksheets("Sheet2").Cells(i, 2).value & _
Leaves & Worksheets("Sheet2").Cells(i, 3).value
g = g + 1
End If
i = i + 1
Loop
End Sub
The above code gives me a Type mismatch error.
It is helpful if you say what line is throwing an error. Also, one should strive to create a Minimal, complete, and verifiable example. The effort to do so often resolves the question before you need to post it.
The code itself seems fine and it runs for me (on an empty workbook) with no type mismatch. Thus, the problem must be with your assumptions about the spreadsheet.
Either of the lines
Quantity = Worksheets("Sheet1").Cells(i, 3).value
Executed = Worksheets("Sheet1").Cells(i, 5).value
will trigger a type mismatch if the corresponding value can't be converted to an integer. This could happen, for example, if one of the cells contains a string (other than something like "1") or an error value such as #N/A or #Value!.
The line which begins
Worksheets("Sheet2").Cells(g, 1).value = _
will throw a type mismatch if one of the values being concatenated can't be converted to a string. An error value in one of the cells is the most likely culprit. If this is the case and for some reason you actually want to create a string that includes substrings which look like e.g. "#N/A" then you could use the Text property of those cells rather than Value.

Comparing cells using match in vba

I am trying to compare two columns of cells in vba using the match function. I am comparing the second column against values in the first and copying and pasting the values that aren't found to another column. I am doing the same with the second against the first as well. For some reason when I try to run this it says that it cannot get the match property of the worksheet function class and I am really confused as to why I am receiving this error. I have pasted the code below. Any help with this issue would be greatly appreciated.
Set PrevMonth = Sheet13.Range(Range("A2"), Range("A2").End(xlDown))
Set currMonth = Sheet13.Range(Range("B2"), Range("B2").End(xlDown))
Count = 2
For i = 2 To PrevMonth.Rows.Count
match = Application.WorksheetFunction.match(Cells(i, 1), currMonth, 0)
If WorksheetFunction.IsNA(match) = True Then
Sheet13.Cells(i, 1).Cut
Sheet13.Cells(Count, 3).Paste
Count = Count + 1
End If
Next i
For i = 2 To currMonth.Rows.Count
match = WorksheetFunction.match(Cells(i, 1), PrevMonth, 0)
If WorksheetFunction.IsNA(match) = True Then
Sheet13.Cells(i, 1).Cut
Sheet13.Cells(Count, 4).Paste
Count = Count + 1
End If
Next i
Can you try using
Application.Match(Cells(i, 1), currMonth, 0)
I know this should have been a comment...but I'm unable to comment as I'm below 50 Rep...
In my experience the "cannot get 'X' property of the worksheet function class" error is indicative that a poor argument has been passed to the worksheet function itself. In your case the arguments are: Cells(i, 1), currMonth. This likely means one of those ranges are inaccessible. If I had to guess, you want to be passing sheet13.cells(i,1). I'd recommend a 'with sheet13' statement for this whole segment of code. WorksheetFunction.Match in particular will throw that error if a match can't be found (VB doesn't work with the #N/A return value). Using a WorksheetFunction.CountIf > 0 can ensure there is a viable match within the range.
Also, Range("A2").End(xlDown) will result in the last cell of the column if there is no data after A2. This is generally unfavorable behavior.

Inside a loop, how to indicate "all rows" when taking the mean of multiple columns (Visual Basic)

I have a loop wherein I take the mean of several columns of numbers with the same number of rows each.
The point of the loop is to capture these means in a new vector.
So for each loop I need to indicate "all rows". In matlab this would be easy, just use ":" But I can't figure out what the analogy is in VB. Please help! Thanks.
(Please advise me as to what I put in the code below where I have ALLROWS).
My attempt so far:
For i = 1 To CA
mrCA11(i) = Application.WorksheetFunction.Average(revCA11(**ALLROWS**,i))
Next i
In matlab this would be:
For i = 1:CA
mrCA11(i) = mean(revCA11(:,i));
Next i
EDIT: I've also tried this trick to no avail:
For j = 1 To CA
For i = 1 To s11
temp11(i) = revCA11(i, j)
Next i
mrCA11(j) = Application.WorksheetFunction.Average(temp11)
Next j
I get the error message: "Unable to get the Average property of the Worksheet Function class"
As everybody (Tim and shahkalpesh at least) pointed out, we need to understand what is revCall or more specifically, we need to understand how you want to give them ALL ROWS in argument.
Finding the last row (or column or cell)
A common Excel issue is to find the last used row / column / cell.
This will give you the end of your vector.
Excel give you several methods to deal with this:
xlTypeLastCell
Last cell used in the entire sheet (regardless if it's used in column A or not)
lastRow = ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row
End(xlUp)
Last cell used (including blanks in-between) in Column A is as simple as this:
lastRow = Range("A" & Rows.Count).End(xlUp).Row
End(xlToLeft)
Last cell used (including blanks in-between) in Row 1 is as simple as this:
lastRow = ActiveSheet.Cells(1, Columns.Count).End(xlToLeft).Row
UsedRange
Last cell used in the WorkSheet (according to Excel interpretation):
Set rangeLastCell = ActiveSheet.UsedRange
Using an array as argument
The methods above told you how to find the last row (if this is what you need). You can then easily create your vector and use it in your procedure revCA11.
You can either give an array as argument as Tim pointed out in his answer with this kind of statement:
myArray = ActiveSheet.Range("A1", Cells(lastRow, lastColumn).Value
Or you can use the integer (or long) to build your vector inside your procedure as simple as declaring a range:
Range("A1:A" & lastRow)
You might clarify exactly how revCA11 is declared/created, but maybe something along these lines might work for you:
Sub Tester()
Dim arr, x
arr = ActiveSheet.Range("A1:D5").Value '2-D array
'average each column
Debug.Print "Columns:"
For x = 1 To UBound(arr, 2)
Debug.Print x, Application.Average(Application.Index(arr, 0, x))
Next x
'average each row
Debug.Print "Rows:"
For x = 1 To UBound(arr, 1)
Debug.Print x, Application.Average(Application.Index(arr, x, 0))
Next x
End Sub