#ERROR: Unable to get match property of the worksheet class. I have already tried solutions available online - vba

NOTE: My problem was not solved by other similar questions on this as well as other sites. Please have a fair view at my question before judging the same
I am trying to perform a task in which first I have to identify the smallest, 2nd smallest numbers and so on and according to this I have to copy data from one column to another. This will continue until the sum of the copied values becomes grater than or equal to certain value in the sheet (Here row no. for the comparison is given by variable "b"). This will be repeated for 172 different sets which are repeated after every 43 cells.
I have written the following code:
Dim m As Range, k As Double, j As Double, b As Double, lIndex As Double, a As Double
Set m = ActiveSheet.Range("E3:E40")
For i = 1 To 172
j = 1
b = 45 + 43 * (i - 1)
For k = 1 To 38
a = Application.Small(m, j)
lIndex = Application.WorksheetFunction.Match(a, m, 0)
If Cells(b, 7).Value < Cells(b, 1).Value Then
Cells(lIndex, 7).Value = Cells(lIndex, 2).Value
Else
End If
j = j + 1
Next k
Set m = m.Offset(43)
Next i
Now there is an error that pops up saying, Unable to get match property of the worksheet class.
NOTE: I have tried solutions online.
Can there be any other way to do it
OR
Is there something wrong I am doing logically or in the syntax as I am new to excel VBAs and coding itself.

a = Application.Small(m, j) will surely return an Error Code when j is actually bigger that the size of te range m. In your code, the range m = Range("E3:E40") has 38 cells, but j can go as high as 38 * 172.
Then you try to call Match with an error code as the first parameter a. This resuts in run-time error. Note here that Application.Match would result in an error code while WorksheetFunction.Match raises a run-time error.
In all cases, no error should occur in your Match if you had fetched correctly the "kth smallest" element. Without being able to check all of you code, I guess what you wanted here was
a = Application.Small(m, k) ' <--- k, not j
And then no error should occur in *.Match(a, m, 0).

After checking your code:
After getting the smallest value, the next value of j should be a + 1 not j + 1.
Why? because if your smallest value is 4 from (4, 6, 10)
on first loop, j = 1, small will return 4.
on second loop, j = 2, small will still return 4, instead of 6.

Related

Excel VBA UDF assign values to a Range according to certain criteria

first question on Stack Overflow here. I'm quite new to VBA so please bear with my question.
I'm trying to run regressions using User Defined Function, and I would like to generate two range variables that are filtered according to a criteria.
For example, in Sheet1 I have:
And I would like to run a regression with Xs and Ys when X is smaller than 0.3.
Previously I tried a code using For loop to assign values to the range:
Dim X As Range
Dim Y As Range
Dim i As Integer
Dim j As Integer
j = 1
With ActiveSheet
For i = 1 To 100
If Cells(i, 1).Value < 0.3 Then
X(j) = Cells(i, 1).Value
Y(j) = Cells(i, 2).Value
End If
j = j + 1
Next i
End With
And apparently it doesn't work so I didn't proceed to the regression part. Could anyone help me correct this?
Alternatively, I've been thinking about whether it's possible to get the whole range of values first and then delete some of them.
Now I have set X and Y ready:
Set X = Range(Cells(1, 1), Cells(7, 1))
Set Y = Range(Cells(1, 2), Cells(7, 2))
But I'm not sure what the possible ways are to delete (not setting them to 0 or empty) some units in these range variables. Could anyone help me with that as well?
Thanks everyone for helping me.

How to apply ATPVBAEN into a dataset with skipped data within the same column?

