Finding first previous 0 from starting cell - vba

I'm trying to count the number of cells between two cells that I need to find with vba. The first cell is going to be in the same column as the current date today. The column headers are left to right in date order (May 1st, May 2nd, May 3rd, etc..). Once I find the column to begin with, I need to count the number of cells backwards that contain 0 until I find a cell without a 0. So say I'm starting in column U, I need to count the number of contiguous cells with 0 until say column R has a 1. My answer would then be 2 because columns T and S had 0's.
The issue I'm running into with the below code is that I'm trying to find first the column I'm supposed to be in based on the date (This part works). Next I'm declaring the rowNum as the activecell row number (This part work). Next I need to find which column to count backwards to (This part does not work). I'm getting the same column number on the colNum FIND code as I am the colStart FIND code.
I'm not getting any errors, it just doesn't seem like it's finding the previous cell with 0 and is instead just giving me the same column. I hope this is clear. Let me know if you need any additional info. Thanks everyone!
today = Date
colNum = Cells.Find(today, searchorder:=xlByRows,
searchdirection:=xlPrevious).Column
rowNum = ActiveCell.Row
colStart = Cells.Find(0, after:=Cells(rowNum, colNum),
searchorder:=xlByColumns, searchdirection:=xlPrevious).Column

Try this code:
Public Function countZeros(ws As Worksheet, rowNum As Long, colNum As Long, Optional stopColumn As Long = 1) As Long
Dim currentCol As Long
countZeros = 0
If colNum <= stopColumn Then Exit Function 'prevents going past a certain point (for example your label column)
For currentCol = colNum - 1 To stopColumn + 1 Step -1
If ws.Cells(rowNum, currentCol) = 0 Then
countZeros = countZeros + 1
Else
Exit Function
End If
Next 'currentCol
End Function
Public Sub mySub()
Dim colNum As Long
Dim rowNum As Long
Dim output As String
Dim today
Dim rng As Range
today = Date
Set rng = Cells.Find(today, searchorder:=xlByRows, searchdirection:=xlPrevious)
If rng Is Nothing Then
MsgBox "Did not find todays date, make sure your active cell is in the right location"
Exit Sub
End If
colNum = rng.Column
rowNum = ActiveCell.Row
output = "Total number of cells from " & Cells(rowNum, colNum).Address & " that have zeros: "
output = output & CStr(countZeros(ActiveWorkbook.ActiveSheet, rowNum, colNum))
MsgBox output
End Sub
With this data set:
you get the output:
Total number of cells from $K$7 that have zeros: 6

Related

VBA Excel search column for last changing value

I've got a column: U. This column has values from U10 till U500.
What I need to get is the last changing value of the column and if it doesn't change then a text "False" or something and if the last changing value is an empty cell, then ignore that..
Column U
11
11
5
11
11
21
For example here the result should be 21.
I've tried comparing two rows and with conditional formatting but with such a big range doing all this for each row is a bit too much.
Does anybody know a good way to do this?
Something like that should do it ...
Sub test()
Dim LastRow As Long, i As Long
With Worksheets("Sheet1") 'your sheet name
LastRow = .Cells(.Rows.Count, "U").End(xlUp).Row 'find last used row in column U
For i = LastRow To 2 Step -1 'loop from last row to row 2 backwards (row 1 can not be compared with row before)
If .Cells(i, "U").Value <> .Cells(i - 1, "U").Value Then 'compare row i with row before. If it changes then ...
MsgBox "Last row is: " & .Cells(i, "U").Address & vbCrLf & _
"Value is: " & .Cells(i, "U").Value
Exit For 'stop if last changing row is found
End If
Next i
End With
End Sub
It loops from last used row in column U to the first row and checks if the current row is different from the row before. If so it stops.
i am not sure of the how you want the output.
IF(AND(RC[-1]<>R[-1]C[-1],ROW(RC[-1])>500,R[-1]C[-1]<>""),RC[-1],"")
try this formula in cells V10:V500
Try this Macro.
First run the AnalyseBefore sub and when you want to check if the value has changed run the AfterAnalyse sub.
Incase you want the range to be dynamic use the code that I have commented and include iCount in your Range calculation
Sub AnalyseBefore()
Dim iCount
Range("U10").Select
iOvalue = Range("U500").Value
'iCount = Selection.Rows.Count
Range("Z1").Value = iOvalue
End Sub
Sub AnalyseAfter()
Dim iCount
Range("U10").Select
iNValue = Range("U500").Value
Range("Z2").Value = iNValue
iOvalue = Range("Z1").Value
If (iOvalue = iNValue) Then
Range("U500").Value = "FALSE"
End If
End Sub

