Excel Vba Code to Check Quantity in a Column - excel

I have a macro to format a spreadsheet. I need some excel vba code to add to the start to check that the Quantity in a column is always '1'
The code needs to check the column from cell H2 to the bottom of the data in H2 (until it finds a blank cell).
If all the values are '1' do nothing and continue running the macro. If it finds any other number (either negative or positive) display a MsgBox "Warning: Quantities Other Than '1' Found. Fix Errors and Re-Run!" then when 'OK' is selected exit the macro.

Something like this:
Sub YourExistingCode()
If QuantityErrorFound Then
MsgBox "Warning: Quantities Other Than '1' Found. Fix Errors and Re-Run!"
Exit Sub
Else
'~~> Run your code
End If
End Sub
Function QuantityErrorFound() As Boolean
Dim cl As Range, result As Boolean
result = False
For Each cl In Range("H2:H" & Range("H2").End(xlDown).Row)
If cl.Value <> 1 Then
result = True
End If
Next cl
QuantityErrorFound = result
End Function
I've used a function (QuantityErrorFound) to make it easier to integrate into your existing code
In your existing code simply add the if statement to check whether an error is found

Just a slight change to Alex P's code really. As you're dealing with 1s a simple sum will be quicker than a loop
Function QuantityErrorFound() As Boolean
Dim result As Boolean
Dim lastR as long
Dim sumCells as long
Dim cntCells as Long
result = False
'lastR = Range("H2").End(xlDown).Row
lastR= Cells(rows.count, Range("H2").Column).End(Excel.xlUp).Row '<< assuming below the last cell is empty then this is a better approach to above line.
sumCells = Excel.Application.Sum(Range("H2:H" & lastR))
cntCells = Range("H2:H" & lastR).cells.count
if (sumCells = cntCells) then
result = True
end if
QuantityErrorFound = result
End Function
Personally in my work spreadsheets I would use a formula in a hidden cell (named range called "ErrorCheck") like this:
=if(countif(H2:H10000,"<>1")>0,"error","ok")
Then in my vba all I need is the following:
if ((range("ErrorCheck") = "error") then
MsgBox "Warning: Quantities Other Than '1' Found. Fix Errors and Re-Run!"
else
...
...
Edit
Please see flaw in my check as pointed out by Ian Cook. I will leave the code as is - but you should force values in column H to be either 1 or 0 if using the above. This could be done with a simple formula:
=if(<current formula>=1,1,0)
or
=1*(<current formula>=1)
Or, defend Ian's possible problem, by changing the Sum in my vba to a countIf:
Function QuantityErrorFound() As Boolean
Dim result As Boolean
Dim lastR as long
Dim sumCells as long
Dim cntCells as Long
result = False
'lastR = Range("H2").End(xlDown).Row
lastR= Cells(rows.count, Range("H2").Column).End(Excel.xlUp).Row '<< assuming below the last cell is empty then this is a better approach to above line.
sumCells = Excel.Application.WorksheetFunction.CountIf(Range("H2:H" & lastR),"=1") '<<not tested and may need to read ...,1)
cntCells = Range("H2:H" & lastR).cells.count
if (sumCells = cntCells) then
result = True
end if
QuantityErrorFound = result
End Function
Then again if using the above it could be simplified to the following:
Function QuantityErrorFound() As Boolean
Dim result As Boolean
Dim lastR as long
Dim sumCells as long
result = False
lastR= Cells(rows.count, Range("H2").Column).End(Excel.xlUp).Row
sumCells = Excel.Application.WorksheetFunction.CountIf(Range("H2:H" & lastR),"<>1")
if (sumCells = 0) then
result = True
end if
QuantityErrorFound = result
End Function

Related

Using the IsEmpty function in VBA [duplicate]

I want to check if a range in Excel is empty.
How do I write in VBA code:
If Range("A38":"P38") is empty
Found a solution from the comments I got.
Sub TestIsEmpty()
If WorksheetFunction.CountA(Range("A38:P38")) = 0 Then
MsgBox "Empty"
Else
MsgBox "Not Empty"
End If
End Sub
If you find yourself in a situation where you can’t use CountA then it's much faster to first store your range as an array and loop on the array's data than it is to individually loop on range/cell data.
Function IsRangeEmpty(ByVal rng As Range) As Boolean
''Returns true if a value is found in parameter range.
''Converts parameter range to an array to check it quickly.
'if range has cells in it then
If Not rng Is Nothing Then
Dim area As Range
For Each area In rng.Areas 'checks through all sub-ranges within the original range e.g., rng=Range("A1:B5,C6:D9")
'if sub-range has more than one cell then
If area.Cells.Count > 1 Then
'save range as array
Dim arr As Variant
arr = area.value
'loop through array
Dim arrCell As Variant
For Each arrCell In arr
'if cell is not empty then
If Len(Trim(arrCell)) > 0 Then
IsRangeEmpty = False
Exit Function
End If
Next arrCell
Else 'unnecessary to loop on a single cell
'if cell is not empty then
If Len(Trim(area.Value2)) > 0 Then
IsRangeEmpty = False
Exit Function
End If
End If
Next area
End If
IsRangeEmpty = True
End Function
Example of how to use it:
Sub debug_IsRangeEmpty()
Debug.Print IsRangeEmpty(Range("A38:P38"))
End Sub
If Range("A38:P38") is empty, it would print True in the Immediate Window.
IsEmpty returns True if the variable is uninitialized, or is explicitly set to Empty; otherwise, it returns False. False is always returned if expression contains more than one variable. IsEmpty only returns meaningful information for variants. (https://msdn.microsoft.com/en-us/library/office/gg264227.aspx) . So you must check every cell in range separately:
Dim thisColumn as Byte, thisRow as Byte
For thisColumn = 1 To 5
For ThisRow = 1 To 6
If IsEmpty(Cells(thisRow, thisColumn)) = False Then
GoTo RangeIsNotEmpty
End If
Next thisRow
Next thisColumn
...........
RangeIsNotEmpty:
Of course here are more code than in solution with CountA function which count not empty cells, but GoTo can interupt loops if at least one not empty cell is found and do your code faster especially if range is large and you need to detect this case. Also this code for me is easier to understand what it is doing, than with Excel CountA function which is not VBA function.
Dim M As Range
Set M = Selection
If application.CountIf(M, "<>0") < 2 Then
MsgBox "Nothing selected, please select first BOM or Next BOM"
Else
'Your code here
End If
From experience I just learned you could do:
If Selection.Rows.Count < 2
Then End If`
Clarification to be provided a bit later (right now I'm working)
Dim cel As Range, hasNoData As Boolean
hasNoData = True
For Each cel In Selection
hasNoData = hasNoData And IsEmpty(cel)
Next
This will return True if no cells in Selection contains any data. For a specific range, just substitute RANGE(...) for Selection.
Another possible solution. Count empty cells and subtract that value from the total number of cells
Sub Emptys()
Dim r As range
Dim totalCells As Integer
'My range To check'
Set r = ActiveSheet.range("A1:B5")
'Check for filled cells'
totalCells = r.Count- WorksheetFunction.CountBlank(r)
If totalCells = 0 Then
MsgBox "Range is empty"
Else
MsgBox "Range is not empty"
End If
End Sub
This just a slight addition to #TomM's answer/ A simple function to check
if your Selection's cells are empty
Public Function CheckIfSelectionIsEmpty() As Boolean
Dim emptySelection As Boolean:emptySelection=True
Dim cell As Range
For Each cell In Selection
emptySelection = emptySelection And isEmpty(cell)
If emptySelection = False Then
Exit For
End If
Next
CheckIfSelectionIsEmpty = emptySelection
End Function
This single line works better imho:
Application.Evaluate("SUMPRODUCT(--(E10:E14<>""""))=0")
in this case, it evaluates if range E10:E14 is empty.

VBA Macro Returning Excel Formula result of "#VALUE" error instead of intended result

I am attempting to write code that will add an input number of leading 0s to strings inside of the range currently selected by the user when the macro is run. I keep encountering errors where instead of returning the expected result, the evaluation of the formula is returning the #Value error and I honestly don't know where to start. I have looked into several solutions using the debugging assistance docs provided by Microsoft but I either don't understand the solution or it isn't there.
Sub Add_leading_00()
Dim rng As Range
Dim Area As Range
Dim leading_count As Long
leading_count = Application.InputBox(Prompt:="Enter an integer:", Type:=1)
If Selection.Cells.Count = 1 Then
Set rng = Selection
Else
Set rng = Selection.SpecialCells(xlCellTypeConstants)
End If
For Each Area In rng.Areas
Area.Value = Evaluate("Rept(0, " & leading_count & ")& & Area.Address & ")
Next Area
End Sub
I do have a function that returns similar results but I have been unsuccessful in my attempts to call that function just on the range selected by the user at the time of the run, and that is necessary for the planned functionality of the macro.
Function AddLeadingZeroes(ref As Range, Length As Integer)
Dim i As Integer
Dim Result As String
Dim StrLen As Integer
StrLen = Len(ref)
For i = 1 To Length
If i <= StrLen Then
Result = Result & Mid(ref, i, 1)
Else
Result = "0" & Result
End If
Next i
AddLeadingZeroes = Result
End Function
There are several discussions on topics adjacent to this one as well that I have found and read through but I am not sure if they're relevant to my error or not. I suspect that I am missing something extremely simple.
Leading 0s question
text formatting

Finding the n-th cell in a column with a given property

I don't have much experience but I'm trying to write a function that will search column A and the 1st time it finds a string beginning with "AT" it will copy that whole string to Cell N1, the 2nd string beginning with "AT" will be copied to N2, so on and so forth until column A is exhausted. This is my feeble attempt so far but I'm not having much luck.
Function Find_AT(ByVal I As Integer)
Dim c As Range
Dim COUNTER As Integer
Dim CAPTURE As Long
COUNTER = 0
For Each c In Range("A1", Range("A65636").End(xlUp))
If Left(c, 2) = AT Then
COUNTER = COUNTER + 1
If COUNTER = I Then
CAPTURE = c
Exit For
End If
End If
Next c
Find_AT = CAPTURE
End Function
Consider:
Function Find_AT(ByVal I As Long) As String
Dim c As Range
Dim COUNTER As Long
Dim CAPTURE As String
Dim v As String
COUNTER = 0
CAPTURE = "xx"
For Each c In Range("A1", Range("A65636").End(xlUp))
v = c.Text & " "
If Left(v, 2) = "AT" Then
COUNTER = COUNTER + 1
If COUNTER = I Then
CAPTURE = c.Address
Exit For
End If
End If
Next c
Find_AT = CAPTURE
End Function
The error with your code is that the text (the string) AT needs to be enclosed in double-quotes "AT". Add Option Explicit to the top of the Module and it would take you to this error when you try to compile or execute the function.
However, given your description, I suspect that you might want to write a sub-procedure (SUB) not a Function. A function is intended to return a value. If you want to use a function you might define it like this:
Function Find_AT(rng As Range, ByVal i As Integer)
That is, you would supply it a Range to search and the number 1 to find the first value in the range that begins with "AT". However, if you put this function in a cell and copy it down, it will still return only the first occurrence. You would need to manually change 1 to 2, 3, etc. (or use a variation of ROW() to automatically generate this sequence).
Anyway, I suspect you really want a SUB-procedure that you might run by clicking a button on the worksheet.
If you wish to continue with your current function, then you could declare the return type as a string:
Function Find_AT(ByVal i As Integer) As String
'...
Dim CAPTURE As String
'...
CAPTURE = c.Text
Otherwise, setting CAPTURE = c and attempting to return this value causes a problem because c is a Range object.
Filtering is much more efficient. Two approaches below:
Filter
Sub GetAT1()
X = Filter(Application.Transpose(Range([a1], Cells(Rows.Count, "A").End(xlUp))), "AT", True)
If UBound(X) > 0 Then [n1].Resize(UBound(X) + 1) = Application.Transpose(X)
End Sub
AutoFilter
Sub GetAT()
Dim rng1 As Range
Set rng1 = Range([a1], Cells(Rows.Count, "A").End(xlUp))
Application.ScreenUpdating = False
ActiveSheet.AutoFilterMode = False
rng1.AutoFilter 1, "=AT*"
rng1.Copy [n1]
If LCase$(Left$([n1], 2)) <> "at" Then [n1].Delete xlUp
ActiveSheet.AutoFilterMode = False
Application.ScreenUpdating = True
End Sub

check if a range is empty in vba module

I wanted to check if an excel range in empty in a section of code in user module.
I used the below code
Worksheets(yearsheet).Range("N" & rownum & ":DI").Select
If Application.WorksheetFunction.CountA(Selection) = 0 Then
Exit Sub
End If
I'm getting runtime error 1004. Can anyone tell whats my mistake?
Thanks in advance.
PS: rownum is integer variable and yearsheet is string variable. both these variables were updated properly in code prior to the above section of the code
"N" & rownum & ":DI" doesn't evaluate to a real address because it's missing the row number for the second half of the address. Also, you should avoid using Select statement whenever possible.
Assuming the whole range is in one row, this would work:
Sub test()
Dim yearsheet As String
Dim rownum As Integer
yearsheet = "Sheet2"
rownum = 2
If Application.WorksheetFunction.CountA(Worksheets(yearsheet) _
.Range("N" & rownum & ":DI" & rownum)) = 0 Then
Exit Sub
End If
End Sub
The best way to test if a selection is (not) empty in VBA:
' Tests if a selection of cells exists.
' #return true or false
Function isCellSelection() As Boolean
Dim r As range
isCellSelection = False
Set r = Selection.Cells
If IsEmpty(r) Then
isCellSelection = True
End If
End Function ' isCellSelection

Detect if range is empty

I want to check if a range in Excel is empty.
How do I write in VBA code:
If Range("A38":"P38") is empty
Found a solution from the comments I got.
Sub TestIsEmpty()
If WorksheetFunction.CountA(Range("A38:P38")) = 0 Then
MsgBox "Empty"
Else
MsgBox "Not Empty"
End If
End Sub
If you find yourself in a situation where you can’t use CountA then it's much faster to first store your range as an array and loop on the array's data than it is to individually loop on range/cell data.
Function IsRangeEmpty(ByVal rng As Range) As Boolean
''Returns true if a value is found in parameter range.
''Converts parameter range to an array to check it quickly.
'if range has cells in it then
If Not rng Is Nothing Then
Dim area As Range
For Each area In rng.Areas 'checks through all sub-ranges within the original range e.g., rng=Range("A1:B5,C6:D9")
'if sub-range has more than one cell then
If area.Cells.Count > 1 Then
'save range as array
Dim arr As Variant
arr = area.value
'loop through array
Dim arrCell As Variant
For Each arrCell In arr
'if cell is not empty then
If Len(Trim(arrCell)) > 0 Then
IsRangeEmpty = False
Exit Function
End If
Next arrCell
Else 'unnecessary to loop on a single cell
'if cell is not empty then
If Len(Trim(area.Value2)) > 0 Then
IsRangeEmpty = False
Exit Function
End If
End If
Next area
End If
IsRangeEmpty = True
End Function
Example of how to use it:
Sub debug_IsRangeEmpty()
Debug.Print IsRangeEmpty(Range("A38:P38"))
End Sub
If Range("A38:P38") is empty, it would print True in the Immediate Window.
IsEmpty returns True if the variable is uninitialized, or is explicitly set to Empty; otherwise, it returns False. False is always returned if expression contains more than one variable. IsEmpty only returns meaningful information for variants. (https://msdn.microsoft.com/en-us/library/office/gg264227.aspx) . So you must check every cell in range separately:
Dim thisColumn as Byte, thisRow as Byte
For thisColumn = 1 To 5
For ThisRow = 1 To 6
If IsEmpty(Cells(thisRow, thisColumn)) = False Then
GoTo RangeIsNotEmpty
End If
Next thisRow
Next thisColumn
...........
RangeIsNotEmpty:
Of course here are more code than in solution with CountA function which count not empty cells, but GoTo can interupt loops if at least one not empty cell is found and do your code faster especially if range is large and you need to detect this case. Also this code for me is easier to understand what it is doing, than with Excel CountA function which is not VBA function.
Dim M As Range
Set M = Selection
If application.CountIf(M, "<>0") < 2 Then
MsgBox "Nothing selected, please select first BOM or Next BOM"
Else
'Your code here
End If
From experience I just learned you could do:
If Selection.Rows.Count < 2
Then End If`
Clarification to be provided a bit later (right now I'm working)
Dim cel As Range, hasNoData As Boolean
hasNoData = True
For Each cel In Selection
hasNoData = hasNoData And IsEmpty(cel)
Next
This will return True if no cells in Selection contains any data. For a specific range, just substitute RANGE(...) for Selection.
Another possible solution. Count empty cells and subtract that value from the total number of cells
Sub Emptys()
Dim r As range
Dim totalCells As Integer
'My range To check'
Set r = ActiveSheet.range("A1:B5")
'Check for filled cells'
totalCells = r.Count- WorksheetFunction.CountBlank(r)
If totalCells = 0 Then
MsgBox "Range is empty"
Else
MsgBox "Range is not empty"
End If
End Sub
This just a slight addition to #TomM's answer/ A simple function to check
if your Selection's cells are empty
Public Function CheckIfSelectionIsEmpty() As Boolean
Dim emptySelection As Boolean:emptySelection=True
Dim cell As Range
For Each cell In Selection
emptySelection = emptySelection And isEmpty(cell)
If emptySelection = False Then
Exit For
End If
Next
CheckIfSelectionIsEmpty = emptySelection
End Function
This single line works better imho:
Application.Evaluate("SUMPRODUCT(--(E10:E14<>""""))=0")
in this case, it evaluates if range E10:E14 is empty.

Resources