I am looking for some help converting my formulas to VBA code.
My data is currently in Column ($T10)
I currently have rows of data similar to:
Jane Doe (doe.jane#___.com)
JOHN DOE, SR (noemail-8858)
first second DE surname surname2 (email#_______.com)
first middle surname (email#_____.net)
Formulas to get the 'normal' names:
[First Surname] =IF($C2678=1,(LEFT(B2684,SEARCH("(",B2684)-1)),"")
[first name] =IF($C4068=1,(LEFT(TRIM(B4074),FIND(" ",TRIM(B4074))-1)),"")
[middle name] =IF($C3888=1,(IF(LEN(TRIM(B3894))-LEN(SUBSTITUTE(B3894," ",""))<>3,"",LEFT(MID(TRIM(B3894),FIND(" ",TRIM(B3894))+1,99),FIND(" ",MID(TRIM(B3894),FIND(" ",TRIM(B3894))+1,99))-1))),"")
[surname] =IF($C4068=1,(TRIM(RIGHT(SUBSTITUTE(TRIM(LEFT(B4074,FIND("(",B4074)-1))," ",REPT(" ",99)),99))),"")
[email] =IF($C4068=1,(MID(TRIM(B4074),FIND("(",TRIM(B4074))+1,FIND(")",TRIM(B4074))-FIND("(",TRIM(B4074))-1)),"")
Results (edited):
| jane Doe | jane | middle | Doe | doe.jane#____.com |
| first surname | first | middle | Surname | noemail-8858 |
I've looked at both TRIM and SPLIT functions, however I haven't been able to find a way to split given the variables (, ( )) in the one cell.
I've used:
http://www.homeandlearn.org/left_and_right_functions.html
http://www.globaliconnect.com/excel/index.php?option=com_content&view=article&id=269:excel-vba-string-functions-left-right-mid-len-replace-instr-instrrev&catid=79&Itemid=475
http://www.exceltrick.com/formulas_macros/vba-split-function/
They aren't really piecing together what I need. I can get some basics, but not the more complex formulas converted to VBA.
Many thanks in advance.
This is an extension of my previous enquiry in 2014, where I was able to get the formulas.
Excel 2010 search for text in IF function - separate cell data
Short of analysing exactly what your formulas are currently doing (i assume you're happy with how they work right?) then I can't see why you can't directly convert them all?
Start point:
=IF($C3888=1,(IF(LEN(TRIM(B3894))-LEN(SUBSTITUTE(B3894," ",""))<>3,"",LEFT(MID(TRIM(B3894),FIND(" ",TRIM(B3894))+1,99),FIND(" ",MID(TRIM(B3894),FIND(" ",TRIM(B3894))+1,99))-1))),"")
Formatted more:
=IF($C3888=1,
(IF(LEN(TRIM(B3894))-LEN(SUBSTITUTE(B3894," ","")) <>3,
"",
LEFT(
MID(
TRIM(B3894),
FIND(
" ",
TRIM(B3894)
) +1,
99
),
FIND(
" ",
MID(
TRIM(B3894),
FIND(
" ",
TRIM(B3894)
)+1,
99
)
)-1
)
)
)
,"")
I think you've got a few too many Mids and Lefts than you need. This is how I've interpreted "Get the word between the first and second spaces of the trimmed value"... Is that right?
VBA-afied:
Function GetMiddleName(rgName As Range) As String
Dim intTrimmed As Integer
Dim intNoSpace As Integer
Dim stTrimmed As String
Dim intFirstSpace As Integer
Dim intSecondSpace As Integer
If rgName.Offset(-6, 1).Value = 1 Then ' This gives the "C3888" from the "B3894"
stTrimmed = Trim(rgName.Value)
intTrimmed = Len(stTrimmed)
intNoSpace = Len(Replace(rgName.Value, " ", ""))
If intTrimmed - intNoSpace <> 3 Then
GetMiddleName = ""
Exit Function
Else
intFirstSpace = InStr(1, stTrimmed, " ")
intSecondSpace = InStr(intFirstSpace + 1, stTrimmed, " ")
GetMiddleName = Mid(stTrimmed, intFirstSpace + 1, intSecondSpace - (intFirstSpace + 1))
Exit Function
End If
Else
GetMiddleName = ""
End If
End Function
Hopefully that gets you started with some ideas for the other formulas... PS the "rept" formula = "string" in VBA (I didn't know there was a rept formula! Nice one!)
That gives me these results:
"Jane Doe (doe.jane#___.com)" = "" (fails the "len - nospaces <> 3" check)
"JOHN DOE, SR (noemail-8858)" = "DOE," (might wanna add a Replace(","...) )
"first second DE surname surname2 (email#_______.com)" = "" (fails the "<>3" check)
"first middle surname (email#_____.net)" = "middle" Works Swimingly?
Related
Description of the current situation:
I have an excel file of approximately 315 columns and 4000 rows. The file contains the answers to a 300-question questionnaire. The data format is as follows:
(Headers) A | B | C | D | E | F | Q.1 | Q.2 | ... | Q.300 |
(FirstRow) Info of first participant | AnswerCode for every Q |
The columns A to F contain contain info on every participant, while the columns Q.1 to Q.300 contain the respective answer code to each question. After storing the file as a large DataTable:
I need to load all 4000 rows on an existing database table, but before I do that I must edit the data format. The end result must become:
ParticipantCode | QuestionCode | AnswerCode | DateOfRegistration
00001 | 0001 | 1234567 | yyyy-MM-dd HH:mm:ss
... | ... | ... | ...
00001 | 0300 | 1234567 | yyyy-MM-dd HH:mm:ss
00002 | 0001 | 1234567 | yyyy-MM-dd HH:mm:ss
... | ... | ... | ...
04000 | 0300 | 1234567 | yyyy-MM-dd HH:mm:ss
So every row of the original ExcelDataTable is transformed into 300 rows in the FinalDataTable. In this way, the FinalDataTable will have about 1.2 million rows.
What Have I implemented so far:
Private Function MyFunction()
For Each ExcelRow As DataRow In ExcelDataTable.Rows
For Each ExcelColumn As DataColumn In ExcelDataTable.Columns
QuestionCodeFound = False
ExcelColumnNameRaw = ExcelColumn.ColumnName.ToString.Trim
If ExcelColumnNameRaw.StartsWith("Q") Then
' Correct the headers
ExcelColumnSplit = ExcelColumnNameRaw.Split("#")
ExcelColumnName = String.Concat(ExcelColumnSplit(0), ExcelColumnSplit(1))
SelectedRowFromDT = QuestionCodeAndQuestionIDDataTable.Select("QuestionID = '" + ExcelColumnName + "'")
' Search for "_", because some questions are different
If SelectedRowFromDT.Length > 0 Then
QuestionCodeFound = True
Else
Dim ExcelColumnSplitForMult As String()
ExcelColumnSplitForMult = ExcelColumnName.Split("_")
SelectedRowFromDT = QuestionCodeAndQuestionIDDataTable.Select("QuestionID = '" + ExcelColumnSplitForMult(0).ToString + "'")
If SelectedRowFromDT.Length > 0 Then
QuestionCodeFound = True
End If
End If
If QuestionCodeFound Then
Dim QuestionCode As String
Dim QuestionTypeDataTable As DataTable
Dim QuestionType As String
' Get the Question Type from the respective table
QuestionType = String.Empty
QuestionCode = SelectedRowFromDT(0).Item("QuestionCode").ToString
QuestionTypeDataTable = SearchInSql(My.Settings.ConnectionString, SQLString)
If QuestionTypeDataTable.Rows.Count > 0 Then
QuestionType = QuestionTypeDataTable.Rows(0).Item(0).ToString.Trim
End If
' Fix the Date Format
DateRaw = ExcelRow.Item(1).ToString
DateSplit = DateRaw.Split("/")
If DateSplit(0).Length = 1 Then
DateSplit(0) = String.Concat("0", DateSplit(0))
End If
If DateSplit(1).Length = 1 Then
DateSplit(1) = String.Concat("0", DateSplit(1))
End If
DateText = String.Concat(DateSplit(0), "/", DateSplit(1), "/", DateSplit(2))
DateRegistration = DateTime.ParseExact(DateText, "MM/dd/yyyy", CultureInfo.InvariantCulture)
DateRegistrationReformed = DateRegistration.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)
DateRegFinal = DateTime.ParseExact((DateRegistrationReformed + " " + "10:00:00").ToString, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)
Dim AnswerValue As String
Dim AnswerCode As String
Dim AnswerCodeDataTable As DataTable
Dim QuestionWasAnswer As String
Dim AnswerValueRow() As DataRow = ExcelDataTable.Select("ParticipantCode = '" + ExcelRow.Item(2).ToString + "'")
AnswerCodeDataTable = New DataTable
AnswerValue = ""
QuestionWasAnswer = "0"
' Complete "QuestionWasAnswer" field for all questions and retrieve the AnswerCode for the answer given by each participant
If AnswerValueRow.Length > 0 And AnswerValueRow(0).Item(ExcelColumnNameRaw).GetType IsNot GetType(DBNull) Then
If Not (QuestionType.Equals("02") Or QuestionType.Equals("03")) Then
AnswerValue = AnswerValueRow(0).Item(ExcelColumnNameRaw)
QuestionWasAnswer = "1"
ElseIf QuestionType.Equals("02") Or QuestionType.Equals("03") Then
Dim ExcelColumnSplitForMultSecond As String()
Dim MultAnswerValue As String
ExcelColumnSplitForMultSecond = ExcelColumnName.Split("_")
MultAnswerValue = AnswerValueRow(0).Item(ExcelColumnNameRaw).ToString.Trim
AnswerValue = ExcelColumnSplitForMultSecond(1).ToString
If MultAnswerValue.Equals("1") Then
QuestionWasAnswer = "1"
ElseIf MultAnswerValue.Equals("2") Then
QuestionWasAnswer = "2"
End If
End If
' Search in the Answers table for the existing AnswerCode
SQLString = String.Format("SELECT Answers.AnswerCode
FROM Answers
WHERE Answers.QuestionCode = '{0}'
AND (Answers.AnswerNumber = '{1}' OR Answers.Answer = '{1}')", QuestionCode, AnswerValue)
AnswerCodeDataTable = SearchInSql(My.Settings.ConnectionString, SQLString)
If AnswerCodeDataTable.Rows.Count > 0 Then
AnswerCode = AnswerCodeDataTable.Rows(0).Item(0).ToString
FormattedDataTable.Rows.Add(ParticipantAnswerCode, ExcelRow.Item(2), QuestionCode, AnswerCode, QuestionWasAnswer, DateRegFinal)
ParticipantAnswerCode = Convert.ToInt32(ParticipantAnswerCode + 1).ToString.PadLeft(ParticipantAnswerCodeFieldLength, "0")
Else
' If a given answer does not exist, save it in the respective table and then try again
Dim AnswerCodeLength = GetLengthFromSqlDataBase(My.Settings.ConnectionString, "Answers", "AnswerCode")
Dim NextAnswerCode = CalculateNextAnswerCode(AnswerCodeLength)
Dim NestAnswerNumber = CalculateNextAnswerNumber(QuestionCode)
SaveNewAnswer(NextAnswerCode, QuestionCode, NestAnswerNumber, AnswerValue)
SQLString = String.Format("SELECT Answers.AnswerCode
FROM Answers
WHERE Answers.QuestionCode = '{0}'
AND Answers.Answer = '{1}'", QuestionCode, AnswerValue)
AnswerCodeDataTable = SearchInSql(My.Settings.ConnectionString, SQLString)
If AnswerCodeDataTable.Rows.Count > 0 Then
AnswerCode = AnswerCodeDataTable.Rows(0).Item(0).ToString
FormattedDataTable.Rows.Add(ParticipantAnswerCode, ExcelRow.Item(2), QuestionCode, AnswerCode, QuestionWasAnswer, DateRegFinal)
ParticipantAnswerCode = Convert.ToInt32(ParticipantAnswerCode + 1).ToString.PadLeft(ParticipantAnswerCodeFieldLength, "0")
End If
End If
End If
End If
End If
Next
Next
Return FormattedDataTable
End Function
After that, I bulk insert the FinalDataTable on the DB.
The problem I am facing:
Using the current program I built, every row in the ExcelDataTable takes about 40 seconds to transform into 300 rows in the FinalDataTable. If I try to load all 4000 rows, it will take more than 40 hours to transform the entire datatable. I need to find a faster way to do this.
As mentioned, there isn't much to go off of on this with what has been provided.
I'm sure there are more helpful fixes to consider but I wanted to put my two cents in about the For Loops.
I recommend switching the
For Each
statements with
For i as integer = 0 to ExcelDataTable.Rows.Count - 1
I've read that For Each is not as performance-friendly as it gathers each "row" as a collection, therefore increasing the overhead per loop.
Here is a SO post about this subject:
Major difference between 'for each' and 'for' loop in .NET
Not sure if that will make a difference for you but thought I would recommend it anyway.
I am trying to create 2 variable from a string in the cell. cell string is "Mr Jonhattan Smith Sun". Value1 I want as "Jonhattan" and value2 as "Smith Sun". I have the following codes but doesn't seem to work properly. any Help Please
value1 = Left(ThirdTable.Rows(10).Cells(2).Range.text, Len(ThirdTable.Rows(10).Cells(2).Range.text) - InStrRev(ThirdTable.Rows(10).Cells(2).Range.text, " "))
value2 = Right(ThirdTable.Rows(10).Cells(2).Range.text, Len(ThirdTable.Rows(10).Cells(2).Range.text) - InStrRev(ThirdTable.Rows(10).Cells(2).Range.text, " ") + 1)
Try:
ss = Split(ThirdTable.Rows(10).Cells(2).Range.Text, " ")
Value1 = ss(1)
Value2 = ss(2) & " " & ss(3)
Given that
ThirdTable.Rows(10).Cells(2).Range.Text Gives you Mr Jonhattan Smith Sun
Demo:
If that Weird Letter comes up like in the Demo use:
Value2 = ss(2) & " " & Left(ss(3), Len(ss(3)) - 1)
Check out this list. I need each one turned into a variable and set equal to 0. Example:
;1-Methyoxy-2-Propanol would be:
$OneMethoxyTwoPropanol = 0
;and 1,2-BUTADIENE would be:
$OneTwoButadiene = 0
Assigning them to a variable wouldn't be a problem, but there are 1500 of them.
If i had to do this work i'll make it this way :
I'll change the case of each word :
Regex to change to sentence case
Make a "SearchAndReplace" :
1 -> One
2 -> Two
...
{underscore} -> ''
{space} -> ''
...
Then in a soft like SublimeText i'll add a $ in front of each line and a = 0 at the end. With the help of the Ctrl+Shift+L
Maybe you could use a regex to help you in the "SearchAndReplace" thing.
Something like this?
Local $sFilePath = #DesktopDir & "\test.ini"
Local $aArray = IniReadSection($sFilePath, "Variables")
Local $aVariablesArray[UBound($aArray)][2]
For $i = 1 To $aArray[0][0]
$aVariablesArray[$i][0] = $aArray[$i][0]
$aVariablesArray[$i][1] = $aArray[$i][1]
Next
For $i = 1 To UBound($aVariablesArray) -1
MsgBox(0, "", "Variable: " & $aVariablesArray[$i][0] & #CRLF & "Value: " & $aVariablesArray[$i][1])
Next
Your ini file should look like this
[Variables]
firstvariable=0
secondvariable=0
etc...=0
To create an ini file just open notepad, write down the file and then save it as .ini.
You can use RegEx to rename each file according to your needs.
The array created is a 2d array. It is needed to store the variable's name and value.
stringreplace and assign will do the job. If the amount of number to word replacements becomes too large you may consider storing those rather than nesting replace functions.
$sStr = "1-Methyoxy-2-Propanol" & #LF & "1,2-BUTADIENE"
$sStr = stringreplace(stringreplace(stringreplace(stringreplace($sStr , "," , "-") , "1-" , "One") , "2-" , "Two") , "-" , "")
$aStr = stringsplit($sStr , #LF , 2)
For $i = 0 to ubound($aStr) - 1
Assign($aStr[$i] , 0)
Next
msgbox(0, '' , Eval("OneMethyoxyTwoPropanol") & #LF & Eval("OneTwoBUTADIENE"))
I simply need to turn them into a variable and set them equal to 0.
As per Documentation - Intro - Arrays:
An Array is a variable containing a series of data elements. Each element in this variable can be accessed by an index number which relates to the position of the element within the Array - in AutoIt the first element of an Array is always element [0]. Arrays elements are stored in a defined order and can be sorted.
Removes lines containing list names (; List I etc.) and empty (or less than three character-) lines (as per $g_sRegexFilter). Stores remaining lines to 2D array-elements. Example:
#include <StringConstants.au3>
#include <FileConstants.au3>
#include <Array.au3>
Global Enum $ITEM_NAME, _
$ITEM_VALUE
Global Const $g_sFilePath = 'C:\list.txt'
Global Const $g_sFileNewline = #CRLF
Global Const $g_sRegexFilter = '(?m)^(.{0,2}\v)|(;.*\v)$'
Global Const $g_sItemHeader = 'name|value'
Global $g_sFileText = ''
Global $g_aFileItems
$g_sFileText = _TextFromFile($g_sFilePath)
$g_sFileText = StringRegExpReplace($g_sFileText, $g_sRegexFilter, '')
$g_aFileItems = StringSplit($g_sFileText, $g_sFileNewline, $STR_ENTIRESPLIT + $STR_NOCOUNT)
_ArrayColInsert($g_aFileItems, $ITEM_VALUE)
For $i1 = 0 To UBound($g_aFileItems) - 1
$g_aFileItems[$i1][$ITEM_VALUE] = 0
Next
_ArrayDisplay($g_aFileItems, '$g_aFileItems', '', 0, Default, $g_sItemHeader)
Func _TextFromFile(Const $sFile)
Local $hFile = FileOpen($sFile, $FO_READ + $FO_UTF8_NOBOM)
Local Const $sData = FileRead($hFile)
FileClose($hFile)
Return $sData
EndFunc
Returns:
1-Methoxy-2-Propanol | 0
1,2-BUTADIENE | 0
2-Diethyl aminoethanol | 0
2-ETHYL HEXANOL | 0
2-ETHYL HEXYL ACRYLATE | 0
2-Ethyl hexyl lights | 0
2-Ethyl phenol | 0
2-Ethylsuccionitrile | 0
2-Methyl piperidine | 0
2-Methyl-2-Butene nitrile | 0
2-Methyl-2-Pentenal | 0
2-Methyl-3-Butene nitrile | 0
2-Methylglutaronitrile | 0
...
As:
$g_aFileItems[ x ][$ITEM_NAME]
$g_aFileItems[ x ][$ITEM_VALUE]
Add additional columns using _ArrayColInsert().
... as there are 1500 of them.
Consider using SQLite. Related.
I am using Notepad++, so I'm not sure if this would work with any other IDEs/Notepads. I'm going to be using 1-Methoxy-2-Propanol for my following example.
I learned not to start variables with numbers, so I needed to replace them with words. 1-Methoxy-2-Propanol contains a 1 and a 2, we need to change these to One and Two.
Starting product:
1-Methoxy-2-Propanol
Press Ctrl + F and move to the replace tab. In the "Find what:" box, type 1. In the "Replace with:" box, type One, then press "replace all" (not just "replace"). Do this for numbers zero (0) through nine (9). Now, your product will look like this:
One-Methoxy-Two-Propanol
Next we need to get rid of the dashes. In the Replace tab, inside of the "Find what:" box, type - and in the "Replace With:" box, backspace completely so there is nothing there, then press "Replace All". Now, your product will look like this:
OneMethoxyTwoPropanol
There are other products that include comas and parenthesis, so simply find and replace these like above.
We need to add $ to the beginning of each word. Press Ctrl + F again and go to the Replace tab. In the "Find what:" box, type ^ which symbolizes the start of a new line. In the "Replace with:" box, type $ and press "Replace All". This will make your product look like:
$OneMethoxyTwoPropanol
We need to set all of these variables zero! Go back to the replace tab. In the "Find what:" box type \r. In the "Replace with:" box, type = 0. Note the space before the equal sign. Press "Replace All". Your product will look like this:
$OneMethoxyTwoPropanol = 0
Your file should have started like this:
1-Methoxy-2-Propanol
1,2-BUTADIENE
2-Diethyl aminoethanol
2-ETHYL HEXANOL
2-ETHYL HEXYL ACRYLATE
2-Ethyl hexyl lights
2-Ethyl phenol
2-Ethylsuccionitrile
2-Methyl piperidine
2-Methyl-2-Butene nitrile
2-Methyl-2-Pentenal
2-Methyl-3-Butene nitrile
2-Methylglutaronitrile
2-Pentene nitrile
2,4,7,9-Tetramethyl-5-decyne-4
And ended up like this:
$OneMethoxyTwoPropanol = 0
$OneTwoBUTADIENE = 0
$TwoDiethylaminoethanol = 0
$TwoETHYLHEXANOL = 0
$TwoETHYLHEXYLACRYLATE = 0
$TwoEthylhexyllights = 0
$TwoEthylphenol = 0
$TwoEthylsuccionitrile = 0
$TwoMethylpiperidine = 0
$TwoMethylTwoButenenitrile = 0
$TwoMethylTwoPentenal = 0
$TwoMethylThreeButenenitrile = 0
$TwoMethylglutaronitrile = 0
$TwoPentenenitrile = 0
$TwoFourSevenNineTetramethylFivedecyneFour = 0
I am trying to extract partial information from a list, for which the information comes in a specific format (this list doesn't come in a spreadsheet):
A BUYS: PRODUCT # 85 / B SELLS
B BUYS: PRODUCT # 500 / C SELLS
B BUYS: PRODUCT # 200 / A SELLS
If I paste the entire list into a textbox, is it possible to extract only part of the data from the textbox?
For the first line of the list "A BUYS: PRODUCT # 85 / B SELLS", I would like to separate: "A" ; "Product" ; "85" ; "B", and put them into different cells in the same row.
Any help would be really appreciated. Or maybe you have a simpler method to achieve this?
Something like below? either that or you could try text to columns
temp = split("A BUYS: PRODUCT # 85 / B SELLS"," ")
A = temp(0)
Product = temp(2)
qty = temp(4)
B = temp(6)
If you would like to iterate through the list, perhaps something like the below?
Sub splitMyList()
Dim iRow As Integer
iRow = 1
Do While Cells( iRow, 1) <> ""
temp = Split( Cells( iRow, 1 ), " ")
Cells( iRow, 2 ) = temp(0)
Cells( iRow, 3 ) = temp(2)
Cells( iRow, 4 ) = temp(4)
Cells( iRow, 5 ) = temp(6)
iRow = iRow + 1
Loop
End Sub
This assumes you pasted your list in column A starting on row 1. Just change the figures if needed. Hope this helps
I have three cells IN , OUT& OverTime all formatted as [h]:mm,
The OT cell has this forumla,
=ROUND(IF(((D10-C10)+(D11-C11))*24>7,((D10-C10)+(D11-C11))*24-7,0)/24*96,0)/96
that calculates OT to the 1/4 hr
8 C D E F
9 IN OUT O/T C/T
10 7:30 AM 12:15 PM 1:45
11 1:00 PM 5:00 PM
When the employee clockes out for the day, I would like the following
code to run;
Dim CT As Date
Title = "Add to CompTime from OverTime"
If Range("E10") > 0 Then
CT = InputBox("Add Hours to CompTime?", Title)
If CT > 0 Then Range("F10").Value = ("E10" - CT)
Else: Range("F10").Value = " "
End If
End Sub
Everthing seems to work except;
If CT > 0 Then Range("F10").Value = ("E10" - CT)
I know it is a formatting issue but I am unable resolve the issue.
Always try to do the explicit referencing to ranges whether they are on sheets or modules. Use proper properties of the objects - in your case you have a range E10 which is not qualified. Assuming you are working in Sheet 1:
Option Explicit
'--Beginning of your Subroutine...
Dim Title as String '-- assuming
Dim CT As Date '-- are you sure you want to have a date here?
Title = "Add to CompTime from OverTime"
If Sheets(1).Range("E10").Value > 0 Then '-- assuming it's a valid date here...
CT = InputBox("Add Hours to CompTime?", Title)
'-- assume your CT = 2:45 and OT = 75:30
'-- use the following as mentioned in my comment
If CT > 0 Then
Application.Text( Sheets(1).Range("F10").Value ,"[h]:mm") =
Application.Text(Sheets(1).Range("E10").Value, "[h]:mm") - Application.Text(CT, "[h]:mm")
Else
Sheets(1).Range("F10").Value = " "
End If
End If
End Sub
PS: If you are trying to calculate hours, you may have just use VBA to do the entire calculations.. =)