I receive a CSV file daily in which I filter to look for certain data. This file requires a lot of manual effort within Excel to filter and format the data. I am devising a VBScript to look at each line to return only the data needed to reduce the manual effort.
Within the CSV file is a "time seen" string which is formatted strangely. The "time seen" data differs from line to line. An example of this data is the following:
3hrs27min 35sec
35min 20sec
8min 38sec
1days1hrs25min 30sec
5days12hrs9min 48sec
I am using this code snippet to remove the "days", "hrs", "min ", and "sec" from the data and replace them with a ":".
strLastField0 = arrFields(9)
strLastField1 = Replace(strLastField0,"min ",":")
strLastField2 = Replace(strLastField1,"hrs",":")
strLastField3 = Replace(strLastField2,"days",":")
strLastField4 = Replace(strLastField3,"sec","")
The result is the following:
d:h:m:s
3:27:35
35:20
8:38
1:1:25:30
5:12:9:48
I am looking to have the data come out formatted in the following manner instead of what it currently is.
hh:mm:ss
03:27:35
00:35:20
00:08:38
25:01:25
132:09:48
Here is a function in which I have been working with to accomplish this, but my attempts have failed to get the formatting like I want.
Function funcFormatTime(TimeString)
Dim TimeArray
Dim h, m, s, hh, mm, ss
TimeArray = Split(TimeString, ":", -1, 1)
d = TimeArray(0)
h = TimeArray(1)
m = TimeArray(2)
s = TimeArray(3)
Do Until s < 60
s = s - 60
m = m + 1
Loop
Do Until m < 60
m = m - 60
h = h + 1
Loop
Do Until h < 24
h = h - 24
Loop
If Len(Trim(h)) = 1 Then hh = "0" & h Else hh = h
If Len(Trim(m)) = 1 Then mm = "0" & m Else mm = m
If Len(Trim(s)) = 1 Then ss = "0" & s Else ss = s
funcFormatTime = hh & ":" & mm & ":" & ss
End Function
This uses a regular expression to split the input strings using the .Replace method with a function pointer that will receive as arguments each of the elements in the string if present or an Empty value if not present.
Option Explicit
Dim aStrings
aStrings = Array( _
"3hrs27min 35sec", _
"35min 20sec", _
"8min 38sec", _
"1days1hrs25min 30sec", _
"5days12hrs9min 48sec" _
)
Dim sTime
For Each sTime in aStrings
WScript.Echo funcFormatTime( sTime )
Next
Function funcFormatTime( inputString )
With New RegExp
.Pattern = "^(?:([0-9]+)days)?(?:([0-9]+)hrs)?(?:([0-9]+)min)?(?:\s*([0-9]+)sec)"
funcFormatTime = .Replace( inputString, GetRef("funcCalcTime") )
End With
End Function
Function funcCalcTime( matchedString, d, h, m, s, offset, originalString )
funcCalcTime = LeftZeroPad( CLng("0" & d) * 24 + Clng("0" & h), 2) & ":" & _
LeftZeroPad( CLng("0" & m), 2) & ":" & _
LeftZeroPad( CLng("0" & s), 2)
End Function
Function LeftZeroPad( value, length )
LeftZeroPad = CStr(value)
If Len(LeftZeroPad) < length Then
LeftZeroPad = Right(String(length, "0") & CStr(LeftZeroPad), length)
End If
End Function
Each of the elements in the regular expression have the form
(?:([0-9]+)days)?
Where (?: )? means that the parenthesis do not define a capture group (?:) and that this group could or couldn't be present (the closing ?). Inside this expression there is a ([0-9]+) that define a capture group that match a sequence of numeric digits. Capture groups are passed as arguments to the replace function, where the only work to do is properly format the values.
Related
Let's say I have four data values and one of them exists sometimes.
My For loop crashes because the path doesn't exist.
I would like to pass a "" in the cell instead of crashing.
myJSON.data[i].bank[0].money <- this part is my problem, because the bank[0].money sometimes doesn't exist.
I would like to keep the cell empty.
I tried an If but I didn't get it formatted properly, same goes for error handling.
Sub DATA()
Set RDict = CreateObject("Scripting.Dictionary")
Set dlist = CreateObject("Scripting.Dictionary")
JSON_String = Form.fromURL("exampleurl")
With CreateObject("htmlfile")
With .parentWindow
.execScript "var myJSON = " & JSON_String & ", csvstring = '';for (i = 0; i < myJSON.data.length; i++) {csvstring += myJSON.data[i].name + ',' + myJSON.data[i].bank[0].money + ',' + myJSON.data[i].location + ',' + myJSON.data[i].planneddate + ';';};"
RData = Split(.csvstring, ";")
End With
End With
For i = 0 To UBound(RData) - 1
DaData = Split(RData(i), ",")
If DaData(0) <> "null" Then RDict(DaData(0)) = DaData
Next i
Dim RSheet() As Variant
If RDict.Count > 0 Then
ReDim RSheet(2 To RDict.Count + 2, 1 To 7)
i = 0
For Each D In RDict
datalist(RDict(Da)(2)) = True
For j = 0 To 6
RSheet(i + 2, j + 1) = RDict(Da)(j)
Next j
i = i + 1
Next Da
RSData.Cells(2, 1).Resize(i, 6) = RSheet
End If
End Sub
You can handle null by using optional chaining with default nullish coalescing (#3 in example).
Something like this should work
Change myJSON.data[i]?.bank[0]?.money
To myJSON.data[i]?.bank[0]?.money ?? 'Unknown'
You can do the same with your other variables (myJSON.data[i].location and myJSON.data[i].planneddate) if they have the potential to be undefined or null as well
EDIT - Use Optional IF when optional chaining is not available
If that feature is not available in HTMLDocument's javascript maybe you can use basic conditional if?
This should work for undefined object, because undefined is == null
(myJSON.data[i].bank[0].money != null ? myJSON.data[i].bank[0].money : '-')
Instead of using Nested If statements, I was wondering if there is a way to void out parts of a string query if cell value is left blank.
Cell structure is as below:
Cell values from these parameters will get passed into vba code that queries a database.
Ideally I don't want to create an individual query for each selection type - and I have it dynamically querying from location already. I want to extend the query to include possible combinations of start, end, value >, value <, while also making it so that if cell value is left blank, then ignore that parameter. So say
SELECT *
from database
WHERE location = 'cell_loc'
AND Value >= 'cell_value'
AND Value <= 'cell_value'
AND Start >= 'cell_date'
AND End <= 'cell_date'
Now imagine that Start is left blank, meaning I want to query from first data point in the database:
I could write a nested if to handle this, but was wondering if there was a way to void out a query parameter so that I could just have a single query fed to database with different parameters changing based off cell data?
Something along the lines of:
SELECT *
from database
WHERE location = 'cell_loc'
AND Value >= 'cell_value'
AND Value <= 'cell_value'
AND Start >= 'cell_date' --> this would be voided out
AND End <= 'cell_date'
Using the coalesce() function you can put an equality condition in your WHERE clause. This is a common SQL trick to deal with null parameters or null values in the data.
SELECT *
from database
WHERE location = 'cell_loc'
AND Value >= 'cell_value'
AND Value <= 'cell_value'
AND (Start >= 'cell_date' OR Start = coalesce('cell date', Start))
AND End <= 'cell_date'
Here's a very basic example:
Sub Tester()
Dim sWhere As String, sql As String
sql = "Select * from myTable "
sWhere = ""
BuildWhere sWhere, "id = <v>", Range("A5")
BuildWhere sWhere, "pName = '<v>'", Range("B5")
BuildWhere sWhere, "pDate > '<v>'", Range("C5")
If Len(sWhere) > 0 Then
sql = sql & " where " & sWhere
Debug.Print sql
'run query
Else
'don't run if no criteria ?
End If
End Sub
'add a where clause only if `c` has a value
Sub BuildWhere(ByRef sWhere As String, test As String, c As Range)
Dim v
v = Trim(c.Value)
If Len(v) > 0 Then
If Len(sWhere) > 0 Then sWhere = sWhere & vbLf & " and "
sWhere = sWhere & Replace(test, "<v>", v)
End If
End Sub
I have a table with ranks. Now I want the ranks to have the suffixes 'St', 'nd', 'rd', 'th' in Microsoft Access. I want to know if there is a way to make that happen using access query. Thanks for the response in advance
This code from Chip Pearsons site will do the trick:
Public Function OrdinalSuffix(ByVal Num As Long) As String
Dim N As Long
Const cSfx = "stndrdthththththth" ' 2 char suffixes
N = Num Mod 100
If ((Abs(N) >= 10) And (Abs(N) <= 19)) _
Or ((Abs(N) Mod 10) = 0) Then
OrdinalSuffix = "th"
Else
OrdinalSuffix = Mid(cSfx, _
((Abs(N) Mod 10) * 2) - 1, 2)
End If
End Function
You'd write it into your query as:
SELECT MyField & OrdinalSuffix(MyField)
FROM MyTable
The formula only version is:
IIf(MyField-100*INT(MyField/100)>=10 And MyField-100*INT(MyField/100)<=14,"th",Choose(MyField-10*INT(MyField/10)+1,"th","st","nd","rd","th","th","th","th","th","th"))
Written as
SELECT MyField & IIf(MyField-100*INT(MyField/100)>=10 And MyField-100*INT(MyField/100)<=14,"th",Choose(MyField-10*INT(MyField/10)+1,"th","st","nd","rd","th","th","th","th","th","th"))
FROM MyTable
I am trying to import data to a table. Basically its a MCQs. All my questions are having superscripts and subscripts, for example X2 , and log52....
I have more than 2000 records, i have to import it. But after importing it comes in plain format, not taking powers. My DB is MYSQL (UTF-8)
Here is the example data
If log5 2, log5 (2x - 5) and log 5(2x - 7/2) are in AP , then x is equal to
after impoting it looks like above, but actually it should be
If log5 2, log5 (2x - 5) and log 5(2x - 7/2) are in AP , then x is equal to
Somebody plz suggest me how to do it
Here's a quick fix for the Subscripts:
Sub log_Script()
Dim cel As Range, rng As Range
Dim i&, k&
Dim myText$, findText$, curStr$
Set cel = Range("A1")
'myText = cel.Value
For i = 1 To Len(cel.Value)
k = k + 1
curStr = Mid(cel.Value, i, 1)
If curStr <> " " Then
findText = findText + curStr
ElseIf curStr = " " Then
findText = ""
End If
Debug.Print findText
If findText = "log" Then
If Mid(cel.Value, i + 1, 1) = " " Then
With cel.Characters(Start:=k + 2, Length:=1).Font
.Subscript = True
End With
Else
With cel.Characters(Start:=k + 1, Length:=1).Font
.Subscript = True
End With
End If
End If
Next i
End Sub
This will go through a range (set currently to be A1:A10), and for each cell, it'll look for log then take the next number and make it subscript. (Note: This is assuming all logs will have base < 10, let me know if that's not necessarily the case).
I could probably make this better, if you can post a few rows or cells from your CSV so I can see what the formatting is exacly like. (Or screenshot a part of your data, that 'd work too).
j and d both evalulate the same function however when I use If IsNull to catch any Null Values the value of d is not correctly evaluated. What is causing this?
Dim d As Integer
Dim j As Integer
j = DSum("Count", "qry_nihr_unique")
If IsNull(d = DSum("Count", "qry_nihr_unique")) Then
MsgBox "No records were found for the data criteria you entered"
GoTo ESub
Else
Me.un_p.Value = d
End If
Debug.Print "j = " & j
Debug.Print "d = " & d
j = 58
d = 0
Updated Code After Answer
Dim d
d = DSum("Count", "qry_nihr_unique")
If IsNull(d) Then
MsgBox "No records were found for the data criteria you entered"
GoTo ESub
Else
Me.un_p.Value = d
End If
After HansUp's answer below I believe this is the most efficient way to write this.
Within IsNull(), the code checks whether d is equal to the DSum() expression. It's an equality test, and nothing is assigned to d. So the value of d remains unchanged --- it was initialized as zero and remains zero.
The situation is much like this Immediate window session:
? DSum("id", "tblFoo")
134
d = 0
? (d = DSum("id", "tblFoo"))
False
? d
0
? IsNull(d = DSum("id", "tblFoo"))
False
? d
0
The following statement will assign the DSum() result to d; not test whether the two are equal:
d = DSum("id", "tblFoo")
? d
134