My Excel crashes whenever I run this simple VBA sub - vba

Sub Highlight_Diff()
Dim i, j As Integer
i = Starting_Row
Do While Cells(i, 3).Value <> ""
For j = 1 To 2 * Currencies
If Abs(Cells(i, 3 * (3 * Currencies + 1) + j).Value) > 100000 Then
Cells(i, 3 * (3 * Currencies + 1) + j).Interior.ColorIndex = 6
End If
Next j
Loop
End Sub
Note Currencies and Starting_Row are just constant integers. As you can see all I am trying to do is loop though rows of code and higlight particularly large balances (for context this has to do with discrepancies which arise due to exchange rate differentials). When I try running this simple sub, my Excel crashes... every single time. I tried running it in a module instead of workbook but that didn't work either. It's definitely not because there are two many rows (only a few hundred, and only 4 cells per row as currencies is set to 2 for this particular job). Any idea on how I can fix this?
*NOTE: Yes I did have i = 1 instead of j = 1 in my for loop at first but I fixed this and it still crashes, so that doesn't appear to be the problem.

You seem to have an error here : i is always reset to 1 at the beginning you the main loop since you also use it in the for.
Do While Cells(i, 3).Value <> ""
For i = 1 To 2 * Currencies
If Abs(Cells(i, 3 * (3 * Currencies + 1) + j).Value) > 100000 Then
Cells(i, 3 * (3 * Currencies + 1) + j).Interior.ColorIndex = 6
End If
Next j
Loop
Since you use next j, I beleive you intended to use that, so change the 'i' in the for for a 'j' and see if it works
If it's the source of your problem, you may be witnessing an infinite loop.
As pointed by cyboashu you might also need to use a Long since vba Integers only count up to 32767 and down to -32768

Your while loop is always looking at the same value.
Since i is no longer a looping variable (in the corrected code) you are left with the following while loop:
Do While Cells(Starting_Row, 3).Value <> ""
...
Loop
You aren't changing the value of Starting_Row, and you aren't changing the value in the cell.
Either you (1) never execute the Do-While code or (2) you execute it forever.
I expect you forgot to increment i
See if this works:
Sub Highlight_Diff()
Dim i, j As Integer ' better practice to make this type a Long
i = Starting_Row
Do While Cells(i, 3).Value <> ""
For j = 1 To 2 * Currencies
If Abs(Cells(i, 3 * (3 * Currencies + 1) + j).Value) > 100000 Then
Cells(i, 3 * (3 * Currencies + 1) + j).Interior.ColorIndex = 6
End If
Next j
i = i + 1 'moves you to the next row until empty cell is found
Loop
End Sub

Related

VBA numbering loop

I am trying to simplify a code in a Macro for numbering rows of specific columns of excel. Currently I am using
With Sheet1.Range("V6")
.Value = 1
.AutoFill .Resize(V6 + C, 1), xlFillSeries
End With
The macro has already set "C" as a variable that can change each time it is run. I want to simplify the code because I don't know how to loop this to repeat in every third column. I have tried For Loops but i am new to VBA and cannot get the program to run. Looping this would help me becasue I currently have this same code altered 85 differnt times to fill 85 different columns. For example, the next set is
With Sheet1.Range("Y6")
.Value = 1
.AutoFill .Resize(Y6 + C, 1), xlFillSeries
End With
Is there a more simple way this can be accomplished?
An alternate approach using Offset to fill every third column, starting in V6.
Sub MyNumbering()
Dim c As Long, i As Long
c = 100
For i = 0 To 84
With Sheet1.Range("V6").Offset(, i * 3)
.Value = 1
.AutoFill .Resize(c), xlFillSeries
End With
Next i
End Sub
This will iterate every 3rd column starting with column V and post 85 columns of row numbers starting in row 6 and ending at C + 6
Sub mynum()
Dim c As Long: c = 100
Dim j As Long
For j = 22 To 22 + 85 * 3 Step 3
With Sheet1.Range(Sheet1.Cells(6, j), Sheet1.Cells(c + 6, j))
.Formula = "=ROW(1:1)"
.Value = .Value
End With
Next j
End Sub

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

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.

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

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

search for similar phone numbers in a column

I have a collection of phone numbers in a column. I want to identify similar ones in the column. The similarity rule would be the first 8 digits are the same and the last two digits are in sequence (at least 3 numbers).
For example,
8601116612
8601116613
8601116614
The three numbers above should be identified as being similar. How can I do this in VBA?
Alrighty, this was kinda frustrating, but here it is:
Sub findSims()
Dim i, j, rc, rc1 As Double
Dim m, z As Double
i = 1
m = 0
z = 0
Do While Cells(i, 1) <> ""
' calculate the value of the last two digits
' for educational reasons one calculation
rc = Right(Cells(i, 1), 2)
rc1 = Right(Cells(i + 1, 1), 2) - 1
' .. and one direct comparation ..
' .. of the first 8 digits and afterwards the first two
If Left(Cells(i, 1), 8) = Left(Cells(i + 1, 1), 8) Then
If rc = rc1 Then
' there are two counters because we have to wait until we have three similar numbers
m = m + 1
z = z + 1
Else
' if the similarities stop, we kill m
m = 0
' we use z to write this cutie of a sentence (you can insert here whatever you like)
If z > 1 Then
Cells(i, 2) = "The Cells from A" & i & " to A" & (i - z) & " are similar."
End If
z = 0
End If
End If
' increment i and the job is done
i = i + 1
Loop
End Sub
This seems kinda clunky and theres probably a better solution, but thats the best I came up with. Have fun.