How to make a cell conditionally negative? (Excel) - excel

I am dealing with monthly accruals in an Excel worksheet. I want to write a function or macro that makes it so when the month changes the values become negative.
What I have is a column (K) where it has the month, and another cell (M18) that has the month, And I want the values in the Amount (N) column to become negative if K=M18. But
=if($K21=$M$18,N=N*-1,"") doesn't work. I can write another column off to the side, say Q, where I can write =if($K21=$M$18, N*-1, ""), but I really need for the value in the N column itself to be negative. Is there a way to write a macro or nested functions to replace the value of N with a negative version of itself conditionally? If I have to make the value of negative N calculate in Q, can I then make a second function that goes back and makes N=Q?
K___L__M______N
____Input previous month
____February
___**Venue_Vendor_Amount**
February _______1,666
February ______3,240
February _______718

Here's a macro to do the trick:
Sub AccrualsMacro()
Dim rngN As Range
Dim clVal As Double
Dim cl As Range
Dim makeNegative As Boolean
makeNegative = [K21] = [M18]
If makeNegative Then
Set rngN = Range("N1", Range("N1").End(xlDown))
For Each cl In rngN
With cl
clVal = .Value
If Not clVal < 0 Then 'only apply this rule if the value is still positive
.Value = -1 * clVal
End If
End With
Next
End If
End Sub

Related

Filter the values according to Date and Time

I have been trying the below code to filter data in raw data sheet by using user inputed value in another sheet. Date is in one column and time is in another column. I need to filter the data using below example.
Start - 11/8/2022 00:30 End - 11/10/2022 14:00
I have tried the below code but its not working. output should we need to filter the data with start date as 11/08/2022 and in time column it should start from 00:30 till 23:30 and the next date 11/09/2022 and all the time intervals should be selected. and at last the date of 11/10/2022 should be selected and the time should be selected as 00:00 to 14:00.
11/08/2022 - 00:30-23:30
11/09/2022 - 00:00-23:30
11/10/2022 - 00:00:14:00
Please help me with this issue.
Thank you all for your support.
I have tried the below code modifying a little bit, but i couldnt able to do the required
Option Explicit
Sub Filter_My_Data()
Dim Data_sh As Worksheet
Dim Filter_Criteria_Sh As Worksheet
Dim Output_sh As Worksheet
Set Data_sh = ThisWorkbook.Sheets("Data")
Set Filter_Criteria_Sh = ThisWorkbook.Sheets("Filter_Criteria")
Set Output_sh = ThisWorkbook.Sheets("Output")
Output_sh.UsedRange.Clear
Data_sh.AutoFilterMode = False
Dim timelist() As long
Dim n As Integer
n = Application.WorksheetFunction.CountA(Filter_Criteria_Sh.Range("A:A")) - 2
ReDim timelist(n) As long
Dim i As Integer
For i = 0 To n
    timelist(i) = Filter_Criteria_Sh.Range("A" & i + 2)
Next i
Data_sh.UsedRange.AutoFilter 3, timelist(), xlFilterValues
Data_sh.UsedRange.Copy Output_sh.Range("A1")
Data_sh.AutoFilterMode = False
MsgBox ("Data has been Copied")
End Sub
The advanced filter can get pretty deep. Ref:
https://www.ablebits.com/office-addins-blog/excel-advanced-filter/
Fill in all but the OUTPUT. Then run the demo which will make the filter.
This may get you going. In a Module, these functions return TRUE/FALSE from comparing the dates and times to those in B12:B15
Function GE_Date1(zcell As Range) As Boolean ' compares to date in B12
GE_Date1 = (zcell.Value >= Range("B12").Value)
End Function
Function GE_Time1(zcell As Range) As Boolean ' compares to time in B13
GE_Time1 = (zcell.Value >= Range("B13").Value)
End Function
Function LE_Date2(zcell As Range) As Boolean ' compares to date in B14
LE_Date2 = (zcell.Value <= Range("B14").Value)
End Function
Function LE_Time2(zcell As Range) As Boolean ' compares to time in B15
LE_Time2 = (zcell.Value <= Range("B15").Value)
End Function
This can be in your sheet to do the filter.
The first 2 lines fill in A2:D2 - this would be done on the sheet, not VBA.
Sub FilterDemo()
' -- fill in the criteria
Range("A2", "B2") = Array("=GE_Date1(A4)", "=GE_Time1(B4)")
Range("C2", "D2") = Array("=LE_Date2(A4)", "=LE_Time2(B4)")
' -- now do the filtering. the most help is
' https://www.ablebits.com/office-addins-blog/excel-advanced-filter/
' "Formulas in the Advanced Filter criteria"
Range("A3:C10").AdvancedFilter xlFilterCopy, _
Range("A1:D2"), Range("F3"), Unique:=False
End Sub
EDIT: Some clarification:
A1:D1 - Criteria heading cells - "should be blank, or be different from any of the list range headings." What I have is the formulas that are entered in A2:D2. To enter the texts, you have to precede them with a ' (quote).
A2:D2 - These are the criteria; they are formulas (functions) returning TRUE/FALSE; GE = Greater than or Equal to; LE = Less than or Equal to. Formulas don't show (the values show) so I made them "xx" and entered the formulas in the VBA script.
B12:B15 - Entered as Dates and Times
From the link:
"Criteria on the same row are joined with an AND operator."
"Criteria on different rows are joined with an OR operator."
So all 4 formulas are ANDed together (all 4 must be TRUE).

