I am trying to make sub's in vba to repeat a process processC after some time
as designated in the Cell K8 = 00:10:00 (text format).
Dim RunTimer As Date
Sub repeatA()
'RunTimer = Now + TimeValue("00:10:00")
RunTimer = Now + TimeValue(K8)
Application.OnTime RunTimer, "repeatB"
End Sub
Sub repeatB()
call processC
call repeatA
End Sub
Sub processC()
...
End Sub
It works fine when I use RunTimer = Now + TimeValue("00:10:00"), but when I tried as above I got below error message. I want to assign "00:10:00" from a cell K8 entry.
You didn't type Range which is probably your main issue. It also matters if your cell's value is numeric or text.
Numeric Examples
In this example below, I have typed 00:10:00 this into the cell (no equals sign). Excel converts this to decimal (.0069444 -- essentially the percentage of a full day). The display may be the decimal or a time such as 12:10:00 AM. Regardless this is a numeric value that does not need to be converted with VBA.
Another numeric situation would be if the cell formula was: =TIMEVALUE("00:10:00") which converts exactly to the same .0069444.
All numeric situation should work with this VBA code:
RunTimer = Now + Range("K8").Value
Text Examples
If your cell is actually text with formulas/displays such as:
="00:10:00"
'00:10:00"
Then you SHOULD use your TimeValue Vba method:
RunTimer = Now + TimeValue(Range("K8").Value)
Test if Numeric or Text
You could also try to make it dynamic by testing using isNumeric (though this might have some issues, depending on excel file)
If IsNumeric(Range("K8")) Then
RunTimer = Now + Range("K8").Value
Else
RunTimer = Now + TimeValue(Range("K8"))
End If
Hope that helps.
Related
I'm working on a research related to frequencies.
I want to sum all the numbers in each cell and reduce them to single number only.
some cells have 2 numbers, others have 13 numbers. like these..
24.0542653897891
25.4846064424057
27
28.6055035477009
I tried several formulas to do that. the best ones have me 2 digits number, that I couldn't sum it again to get a single result.
like these Formulas:
=SUMPRODUCT(MID(SUBSTITUTE(B5,".",""),ROW(INDIRECT("1:"&LEN(B5)-1)),1)+0)
=SUMPRODUCT(1*MID(C5,ROW(INDIRECT("1:"&LEN(C5))),1))
any suggestion?
Thank you in advance.
EDIT
Based on your explanation your comments, it seems that what you want is what is called the digital root of the all the digits (excluding the decimal point). In other words, repeatedly summing the digits until you get to a single digit.
That can be calculated by a simpler formula than adding up the digits.
=1+(SUBSTITUTE(B5,".","")-1)-(INT((SUBSTITUTE(B5,".","")-1)/9)*9)
For long numbers, we can split the number in half and process each half. eg:
=1+MOD(1+MOD(LEFT(SUBSTITUTE(B5,".",""),INT(LEN(SUBSTITUTE(B5,".",""))/2))-1,9)+1+MOD(RIGHT(SUBSTITUTE(B5,".",""),LEN(SUBSTITUTE(B5,".",""))-INT(LEN(SUBSTITUTE(B5,".",""))/2))-1,9)-1,9)
However, the numbers should be stored as TEXT. When numbers are stored as numbers, what we see may not necessarily be what is stored there, and what the formula (as well as the UDF) will process.
The long formula version will correct all the errors on your worksheet EXCEPT for B104. B104 appears to have the value 5226.9332653096000 but Excel is really storing the value 5226.9333265309688. Because of Excel's precision limitations, this will get processed as 5226.93332653097. Hence there will be a disagreement.
Another method that should work would be to round all of the results in your column B to 15 digits (eg: . Combining that with using the long formula version should result in agreement for all the values you show.
Explanation
if a number is divisible by 9, its digital root will be 9, otherwise, the digital root will be n MOD 9
The general formula would be: =1+Mod(n-1,9)
In your case, since we are dealing with numbers larger than can be calculated using the MOD function, we need to both remove the dot, and also use the equivalent of mod which is n-(int(n/9)*9)
Notes:
this will work best with numbers stored as text. Since Excel may display and/or convert large numbers, or numbers with many decimal places, differently than expected, working with text strings of digits is the most stable method.
this method will not work reliably with numbers > 15 digits.
If you have numbers > 15 digits, then I suggest a VBA User Defined Function:
Option Explicit
Function digitalRoot(num As String) As Long
Dim S As String, Sum As Long, I As Long
S = num
Do While Len(S) > 1
Sum = 0
For I = 1 To Len(S)
Sum = Sum + Val(Mid(S, I, 1))
Next I
S = Trim(Str(Sum))
Loop
digitalRoot = CLng(S)
End Function
You could use a formula like:
=SUMPRODUCT(FILTERXML("<t><s>"&SUBSTITUTE(A1," ","</s><s>")&"</s></t>","//s"))
You might need an extra SUBSTITUTE for changing . to , if that's your decimal delimiter:
=SUMPRODUCT(FILTERXML("<t><s>"&SUBSTITUTE(SUBSTITUTE(A1,".",",")," ","</s><s>")&"</s></t>","//s"))
However, maybe a UDF as others proposed is also a possibility for you. Though, something tells me I might have misinterpreted your question...
I hope you are looking for something like following UDF.
Function SumToOneDigit(myNumber)
Dim temp: temp = 0
CalcLoop:
For i = 1 To Len(myNumber)
If IsNumeric(Mid(myNumber, i, 1)) Then temp = temp + Mid(myNumber, i, 1)
Next
If Len(temp) > 1 Then
myNumber = temp
temp = 0
GoTo CalcLoop
End If
SumToOneDigit = temp
End Function
UDF (User Defined Functions) are codes in VBA (visual basic for applications).
When you can not make calculations with Given Excel functions like ones in your question, you can UDFs in VBA module in Excel. See this link for UDF .. If you dont have developer tab see this link ,, Add a module in VBA in by right clicking on the workbook and paste the above code in that module. Remember, this code remains in this workbook only. So, if you want to use this UDF in some other file your will have to add module in that file and paste the code in there as well. If you are frequently using such an UDF, better to make add-in out of it like this link
In addition to using "Text to Columns" as a one-off conversion, this is relatively easy to do in VBA, by creating a user function that accepts the data as a string, splits it into an array separated by spaces, and then loops the elements to add them up.
Add the following VBA code to a new module:
Function fSumData(strData As String) As Double
On Error GoTo E_Handle
Dim aData() As String
Dim lngLoop1 As Long
aData = Split(strData, " ")
For lngLoop1 = LBound(aData) To UBound(aData)
fSumData = fSumData + CDbl(aData(lngLoop1))
Next lngLoop1
fExit:
On Error Resume Next
Exit Function
E_Handle:
MsgBox Err.Description & vbCrLf & vbCrLf & "fSumData", vbOKOnly + vbCritical, "Error: " & Err.Number
Resume fExit
End Function
Then enter this into a cell in the Excel worksheet:
=fSumData(A1)
Regards,
The UDF below will return the sum of all numbers in a cell passed to it as an argument.
Function SumCell(Cell As Range) As Double
Dim Fun As Double ' function return value
Dim Sp() As String ' helper array
Dim i As Integer ' index to helper array
Sp = Split(Cell.Cells(1).Value)
For i = 0 To UBound(Sp)
Fun = Fun + Val(Sp(i))
Next i
SumCell = Fun
End Function
Install the function in a standard code module, created with a name like Module1. Call it from the worksheet with syntax like =SumCell(A2) where A2 is the cell that contains the numbers to be summed up. Copy down as you would a built-in function.
I was entering data and I mistakenly entered my "Purchase Order Number" in the "Purchase Order Date" cell.
This resulted in an overflow error on the following line.
If Target.Column > 1 And Target <> "" Then
Target.Column evaluated to 2 as expected so I'm pretty sure it was failing on Target <> "" .
The target cell was set to date format and I typed "4502554236" in it (which doesn't evaluate to a correct date). Now looking on the sheet I see the cell is filled with ########### .
I was making sure the cell was not empty but I suppose I need to also check if the data in the cell isn't an error ?
What is the proper way to do that ?
thanks
If you need to test for errors in cells, you can use the IsError() function. In your case, this isn't the problem and isn't what is causing the overflow on the following line. Having a value that can't be represented as a date in a cell is not an error (it is just invalid for that cell format) - Excel just displays it as a string of '#' instead. You can verify this by inserting this line, which will report "False":
Debug.Print IsError(Target)
The problem is that you rely on Target being a Date, so that is what you need to test for. Conveniently, there is an IsDate function you can test with. Try replacing your If statement with this:
`If Target.Column > 1 And IsDate(Target) Then`
This code afterwards also has a couple issues (from the comments):
If Target > "1899-12-30" And Target < "1900-02-01" Then _
Target = CDate(Year(Date) & "-" & Month(Date) & "-" & _
Format(Day(Target + 1), "00"))
First, you are mixing String and Date types in your comparison, which can be a recipe for disaster because you are actually doing string comparisons as opposed to date comparisons. This is completely dependant on the format of the date. Consider this line of code:
Debug.Print DateSerial(2000, 1, 1) > "1899-12-30"
It evaluates to False with my regional settings because it is basically testing this...
Debug.Print "1/1/2000" > "1899-12-30"
...and '/' happens to be "less than" '8'.
Note that since 1899-12-30 is the epoch date for VBA, you can replace this with a simple > 0. It is much safer, easier, and more readable to use DateSerial to build a date to test against. Note that you can also simplify your building of the formatted date string with a single call to Format:
Format$(Date + 1, "yyyy-mm-dd")
However, if you already have that in the cell format (evidenced by your string comparisons working), you can just let Excel do the formatting for you and set the date to the cell directly. Combine all of that, and you get something like this:
If Target.Column > 1 And IsDate(Target) Then
If Target > 0 And Target < DateSerial(1900, 2, 1) Then
Target = Date + 1
End If
End If
If you are sure that Target is only a single cell then you can always check with:
..........If Target.Text <> "" ...................
I have an excel sheet set up to automatically calculate meetings per day by day of the week. I would like to write a formula to return all dates I have a meeting scheduled (comma separated preferably), but I am having some difficulty. Using Vlookup, I can only get it to return the first date.
For example, here is what my data looks like:
A B C
Initial Meetings Follow-up Meetings Date
1 1 7/29/2015
0 1 7/30/2015
1 1 7/31/2015
0 0 8/1/2015
0 0 8/2/2015
I would like to write a formula to return "7/29/2015, 7/31/2015" in one cell, and "7/29/2015, 7/30/2015, 7/31/2015" in another, but I seem to be stuck.
You can't do this with vLookup.
This can be done relatively easily in a VB script, but it would affect portability as many if not most users disable macros by default and in many cases users are prevented from using Macros because their company disables them and makes it policy that users should not use them.
If you are OK with Macros, you can put the following into a new module and then use =MultiVlookup(lookup_value,table_array, col_index_num) in the same way as you'd use vlookup and it should give you a comma separated list of multiple matches:
Public Function MultiVlookup(find_value, search_range, return_row)
Dim myval ' String to represent return value (comma-separated list)
Dim comma ' Bool to represent whether we need to prefix the next result with ", "
comma = False
'Debug.Print find_value.value, return_row
For Each rw In search_range.Rows ' Iterate through each row in the range
If rw.Cells(1, 1).value = find_value Then ' If we have found the lookup value...
If comma Then ' Add a comma if it's not the first value we're adding to the list
myval = myval + ", "
Else
comma = True
End If
myval = myval + Str(rw.Cells(1, return_row).value)
End If
Next
MultiVlookup = myval
End Function
This may not be the cleanest way of doing it, and it isn't a direct copy of vlookup (for instance it does not have a fourth "range lookup" argument as vlookup does), but it works for my test:
Finally my original suggestion (in case it helps others - it's not the exact solution to the question) was:
I've not tried it myself, but this link shows what I think you might be looking for.
Great code, but don't forget to add the following is you use Option Explicit:
Dim rw As Range
WHEELS
I'm having problem making the match() work in excel VBA. The code is:
x = Application.Match("Sep 2008", Range("F1:F1"), 0)
The value in cell F1 is 9/1/2008.
Even if I changed Sep 2008 to 9/1/2008, it still doesn't return any value.
Any idea how to fix it?
The reason why Even if I changed Sep 2008 to 9/1/2008, it still doesn't return any value.
Is because when there is a Date in excel, Excel automatically converts that date to a numeric value, What you really want to search for is:
39692
This number is the number of days between 9/1/2008 and excel default of 1/1/1900
every date in excel is stored with a value like this. So the easiest way to handle this would be to convert what you see as a date to what excel sees as a date using CDate().
This by itself will give you an unuseful error that vba can't get the property.
That is because the Lookup_value can be a value (number, text, or logical value) or a cell reference to a number, text, or logical value. Not a date so simply convert the now date value to a number to search for the matching number in the list using CLng()
Give this a shot it will also be much faster then using the Find alternative:
x = WorksheetFunction.Match(CLng(CDate("Sep 2008")), Range("F1:F1"), 0)
This should give you the result expected
To handle when no match is found try this Sub:
Sub MatchDate()
Dim myvalue As Double
Dim LastRow As Long
LastRow = Cells(Rows.Count, "F").End(xlUp)
On Error GoTo NotFound
myvalue = WorksheetFunction.Match(CLng(CDate("Sep 2008")), Range("F1:F" & LastRow), 0)
MsgBox (myvalue)
End
NotFound:
MsgBox ("No Match Was Found")
End
End:
End Sub
Your best bet is to use .Find(). This will return a range if found or nothing if not.
Set x = Range("F1:F1").Find(CDate("Sept 2008"), , , xlWhole)
If you wanted the column number:
x = Range("F1:F1").Find(CDate("Sept 2008"), , , xlWhole).Column
With capture of not found
Sub test()
Dim y As Date, x As Variant, c As Long
y = CDate("Sep 2008")
Set x = Range("1:1").Find(y, , , xlWhole)
If Not x Is Nothing Then
c = x.Column '<~~found
Else
Exit Sub 'not found
End If
End Sub
Bottom line:
use WorksheetFunction.Match(CDbl(date), range, 0)
Alternatively, use a Date cell's Value2 property (which will also be a Double) instead of Value for the search key.
CLng suggested in other answers would discard the time part of the date.
The same problem exists for the Currency data type but you can't use CDbl for it (see below for options).
Range.Value2 Property (Excel) article suggests that Date and Currency types are "special" in that they have an "internal representation" that's in stark contrast with displayed value. Indeed:
Date is internally represented as IEEE 64-bit (8-byte) floating-point numbers where the integer part is the date and fractional part is the time
Currency is also 8-byte but is treated as a fixed-point number with 4 fractional digits (an integer scaled by 10'000)
Apparently, Match compares these internal values for performance reasons. So, we must ensure that they, rather than the readable representations, match exactly.
Since Date is already floating-point internally, CDbl(date) doesn't actually change the data.
For the Currency type, CDbl does change data, so it's out of question. So either
use the exact representation of the key (to 4 fractional digits) this way or another if you require exact match, or
make the cells in the range actually be formulas with Round) if the value to compare with comes from elsewhere and/or you only require equality to 2 fractional digits
This way it works using this method:
Nbr,L, C as Integer
Datedeb as date
nbr = WorksheetFunction.Match(CLng(CDate(Datedeb)), Range(Cells(L, C), Cells(L + 100, C)), 0)
I think I can safely assume that the value in F1 is a date. In you code "Sep 2008" is a string. You will never be able to get a successful match as long as your datatypes are inconsistent.
If you are looking for a date, then make sure that the first parameter is a date.
Dim dSearchSDate As Date
dSearchSDate = "01/Sept/2008"
x = Application.Match(dSearchSDate, Range("F1:F1"), 0)
Here is another possible approach.
Sub temp()
Dim x
Dim dSearchSDate As Date
dSearchSDate = "01/Sept/2008"
If ThisWorkbook.Worksheets(1).Range("F1:F1").Value = dSearchSDate Then
Debug.Print "Found it!"
Else
Debug.Print "Doh!!"
End If
End Sub
I know this post is old, but I had the same issue, and did find the answer.
To make it work, you first need to make VBA see the same data formatting as it appears in your excel spreadsheet :
YourVar = Format("YourDate","mmm-yyyy")
YourResult = Application.match(Clng(Cdate(YourVar)), YourRange, 0)
Regards
Gilles
On the Daily Dose of Excel website, written by the late Frank Kabel, there are some formulae which can stand in for ATP functions. Not being an Excel guru, I'm struggling with converting one (so far!) to VB6. (Why I'm doing this I may relate once the NDA runs out.)
The problem I'm having is with the code standing in for SERIESSUM, namely,
=SUMPRODUCT(coefficients,x^(n+m*(ROW(INDIRECT("1:"&ROWS(coefficients)))-1)))
Now the SUMPRODUCT and ROWS functions I've been able to render fairly simply with
Public Function SUMPRODUCT(a1 As Variant, a2 As Variant) As Double
Dim dRes As Double
Dim dVal As Double
Dim i As Long
If LBound(a1) = LBound(a2) And UBound(a1) = UBound(a2) Then
For i = LBound(a1) To UBound(a1)
dVal = a1(i) * a2(i)
dRes = dRes + dVal
Next
End If
SUMPRODUCT = dRes
End Function
Public Function ROWS(a1 As Variant)
ROWS = UBound(a1) - LBound(a1) + 1
End Function
What I don't 'get' yet is
how x^(n+m*(ROW(INDIRECT("1:"&ROWS(coefficients)))-1)) evaluates to an array
and what that array might contain
Any Excel gurus out there?
ROW(INDIRECT("1:"&ROWS(coefficients)))-1
If coefficients has 5 rows, this will return the array {1,2,3,4,5}. The rest of the progression is
{1m, 2m, 3m, 4m, 5m)
{n+1m, n+2m, n+3m, n+4m, n+5m)
{x^n+1m, x^n+2m, x^n+3m, x^n+4m, x^n+5m)
That resulting array gets 'series summed' against coeffecients.
You can see the progression in Excel's formula bar by using Ctrl+= on highlighted parts of the formulas. There is a limit on how many characters you can display in the formula bar, so if coefficients has a lot of rows, you may get the error "formula too long"
In the formula bar, select ROW(INDIRECT("1:"&ROWS(coefficients)))-1 and press Ctrl+=. Then select another portion of your formula, making sure you match opening and closing parentheses, and hit Ctrl+=. You can iterate this until you have the whole formula calculated. When you're done, be sure to ESCAPE out of the cell so you don't lose your original formula.
See also Episode 474 here.