So I have a problem where I am stuck and cannot figure out a way to solve it. My code is explained ahead, but the main issue is that I must apply a smoothening exponential function with ATPVBAEN, by sections, because data is not always present, although my x axis still needs to be progressive for each measurement and some may be skipped, as displayed below:
0 34.5
2 40.6
4 41.5
6
8
10 30.4
12 32.9
Since I need to use exponential smoothening in a set of data, so programmatically I accessed in the following form the ATPVBAEN add-in provided by Excel.
Preparing my data:
Dim plan As Worksheet
Set plan = ThisWorkbook.Sheets("PLANILLA")
Dim plan2 As Worksheet
Set plan2 = ThisWorkbook.Sheets("FILTER")
Dim data As Worksheet
Set data = ThisWorkbook.Sheets("DATA")
Getting offsets and coping values to be extracted from original data before using he exponential smoothening.
k = data.Range("C2", data.Range("C2").End(xlDown)).Rows.Count 'Find last index x axis column
p = k + 15 'Set offset
plan2.Range("K16", "K" & p).Value = data.Range("E2", "E" & k + 1).Value 'Copy values
plan2.Range("L16", "L" & p).Value = data.Range("E2", "E" & k + 1).Value
plan2.Range("M16", "M" & p).Value = data.Range("F2", "F" & k + 1).Value
Now, I run the function that allows me to do the data smoothening, placing data in the row L (destination), using the data from column D (origin). So finally I do the following:
Application.Run "ATPVBAEN.XLAM!Expon", plan2.Range("L16", "L" & p), plan2.Range("D16", "D" & p), smoothVal, False, False, False
Here is where I need a way in which I can apply the function in sections, because I have data that skips some cells, leaving them empty. The problem is that Excel assumes empty data as a 0, and my measurements get affected in a really bad way. Is there a work around to apply the ATPVBAEN by parts like in a for loop (setting on each loop the beginning and end with the variables p and k like above)? How can I be able to recognize where the beginning and end of each subset of data is located within the column?
Because the line :
(…, data.Range("C2").End(xlDown)).Rows.Count
seems to be not an effective solution.
My code works greatly when I use the full length of the column with a continuous dataset, but I would like to make it possible to work with datasets that have skipped data.
Many thanks!!
So the solution and a way to work around is the following:
Use a Loop to iterate over data until we know there are not any subsets left. Skip the subsets doing a module operation to get the reminder og the interation count.
Do While reachMax > k 'Control how many times I can iterate over data
init = prev = k
counter = counter + 1
k = data.Range("E" & 1, data.Range("E" & init).End(xlDown)).Rows.Count 'Find last index of column
p = k + 15 'Set offset because we are working with data of other sheet
If (counter Mod 2) = 1 Then 'Able to skip subsets with no data
Application.Run "ATPVBAEN.XLAM!Expon", plan2.Range("K" & prev + 14, "K" & p), plan2.Range("C" & prev + 14, "C" & p), smoothVal, False, False, False
End If
Loop
Obviously we are setting the variables of reachMax with the x axis last data, and k with the index number of the y-axis data, in this located in columns C (destination) and K (origin)

Nested For...Next loops to copy and paste several times