Get the lowest values greater than zero

I have the above code, that finds the five max values from a column.
I need to do the same but for the minimum values higher then zero.
I need this to be in VBA because the user can change the values after in the worksheet.
I had already changed max for min, but get errors.
Sub best()
Dim maxvalue As Long
Dim copyrow As Long
copyrow = 30
Dim prevval As Long
Dim prevrow As Long
Dim i As Long
Dim fndrow As Long
prevval = 0
prevrow = 0
For i = 1 To 5
maxvalue = WorksheetFunction.Large(Sheets("Resumo").Range("J11:J47"), i)
If maxvalue <> prevval Then
fndrow = Sheets("Resumo").Range("J11:J47").Find(What:=maxvalue, LookIn:=xlValues, lookat:=xlWhole).Row
Else
fndrow = Sheets("Resumo").Range("J" & prevrow & ":J47").Find(What:=maxvalue, LookIn:=xlValues, lookat:=xlWhole).Row
End If
Dim vendor As String
vendor = Sheets("Resumo").Range("G" & CStr(fndrow))
Sheets("os melhores").Range("F" & CStr(copyrow)) = maxvalue
If InStr(vendor, " ") <> 0 Then
Sheets("os melhores").Range("G" & CStr(copyrow)) = Left(vendor, InStr(vendor, " "))
Sheets("os melhores").Range("H" & CStr(copyrow)) = Right(vendor, InStr(vendor, " "))
Else
Sheets("os melhores").Range("G" & CStr(copyrow)) = Sheets("Resumo").Range("G" & CStr(fndrow))
End If
prevval = maxvalue
prevrow = fndrow
copyrow = copyrow + 1
Next i
End Sub
File
Thanks in advance
You don't need to use a macro to solve this question or your original question about the top 5. You can use an array formula.
Please see this screen shot for reference:
Setup:
A1:A7 has data, you will need to update with your range
C2 has the formula in C3
C3:C7 have the top 5 largest values
D2 has the formula in D3
D3:D7 have the top 5 small values that are greater than 0
Taking the Max as an example, put the formula in cell C3 and press enter. You will then get the largest number. From there highlight cell C3 and press shift down 4 times so you highlight the next 4 rows. Then go into the formula bar and click the formula like you want to edit it. From there press ctrl+shift+enter (PC) command+enter (apple, I think) and it will fill in the remaining cells. They will update as you change the values in the referenced range.
Here is a screen shot of what it should look like to do the array formula:
As you can see, the cell with the formula is the main cell with focus while the next 4 cells are highlighted. The cursor is in the formula box and then press the ctrl+shift+enter.
Minor Update:
You don't even need an array formula to solve the largest 5. You could just set each excel to LARGE(A1:A7, 1) then the next cell as LARGE(A1:A7, 2) then the next cell as LARGE(A1:A7, 3), etc..
You could try this:
Option Explicit
Sub best()
Dim copyrow As Long
Dim helpRng As Range
copyrow = 30
With Worksheets("Resumo")
With .Range("J11:J47")
Set helpRng = .Offset(, .Parent.UsedRange.Columns.Count)
helpRng.Value = .Value
helpRng.Offset(, 1).Value = .Offset(, -7).Value
Set helpRng = helpRng.Resize(.Rows.Count + 1, 2).Offset(-1)
End With
End With
With helpRng
.Cells(1, 1).Resize(, 2) = "header"
.Sort key1:=helpRng, order1:=xlAscending, Header:=xlYes
.AutoFilter field:=1, Criteria1:=">0"
If Application.WorksheetFunction.Subtotal(103, .Resize(, 1)) > 1 Then
Worksheets("os melhores").Cells(copyrow, "F").Resize(5, 2).Value = .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).Resize(5).Value
Worksheets("os melhores").Cells(copyrow, "G").Resize(5).TextToColumns DataType:=xlDelimited, ConsecutiveDelimiter:=True, Space:=True
End If
.Parent.AutoFilterMode = False
.ClearContents
End With
End Sub
and change order1:=xlAscending to order1:=xlDescending to have the top five highest values report in "os melhores" worksheet
Based on the code, I think the error is caused by that if you change WorksheetFunction.Large to WorksheetFunction.Small (which you did, right?) then maxvalue can be 0, so 0 is not a dedicated 'invalid value' anymore. You can do one of the following:
declare maxvalue as variant and use a different value to mean invalid, e.g. "n/a", examine if maxvalue is this one in the first If.
(and this also helps to filter out 0's and negative values) The first parameter of WorksheetFunction.Large should be another Worksheet function that excludes, negative (or non-positive values), like you would do it in a formula:
=IF( 0 < a, a, bignumber )
Write an algorithm to store the values and the line numbers into a fixed size array.

Using VBA If then statement to copy and paste data

I am a brand new VBA user attempting to copy and paste data based on a range of dates. In column one I have dates and in column two I have the data I would like to copy and paste. CurYear refers to the end date in the range I am looking for and StatDate refers to the beginning date in the Range I am looking for. When I run this code it crashes Excel. Please help I am very lost
Worksheets("Weekly").Select
Dim nRows As Integer
Dim CurYear As Date
Dim StartDate As Date
nRows=Range("A1").CurrentRegions.Count.Rows
CurYear=Range("I265").Value
StartDate=Range("M5").Value
Do While Cells(nRows,1)<>""
if Cells(nRows,1).Value< CurYear & Cells(nRows,1)> StartDate Then
Cells(nRows,1).Offset(0,1).Copy
Worksheets("Weekly").Range("H41").Paste
Loop
End If
Put "option explicit" at the top of your code (before the sub) and it will tell you things to fix. Doing that will fix the part of your error where your end if was outside the loop instead of inside it but it won't catch that you weren't changing your loop counter. Try this code instead. It is actually pretty much the same as what you had with a couple minor changes.
Option Explicit
Sub test()
Dim sht As Worksheet, i As Long, l As Long, j
Dim nRows As Integer
Dim CurYear As Date
Dim StartDate As Date
Set sht = Worksheets("Test1") ' set the sheet as object isntead of selecting it for faster code and avoiding other issues
nRows = Cells(sht.Rows.Count, "B").End(xlUp).Row 'Last used row in column B - current region lastrow gets twitchy in some circumstances and should be avoided unless there is a reason to use it
l = 41
CurYear = range("I265").Value
StartDate = range("M5").Value
For i = 1 To nRows
If Cells(i, 1).Value < CurYear And Cells(i, 1).Value > StartDate Then 'for If statements you use "and" not "&"
Cells(l, 15) = Cells(i, 2) 'you will want something like this line and the next if you don't want to overwrite H41 if there is more than one match
l = l + 1
End If
Next i
End Sub
Also, to help with debugging, Open your locals window (View in the VBE). Step through your code with F8, watching your variables in the locals window to ensure that they are what you expect them to be at that step in your script.
If you do this with your code, you will see that you were missing a counter change with your variable for your loop. So it was looking for nRow to eventually be "" but it stays at whatever it was set to. Infinite loop. I changed it to a for next format but 6 of 1 and half dozen of another for your code.
Welcome to VBA. Don't poke yer eye out. :-)
Instead of using copy/ paste that uses a lot of memory and makes the program run slow, you maybe want to consider the following code that serves the same purpose as your code or Rodger's yet faster than using Select and copy/ paste syntax.
Sub Test()
Dim nRows As Long, LastRow As Long 'Declare as Long instead of Integer to avoid overflow
Dim CurYear As Date, StartDate As Date
LastRow = Cells(Rows.Count, 1).End(xlUp).Row 'Count the last used row in column 1 where you put the first data (dates)
nRows = 2 'Set the starting point of row where you put the first data (dates). In this example I use 2
CurYear = Range("I265").Value
StartDate = Range("M5").Value
Do
If Cells(nRows, 1).Value < CurYear And Cells(nRows, 1) > StartDate Then 'Use And not &
Cells(nRows, 5).Value = Cells(nRows, 2).Value 'This is essentially a "copy/ paste" syntax. Change the value (5) to the column you want to paste the value in column 2
End If
nRows = nRows + 1 'Set an increment value so each looping the nRows will increase by 1
Loop Until nRows = LastRow + 1 'Added by 1 so that the data in LastRow will keep being processed
End Sub

Excel - VBA/Formula for checking duplicate (similar*) records using multiple conditions

I'm looking for some help with this one. I need to check an Excel file with approx. 40000 records for similar records, according to multiple conditions. I also need this to be fairly foolproof for someone not entirely IT savvy to do regularly once shown how. Therefore, a macro or formula based solution would be preferred.
It seems easy enough to start with. Data has a header row with one record per row. It's easy enough to use conditional formatting to highlight rows with duplicate data in one or more columns, but I have an added complication.
Problem: I need to find records which have the same data in columns E and F (Surname and DOB), and I need the record to be highlighted as a duplicate if the dates in column K are similar (i.e. within 10 days of each other). Only if all 3 conditions are met would the record be highlighted as a potential duplicate.
Is this possible? I'm open to suggestions.
Function MATCHER(str1 As Range, rng1 As Range, str2 As String, rng2 As Range, num1 As Long, rng3 As Range, lim1 As Long)
Dim col(2) As Range, ws As Worksheet, rng As Range, str11 As String, FirstRow As Long, LastRow As Long
MATCHER = False
Set ws = rng1.Worksheet 'finds the worksheet of the first range (assumes all three ranges are on the same worksheet)
FirstRow = str1.Row + 1
LastRow = ws.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row 'find the last row in the worksheet
Set rng = ws.Range("1:" & LastRow) 'look down to the last row, inclusive
Set col(0) = Intersect(rng1.Columns, rng) 'apply the above criterion
Set col(1) = Intersect(rng2.Columns, rng) 'apply the above criterion
Set col(2) = Intersect(rng3.Columns, rng) 'apply the above criterion
For i = FirstRow To LastRow 'from first to last row
If col(0)(i, 1).Value2 = str1.Value2 Then 'if the first range variable's first column's value in row equals the first string variable
If col(1)(i, 1) = str2 Then 'if the second range variable's first column's value in row equals the second string variable
If col(2)(i, 1) >= num1 - lim1 And col(2)(i, 1) <= num1 + lim1 Then 'if the third range variable's first column's value in row equals the lookup value in confidence interval
MATCHER = True 'then it is true
Exit Function 'and there's no point in looking further
End If
End If
End If
Next
End Function
Sub ertdfgcvb()
x = MATCHER(Range("A1"), Range("A1"), "B", Range("B1"), 200, Range("C1"), 10)
MsgBox x
End Sub
For runtime considerations test in live environment. The first n-1 records will have the output TRUE where n = similar records.
It is meant to be used on a single recordset.

How to locate all '0' on fixed row and varying Columns, then sum them up?

I need help with my code, I'm not sure why it isnt running properly and takes a very long time. What i'm trying to do is to locate repeated temp, for example, 0. After locating 0, I will continue to look for any more 0 at the temp row, if there is i will sum the test1 of B3 and test1 of H3 together... it will continue until the end of the row and will be pasted at Column N or O which is an empty column. After that, will have to do the same for 100, overall.
The resultant should be like this
I have trouble running the following code that i tried writing.
Dim temprow As Long, ColMax1 As Long, tempcell As Range, ColCount1 As Long
Dim temprow1 As Long, valuetohighlight As Variant, valuetohighlight1 As Variant
Dim totalvalue As Double, findvalues As Long
temprow = 1
ColMax1 = 10
Do
Set tempcell = Sheets("Sheet1").Cells(temprow, 1)
'Look for the word temp in column A
If tempcell = "temp" Then
'Look for values = 0
For ColCount1 = 2 To ColMax1
findvalues = Sheets("Sheet1").Cells(temprow, ColCount1)
If findvalues = 0 Then
temprow1 = temprow + 1
valuetohighlight = Sheets("Sheet1").Cells(temprow1, ColCount1)
End If
Next
'Look for other values that is equal to 0
For ColCount1 = 3 To ColMax1
findvalues = Sheets("Sheet1").Cells(temprow, ColCount1)
If findvalues = 0 Then
temprow1 = temprow + 1
valuetohighlight1 = Sheets("Sheet1").Cells(temprow1, ColCount1)
End If
Next
temprow = temprow + 1
End If
Loop
For ColCount1 = 1 To ColMax1
If Sheets("Sheet1").Cells(temprow, ColCount1) = "" Then
totalvalue = 0
totalvalue = valuetohighlight + valuetohighlight1
End If
Next
End Sub
If you have any ideas or opinion, do share it with me.. will appreciate your help!
Slight Modifications
Now need also to consider the name.
What you want to achieve can be done with a formula. The trick is to keep the Cell Headers in Col O to Q in Row 2 to actual values that you want to compare.
Formula in Cell O3
=SUMPRODUCT(($B$2:$M$2=$O$2)*B3:M3)
Snapshot
FOLLOW UP
Hi, i remember u using that formula and typed it into a VBA for me before, i have tried and it work.. Sheets("Sheet1").[O5] = Evaluate("SUMPRODUCT((B2:M2=O2)*(B5:M5))") but, i cant really have a fixed column for the printed result and also the temp may not falls on Row 2...
Here is a sample code. Change 15 to the relevant column where you want to display the result. I have commented the code so you shouldn't have any problem in understanding the code. If you still do then simply ask :)
CODE
Option Explicit
Sub Sample()
Dim ColNo As Long, tempRow As Long
Dim ws As Worksheet
Dim aCell As Range
'~~> Change this to the column number where you want to display the result
'~~> The code assumes that Row 2 in this column has headers
'~~> for which you want to retrieve values
ColNo = 18 '<~~ Example :- Column R
'~~> Change this to relevant sheet name
Set ws = Sheets("Sheet1")
'~~> Get the row number which has "Temp"
Set aCell = ws.Columns(1).Find(What:="Temp", LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not aCell Is Nothing Then
'~~> This is the row which has 'Temp'
tempRow = aCell.Row
'~~> Sample for putting the value in Row 3 (assuming that 'temp' is not in row 3)
'~~> SNAPSHOT 1
ws.Cells(3, ColNo).Value = Evaluate("=SUMPRODUCT(($B$" & tempRow & ":$M$" & tempRow & "=" & _
ws.Cells(2, ColNo).Address & ")*(B3:M3))")
'~~> If you want to use formula in the cell in lieu of values then uncomment the below
'~~> SNAPSHOT 2
'ws.Cells(3, ColNo).Formula = "=SUMPRODUCT(($B$" & tempRow & ":$M$" & tempRow & "=" & _
ws.Cells(2, ColNo).Address & ")*(B3:M3))"
Else
MsgBox "Temp Not Found. Exiting sub"
End If
End Sub
SNAPSHOT (IF YOU USE EVALUATE IN THE ABOVE CODE)
SNAPSHOT (IF YOU USE .FORMULA IN THE ABOVE CODE)
HTH
Sid