How can I write a more complex search in VBA?

I am trying to return a list of dates within 30 days of a given day. For example, I want to be able to input todays date and I want excel to search a separate table, with multiple columns of dates. If it finds one within 30 days of today, AND the value of the cell to the left of the date reads "no", then I want it to return the date, the row, and the column.
My initial thought is to define the range of the table and loop through the cells. Then add an if statement that compares each cell with today's date. If they're within 30 days, it will check the value to the left with another if statement. If the value to the left reads no, then it will assign variable = cell value, row number, column number.
In theory, this will leave me with a list of variables that are my matches. Then, all I have to do it report the list of variables in a cell. My vba skills are basic so I need help with the actual diction.
***Update
At this point I'm just trying to get a working loop:
Sub GenerateReport()
'must have selection to work
'Application.DisplayAlerts = True
Dim rng As Range
Dim i As Integer
Dim TD() As Variant
ReDim TD(Selection.Cells.Count)
i = 1
For Each rng In Selection
If IsDate(Cell.Value) Then
If Abs(CDate(Cell.Value) - SearchDate) <= 30 And LCase(Cell.Offset(0, -1).Value) = "no" Then
TD(i) = Cell.Value
Else
TD(i) = 0
i = i + 1
End If
End If
Next
End Sub
I would expect to get a list of 1s and 0s from this, but I can't get it to run.
You could do something along these lines:
Sub GenerateReportFromSelection()
Dim c As Range, col As New Collection, v
For Each c In Selection.Cells
If c.Column > 1 Then 'make sure you can offset one col to left...
v = c.value 'read the value once
If IsDate(v) Then
If Abs(CDate(v) - Date) <= 30 And _
LCase(c.Offset(0, -1).value) = "no" Then
col.Add c 'add cell to collection
End If '30 days + no
End If 'is a date
End If 'col>1
Next
'review the hits
Debug.Print "---Matches---"
For Each c In col
Debug.Print c.value, c.Row, c.Column
Next c
End Sub

Is there a function for finding "ends with"

Is there a function to find from entries in a column that ends with ".5" using vba?
I have a live feed that I take from a html page, in values in column B are float numbers and I want to know if I can use a VBA function to find out how many values are ending with 0.5
Well without VBA:
=SUMPRODUCT(--(RIGHT(B1:B23,2)=".5"))
and with vba, then:
Sub dural()
MsgBox Evaluate("SUMPRODUCT(--(RIGHT(B1:B23,2)="".5""))")
End Sub
EDIT#1:
The worksheet formula treats column B like Strings. and counts how many in column B end with .5. This is expressed as an array of 0/1 by the expression within the --().
SUMPRODUCT() just adds up this array.
And with VBA, as a user-defined function (UDF):
Public Function CountThePointFive(ByRef theArea As Range) As Long
Dim count As Long
Dim cell As Variant
For Each cell In theArea
Dim value As String
value = CStr(cell.value)
Dim integerPart As Long
integerPart = CLng(Left$(CStr(value), Len(value) - InStr(1, value, ".")))
If (cell.value - integerPart) = 0.5 Then
count = count + 1
End If
Next cell
CountThePointFive = count
End Function

VBA Excel find date period in range and do calculations within