I want to copy the cells "A2:A" & patientprofiles + 1 and paste them in the first unused row in column D (i.e., there should be no blank cells between what's already in column D and what I want to paste there, but I also don't want to paste over what's already there). I then want to repeat this process a user-defined number of times (this variable will be called g1_observations). I then want to copy the cells "A" & patientprofiles + 2 & ":A" & 2 * patientprofiles + 1 to the new last used row in column D (i.e., taking into account that I've just pasted patientprofiles number of cells g1_observations number of times at the bottom of column D. I want to continue repeating this process a user-defined number of times (this number of times is defined by the variable numberofgrids).
For example: imagine that the user has defined that there will be three grids. Grid 1 will have 2 observations, Grid 2 will have 3 observations, and Grid 3 will have 4 observations. Also imagine that patientprofiles has been set to 40.
If this is the case, there will already be values in cells D1:D121, so I want to begin pasting in D122. I want to paste the cells A2:A41 (40 cells because patientprofiles = 40) to cells D122:D161; I want to paste the cells A42:A81 to cells D162:D201 and again to D:202:D241; and I want to paste cells A82:A121 to cells D242:D281, again to cells D282:D321, and again to cells D322:D361. I'm pasting each "grid" one less time than the number of observations for that grid, because the first group of observations for all grids is what's contained in cells D2:D121. End example
I'm pretty sure I need to use a nested For...Next loop in order to do this, but I'm having trouble with both the inner and outer loop. I think the outer loop should go something like this:
Dim i as long
For i = 0 to numberofgrids - 1
[insert inner loop here]
Next
As far as the inner loop goes, I'm not really sure what I'm doing because it keeps pasting over itself when I am pasting from two grids. The current code I have uses repeated For...Next loops and doesn't work:
Dim myLastRow as Integer
myLastRow = Worksheets("Work").UsedRange.Rows.Count
Dim j as Long
For j = 1 To g1_observations - 1
If j = 1 Then
Range(Cells(2, 1), Cells((patientprofiles + 1), 1)).Copy _
Destination:=Worksheets("Work").Cells(j * myLastRow + 1, 4)
ElseIf j > 1 Then
Range(Cells(2, 1), Cells((patientprofiles + 1), 1)).Copy _
Destination:=Worksheets("Work").Cells((j + 1) * (myLastRow / 2) + 1, 4)
Else: Range("A1").Select
End If
Next
For j = 1 To g2_observations - 1
If j = 1 Then
Range(Cells(patientprofiles + 2, 1), Cells((2 * patientprofiles + 1), 1)).Copy _
Destination:=Worksheets("Work").Cells(j * myLastRow + 1, 4)
ElseIf b > 1 Then
Range(Cells(patientprofiles + 2, 1), Cells((2 * patientprofiles + 1), 1)).Copy _
Destination:=Worksheets("Work").Cells((b + 1) * (myLastRow / 2) + 1, 4)
Else: Range("A1").Select
End If
Next
It pastes over itself, and sometimes it skips lines. I can't really figure out how to reconcile myLastRow with a loop.
I think the inner loop should probably start off something like this:
Dim j as Long
For j = 0 to gj_observations - 1
Range(Cells(j * XXX + 2, 1), Cells((j + 1) * patientprofiles + 1).Copy _
Destination:=Worksheets("Work").Cells(myLastRow * j + 1) , 4
but I'm having difficulty because the variables are called g1_observations, g2_observations, g3_observations, etc., all the way up to g10_observations, and obviously gj_observations won't work. I want to loop on the number between "g" and "_", but I don't know how to get VBA to read variables that way, or if that's possible at all.
Can anyone help me out here? My mind is spinning from trying to understand the concept of loops, especially with different variables at each level.
Also, side question, how do you tell VBA to do nothing in an If statement? I currently have it selecting A1 by writing Else: Range("A1").Select, but I'm sure there's a better way of doing it.
When you're writing macros, it's a better practice to work with ranges and avoid manipulating cells one at a time in a loop. Your macro will run much faster and the code will be clearer.
If you want to create a set of variables that you can access by number, you would use something called an array. This is a pretty fundamental concept that exists in almost every programming language, so I'll refer you to MSDN or your favorite VBA language reference guide for more details.
Dim ws As Worksheet
Dim lr As Long ' Last Row
Dim szpp As Long ' Size (rows) patient profiles
Dim szgobsrv(2) As Long ' Size (rows) observation groups
Dim i As Long
Dim j As Long
Dim SourceCells As Range
Dim TargetCell As Range
Set ws = Sheets("Work")
szpp = 40
szgobsrv(0) = 1
szgobsrv(1) = 2
szgobsrv(2) = 3
For i = 0 To UBound(szgobsrv)
lr = ws.UsedRange.Row + ws.UsedRange.Rows.Count - 1
' copy the patient profile cells multiple times depending on group size
For j = 0 To szgobsrv(i) - 1
Set SourceCells = ws.[A2].Offset(i * szpp).Resize(szpp)
Set TargetCell = ws.[D1].Offset(lr + j * szpp)
SourceCells.Copy TargetCell
Next
Next
Note the usage of the Resize and Offset methods. These are helpful Range methods that can change the size and position of a range by a fixed amount.
The main problem you are having with values being over written is that youre not using Offset.
Another important thing to remember about nested loops is that the nested loop runs i times per loop of the upper level loop. I am thinking that nested loops here might not be good for you. You could probably just make them all independent loops?
If you want to loop to the number contained within the variable you might want to set that variable equal to a number.
example:
g2_observations =2
For j = 1 To g2_observations - 1
Aside from this I am actuall yhaving difficulty understanding what you need, but hopefully this helps?
numberofgrids = input
i = 1 to numberofgrids
gridCount = gridCount + 1
'Loop Stuff
Case Select gridCount
Case is = 1
'logic
Case is = 2
'logic
Etc etc
End Select
If numberofgrids = gridCount Then
Exit For
End If
Next i

VBA Match and Find Error: No matching type & Undefined Error

Code
n = Worksheets("Datasheet").UsedRange.Rows.count
For l = 2 To k
check = Application.Find(Worksheets("AFAS Dump").Cells(l, 1), Worksheets("Datasheet").Range("K4:K10000000"))
If check = 0 Then
Cells(n + 1, 1) = Worksheets("AFAS Dump").Cells(l, 13)
End If
Next l
AFAS Dump column 1 contains an amount of nr's. Datasheet also contains an amount of nr's. I want to check whether a nr from AFAS dump is also in Datasheet. If not I want to add that nr to datasheet. I want to use Match or Find but they both give me problems.
Another thing is my K10000000, I want the nr to be changed by a parameter but ive noticed that doesn't work. I prefer Ke where e will be assigned to a value.
It's a little unclear what you are trying to achieve as you've got a reference to Column 13 in one of the lines which doesn't make sense if you are trying to add to Column K. If you want the macro to check if a number from Column 1 in the 'AFAS Dump' sheet is in Column K of the 'Datasheet' and add it to the bottom of 'Datasheet' if it's not already there then try this:
n = Worksheets("Datasheet").Cells(Rows.Count,11).End(xlup).Row
e = Worksheets("AFAS Dump").Cells(Rows.Count,1).End(xlup).Row
For l = 2 To e
If Worksheetfunction.Match(Worksheets("AFAS Dump").Cells(l, 1), _
Worksheets("Datasheet").Range("K4:K" & n),0) = 0 Then
Worksheets("Datasheet").Cells(n + 1, 11) = Worksheets("AFAS Dump").Cells(l, 1)
n = n + 1
End If
Next l

Excel VBA: Find first value in row larger than 0 and sum over following 4 cells

As a complete beginner to VBA Excel, I would like to be able to do the following:
I want to find the first value larger than 0 in a row, and then sum over the following 4 cells in the same row. So
Animal1 0 0 1 2 3 0 1
Animal2 3 3 0 1 4 2 0
Animal3 0 0 0 0 0 1 0
Results in
Animal1 7
Animal2 11
Animal3 1
Is this possible?
(Your problem description didn't match your examples. I interpreted the problem as one of summing the 4 elements in a row which begin with the first number which is greater than 0. If my interpretation is wrong -- the following code would need to be tweaked.)
You could do it with a user-defined function (i.e. a UDF -- a VBA function designed to be used as a spreadsheet function):
Function SumAfter(R As Range, n As Long) As Variant
'Sums the (at most) n elements beginning with the first occurence of
'a strictly positive number in the range R,
'which is assumed to be 1-dimensional.
'If all numbers are zero or negative -- returns a #VALUE! error
Dim i As Long, j As Long, m As Long
Dim total As Variant
m = R.Cells.Count
For i = 1 To m
If R.Cells(i).Value > 0 Then
For j = i To Application.Min(m, i + n - 1)
total = total + R.Cells(j)
Next j
SumAfter = total
Exit Function
End If
Next i
'error condition if you reach here
SumAfter = CVErr(xlErrValue)
End Function
If your sample data is in A1:H3 then putting the formula =SumAfter(B1:H1,4) in I1 and copying down will work as intended. Note that the code is slightly more general than your problem description. If you are going to use VBA, you might as well make your subs/functions as flexible as possible. Also note that if you are writing a UDF, it is a good idea to think of what type of error you want to return if the input violates expectations. See this for an excellent discussion (from Chip Pearson's site - which is an excellent resource for Excel VBA programmers).
ON EDIT: If you want the first cell greater than 0 added to the next 4 (for a total of 5 cells in the sum) then the function I gave works as is, but using =SumAfter(B1:H1,5) instead of =SumAfter(B1:H1,4).
This is the one of the variants of how you can achieve required result:
Sub test()
Dim cl As Range, cl2 As Range, k, Dic As Object, i%: i = 1
Set Dic = CreateObject("Scripting.Dictionary")
For Each cl In ActiveSheet.UsedRange.Columns(1).Cells
For Each cl2 In Range(Cells(cl.Row, 2), Cells(cl.Row, 8))
If cl2.Value2 > 0 Then
Dic.Add i, cl.Value2 & "|" & Application.Sum(Range(cl2, cl2.Offset(, 4)))
i = i + 1
Exit For
End If
Next cl2, cl
Workbooks.Add: i = 1
For Each k In Dic
Cells(i, "A").Value2 = Split(Dic(k), "|")(0)
Cells(i, "b").Value2 = CDec(Split(Dic(k), "|")(1))
i = i + 1
Next k
End Sub
Here is what I would use, I dont know any of the cell placement you have used so you will need to change that yourself.
Future reference this isnt a code writing site for you, if you are new to VBA i suggest doing simple stuff first, make a message box appear, use code to move to different cells, try a few if statments and/or loops. When your comftable with that start using varibles(Booleans, string , intergers and such) and you will see how far you can go. As i like to say , "if you can do it in excel, code can do it better"
If the code doesnt work or doesnt suit your needs then change it so it does, it worked for me when i used it but im not you nor do i have your spread sheet
paste it into your vba and use F8 to go through it step by step see how it works and if you want to use it.
Sub test()
[A1].Select ' assuming it starts in column A1
'loops till it reachs the end of the cells or till it hits a blank cell
Do Until ActiveCell.Value = ""
ActiveCell.Offset(0, 1).Select
'adds up the value of the cells going right and removes the previous cell to clean up
Do Until ActiveCell.Value = ""
x = x + ActiveCell.Value
ActiveCell.Offset(0, 1).Select
ActiveCell.Offset(0, -1).ClearContents
Loop
'goes back to the begining and ends tallyed up value
Selection.End(xlToLeft).Select
ActiveCell.Offset(0, 1).Value = x
'moves down one to next row
ActiveCell.Offset(1, 0).Select
Loop
End Sub