I am performing a VB function to sort out cumulative amount within a specified period with different categories. The objective is for personnel account expenses.
For example, I have in a sheet "tmp" in column A some dates, in column B a category (eg. Salary, Transport ...) and in column C the values.
I want to define a function and I have tried to set:
Function Cumulatif(Categorie As String, Debut As Date, Fin As Date) As Double
' Do the sum of a category between a starting and ending date specified
Dim Operations As Variant
Dim SpecificSum As Double
Dim i As Integer
Operations = ThisWorkbook.Sheets("tmp").Range("A1:C3")
For Each Row In Operations.Rows
SpecificSum = 0
Next
Cumulatif = SpecificSum
End Function
But I don't really know how to get the values from another sheet and do the loop in the range to set this sum. Can somebody help me on this?
The assigment to operations needs to be an object assignment, meaning: use Set (not Let/default).
Set Operations = ThisWorkbook.Sheets("tmp").Range("A1:C3)
You can get the values from the other sheet (assuming you mean "tmp") as follows
SpecificSum = SpecificSum + Row.Cells(1, 1)
I think you'd be better off having the function take an additional range parameter and supplying Sheet1!A as an argument; this way Excel will know when it needs to recalculate.
Function Cumulatif(Categorie As String, Debut As Date, Fin As Date, Operations As Range) As Double
' Do the sum of a category between a starting and ending date specified
'Dim Operations As Variant
Dim SpecificSum As Double
Dim i As Integer
'Set Operations = ThisWorkbook.Sheets("Sheet1").Range("A2:C3")
For Each Row In Operations.Rows
SpecificSum = SpecificSum + Row.Cells(1, 1)
Next
Cumulatif = SpecificSum
End Function
And call Cumulatif using such a formula:
=Cumulatif("hello9","10/1/2010", "10/31/2010",Sheet1!A2:A3)

Excel MAXIF function or emulation?

I have a moderately sized dataset in excel from which I wish to extract the maximum value of the values in Column B, but those that correspond only to cells in Column A that satisfy certain criteria.
The desired functionality is similar to that of SUMIF or COUNTIF, but neither of those return data that is necessary. There isn't a MAXIF function; how do I emulate one?
You can use an array formula.In the cell in which you want the max calculated enter: =Max(If([test],[if true],[if false]) where you replace the values in square brackets with the test, what to return if true and what to return if false. For example:
=MAX(IF(MOD(A2:A25,2)=0,A2:A25,0)
In this formula I return the value in column A if the value divided by 2 has no remainder. Notice that I use a range of cells in my comparison and in the value if false rather than a single cell.
Now, while still editing the cell, hit Ctrl+Shift+Enter (hold down the Ctrl key and the Shift together and then hit enter).
This creates an array formula that acts on each value in the range.
EDIT BTW, did you want to do this programmatically or manually? If programmatically, then what environment are you using? VBA? C#?
EDIT If via VBA, you need to use the FormulaArray property and R1C1 references like so:
Range("A1").Select
Selection.FormulaArray = "=MAX(IF(MOD(R[1]C:R[24]C,2)=0,R[1]C:R[24]C,0))"
Array formulas don't work very well when you want to use dynamic or named ranges (e.g., "the maximum amount due for rows above the current row that have the same counterparty as the current row). If you don't want to use an array formula, you can always resort to VBA to do something like this:
Function maxIfs(maxRange As Range, criteriaRange As Range, criterion As Variant) As Variant
maxIfs = Empty
For i = 1 To maxRange.Cells.Count
If criteriaRange.Cells(i).Value = criterion Then
If maxIfs = Empty Then
maxIfs = maxRange.Cells(i).Value
Else
maxIfs = Application.WorksheetFunction.Max(maxIfs, maxRange.Cells(i).Value)
End If
End If
Next
End Function
A limitation with the code provided thus far is that you are restricted to 2 conditions. I decided to take this code further to not restrict the number of conditions for the MaxIfs function. Please see the code here:
Function MaxIfs(MaxRange As Range, ParamArray Criteria() As Variant) As Variant
Dim n As Long
Dim i As Long
Dim c As Long
Dim f As Boolean
Dim w() As Long
Dim k As Long
Dim z As Variant
'Error if less than 1 criteria
On Error GoTo ErrHandler
n = UBound(Criteria)
If n < 1 Then
'too few criteria
GoTo ErrHandler
End If
'Define k
k = 0
'Loop through cells of max range
For i = 1 To MaxRange.Count
'Start by assuming there is a match
f = True
'Loop through conditions
For c = 0 To n - 1 Step 2
'Does cell in criteria range match condition?
If Criteria(c).Cells(i).Value <> Criteria(c + 1) Then
f = False
End If
Next c
'Define z
z = MaxRange
'Were all criteria satisfied?
If f Then
k = k + 1
ReDim Preserve w(k)
w(k) = z(i, 1)
End If
Next i
MaxIfs = Application.Max(w)
Exit Function
ErrHandler:
MaxIfs = CVErr(xlErrValue)
End Function
This code allows 1 to multiple conditions.
This code was developed with reference to multiple code posted by Hans V over at Eileen's Lounge.
Happy coding
Diedrich

Resources