VBA Double as String with comma and not point - vba

I am searching for a solution to convert a double to a string, but the string should have a comma before the decimal place, not a point.
"One and a half" should look that way 1,5 (german notation).
Thanks for your help!!

A combination of CStr and Replace will do the job.
Function Dbl2Str(dbl As Double) As String
Dbl2Str = Replace(CStr(dbl), ".", ",")
End Function

Unfortunately in VBA, you can't easily write locale-independent code. That is, you can't specify a locale when you take a CStr cast.
One work around is to convert a double like 0.5 to a string and see what you end up with. If you end up with 0,5 then you're in German (etc.) locale, and you don't need to do anything else.
If you end up with 0.5 then you know you need to make a conversion. Then you just need to traverse your string, replacing dots with commas and vice versa (the vice versa bit is important in case your string has thousands delimiters). You can use Replace for that.

Following RubberDuck comment I ended up with this:
Function DblToStr(x As Double)
DblToStr = CStr(x)
If (Application.ThousandsSeparator = ".") Then
DblToStr = Replace(DblToStr, ".", "")
End If
If (Application.DecimalSeparator = ".") Then
DblToStr = Replace(DblToStr, ".", ",")
End If
End Function

something like this then
Dim somestring As String
Dim someDec As Double
someDec = 1.5
somestring = CStr(someDec)
somestring = Replace(somestring, ".", ",")
MsgBox (somestring)

Select the cells you wish to convert and run this small macro:
Sub changeIT()
For Each r In Selection
t = r.Text
If InStr(1, r, ".") > 0 Then
r.Clear
r.NumberFormat = "#"
r.Value = Replace(t, ".", ",")
End If
Next r
End Sub
Only those cells with "." in them will change and they will be Strings rather than Doubles

I checked the other answers but ended up writing my own solution to convert user inputs like 1500.5 into 1,500.50, using below code:
'
' Separates real-numbers by "," and adds "." before decimals
'
Function FormatNumber(ByVal v As Double) As String
Dim s$, pos&
Dim r$, i&
' Find decimal point
s = CStr(v)
pos = InStrRev(s, ".")
If pos <= 0 Then
pos = InStrRev(s, ",")
If pos > 0 Then
Mid$(s, pos, 1) = "."
Else
pos = Len(s) + 1
End If
End If
' Separate numbers into "r"
On Error Resume Next
i = pos - 3
r = Mid$(s, i, 3)
For i = i - 3 To 1 Step -3
r = Mid$(s, i, 3) & "," & r
Next i
If i < 1 Then
r = Mid$(s, 1, 2 + i) & "," & r
End If
' Store dot and decimal numbers into "s"
s = Mid$(s, pos)
i = Len(s)
If i = 2 Then
s = s & "0"
ElseIf i <= 0 Then
s = ".00"
End If
' Append decimals and return
FormatNumber = r & s
End Function

Related

Deleting duplicate text in a cell in excel

I was wondering how to remove duplicate names/text's in a cell. For example
Jean Donea Jean Doneasee
R.L. Foye R.L. Foyesee
J.E. Zimmer J.E. Zimmersee
R.P. Reed R.P. Reedsee D.E. Munson D.E. Munsonsee
While googling, I stumbled upon a macro/code, it's like:
Function RemoveDupes1(pWorkRng As Range) As String
'Updateby20140924
Dim xValue As String
Dim xChar As String
Dim xOutValue As String
Set xDic = CreateObject("Scripting.Dictionary")
xValue = pWorkRng.Value
For i = 1 To VBA.Len(xValue)
xChar = VBA.Mid(xValue, i, 1)
If xDic.exists(xChar) Then
Else
xDic(xChar) = ""
xOutValue = xOutValue & xChar
End If
Next
RemoveDupes1 = xOutValue
End Function
The macro is working, but it is comparing every letter, and if it finds any repeated letters, it's removing that.
When I use the code over those names, the result is somewhat like this:
Jean Dos
R.L Foyes
J.E Zimers
R.P edsDEMuno
By looking at the result I can make out it is not what I want, yet I got no clue how to correct the code.
The desired output should look like:
Jean Donea
R.L. Foye
J.E. Zimmer
R.P. Reed
Any suggestions?
Thanks in Advance.
Input
With the input on the image:
Result
The Debug.Print output
Regex
A regex can be used dynamically iterating on the cell, to work as a Find tool. So it will extract only the shortest match. \w*( OUTPUT_OF_EXTRACTELEMENT )\w*, e.g.: \w*(Jean)\w*
The Regex's reference must be enabled.
Code
Function EXTRACTELEMENT(Txt As String, n, Separator As String) As String
On Error GoTo ErrHandler:
EXTRACTELEMENT = Split(Application.Trim(Mid(Txt, 1)), Separator)(n - 1)
Exit Function
ErrHandler:
' error handling code
EXTRACTELEMENT = 0
On Error GoTo 0
End Function
Sub test()
Dim str As String
Dim objMatches As Object
Set objRegExp = CreateObject("VBScript.RegExp") 'New regexp
lastrow = ActiveSheet.Cells(ActiveSheet.Rows.Count, "A").End(xlUp).Row
For Row = 1 To lastrow
str = Range("A" & Row)
F_str = ""
N_Elements = UBound(Split(str, " "))
If N_Elements > 0 Then
For k = 1 To N_Elements + 1
strPattern = "\w*(" & EXTRACTELEMENT(CStr(str), k, " ") & ")\w*"
With objRegExp
.Pattern = strPattern
.Global = True
End With
If objRegExp.test(strPattern) Then
Set objMatches = objRegExp.Execute(str)
If objMatches.Count > 1 Then
If objRegExp.test(F_str) = False Then
F_str = F_str & " " & objMatches(0).Submatches(0)
End If
ElseIf k <= 2 And objMatches.Count = 1 Then
F_str = F_str & " " & objMatches(0).Submatches(0)
End If
End If
Next k
Else
F_str = str
End If
Debug.Print Trim(F_str)
Next Row
End Sub
Note that you can Replace the Debug.Print to write on the target
cell, if it is column B to Cells(Row,2)=Trim(F_str)
Explanation
Function
You can use this UDF, that uses the Split Function to obtain the element separated by spaces (" "). So it can get every element to compare on the cell.
Loops
It will loop from 1 to the number of elements k in each cell and from row 1 to lastrow.
Regex
The Regex is used to find the matches on the cell and Join a new string with the shortest element of each match.
This solution operates on the assumption that 'see' (or some other three-letter string) will always be on the end of the cell value. If that isn't the case then this won't work.
Function RemoveDupeInCell(dString As String) As String
Dim x As Long, ct As Long
Dim str As String
'define str as half the length of the cell, minus the right three characters
str = Trim(Left(dString, WorksheetFunction.RoundUp((Len(dString) - 3) / 2, 0)))
'loop through the entire cell and count the number of instances of str
For x = 1 To Len(dString)
If Mid(dString, x, Len(str)) = str Then ct = ct + 1
Next x
'if it's more than one, set to str, otherwise error
If ct > 1 Then
RemoveDupeInCell = str
Else
RemoveDupeInCell = "#N/A"
End If
End Function

VBA how to fix errors for strings starts with special character

I am fairly new in VBA, i am working on a project, there's small problem i am facing. I am taking newLastCmtTypeCol, newLastCmtCol, newLastNoteCol, oldLastCmtTypeCol, oldLastCmtCol, oldLastNoteCol as strings and i am only calling them in this part of code. so the error happend when one of the string start with a special character. I am taking input from sheet with alot of data. there's absolutely no way i can go through all of that data all the time. I just wanna ignore the strings start with starts with special character, so i wouldnt see any error.Here is the part of the code.
Dim newLastCmtTypeCol As String
Dim newLastCmtCol As String
Dim newLastNoteCol As String
Dim oldLastCmtTypeCol As String
Dim oldLastCmtCol As String
Dim oldLastNoteCol As String
newLastCmtTypeCol = "N"
newLastCmtCol = "O"
newLastNoteCol = "P"
oldLastCmtTypeCol = "Q"
oldLastCmtCol = "R"
oldLastNoteCol = "S"
For j = 0 To indexNew(i, 4)
If (StrComp(ws1.Range(newLastCmtTypeCol & i + j), ws1.Range(oldLastCmtTypeCol & i + j)) = 0) And _
(StrComp(ws1.Range(newLastCmtCol & i + j), ws1.Range(oldLastCmtCol & i + j)) = 0) And _
(StrComp(ws1.Range(newLastNoteCol & i + j), ws1.Range(oldLastNoteCol & i + j)) = 0) And categoryCode = 1 Then
categoryCode = 1
ElseIf IsEmpty(ws1.Range(oldLastCmtTypeCol & i + j)) And IsEmpty(ws1.Range(oldLastCmtCol & i + j)) And IsEmpty(ws1.Range(oldLastNoteCol & i + j)) Then
categoryCode = 3
Exit For
Else
categoryCode = 2
End If
Next j
Any solution?
Your issues seems to be with cells containing an error, not special characters.
If so, you probably want to filter out such cells.
You could use IsError to wrap your code, e.g.
If (Not (IsError(ws1.Range(newLastCmtTypeCol & i + j))) and _
Not (IsError(ws1.Range(oldLastCmtTypeCol & i + j))) and _
... _
) Then
Then you would be able to compare anything else.
You may want to use conversions between String and numbers, if needed.
Public Function DelInvalidCharacters(InputString As String) As String
Dim ModString As String, InvalidChars As String, Char As String
Dim i As Integer
InvalidChars = "\/:*?""<>|';#,()%&$+- "
ModString = vbNullString
For i = 1 To Len(InputString)
Char = Mid(InputString, i, 1)
If InStr(1, InvalidChars, Char) = 0 Then
ModString = ModString & Char
End If
Next i
DelInvalidCharacters = ModString
End Function
Just call this function for each variable you want to strip bad characters out of
Calling it like this
Dim this As String
this = "*this"
this = DelInvalidCharacters(this)

how to add any character in between string in access vba

i have string like "NIFTY29-12-2016CE6300.00"
and i want output as :
"NIFTY_29-12-2016_6300_CE"
the problem is first part i.e.(NIFTY) is not fixed in length it can be abcd,rdftghe or anything
And last part i.e.(6300.00) is also not fixed length it can be 123.8888888.23.88989 or anything
try this code to get position of the first digit in a string and i able to concat "_" before that the code is as follow :
If InStr(CStr(rs.Fields!Symbol), "CE") Then
StrOg = CStr(rs.Fields!Symbol)
For i = 1 To Len(StrOg)
currentCharacter = Mid(StrOg, i, 1)
If IsNumeric(currentCharacter) = True Then
GetPosChar = i
Exit For
End If
Next i
strtemp = Left(StrOg, GetPosChar) & "_" & Right() & "_"
Else
i'm acheving till this :"NIFTY_"
please help me!!!! thanks in advance
Try below. Since you have not given proper explanation of where changes has to be made. I wrote the code with some assumptions like, symbol CE is available to you, we need to truncate all decimal part, etc. You can see teh code and proceed further.
Private Sub test()
Dim StrOg As String
StrOg = "NIFTY29-12-2016CE6123.8888888"
Debug.Print "Org=" & StrOg
Debug.Print "New=" & ReFormat(StrOg)
End Sub
Private Function ReFormat(ByVal inputText) As String
Dim strNew As String
Dim charPos As Integer
Dim counter As Integer
Dim found As Boolean
'Search for 1st character from reverse and remove the 2 charters (i.e. symbol CE)
found = False
For i = Len(inputText) To 1 Step -1
If (Not IsNumeric(Mid$(inputText, i, 1))) And Mid$(inputText, i, 1) <> "." Then
charPos = i
found = True
Exit For
End If
Next i
If found Then
strNew = Left$(inputText, charPos - 2) & "_" & Right$(inputText, Len(inputText) - charPos)
Else
strNew = inputText
End If
'Search for 1st digit and if found update the string
found = False
For i = 1 To Len(strNew)
If IsNumeric(Mid$(strNew, i, 1)) Then
charPos = i
found = True
Exit For
End If
Next i
If found Then
strNew = Left$(strNew, charPos - 1) & "_" & Right$(strNew, Len(strNew) - charPos + 1)
End If
'Search for last decimal and truncate thereAfter
charPos = InStrRev(strNew, ".")
If charPos > 0 Then
strNew = Left$(strNew, charPos - 1) & "_CE"
End If
ReFormat = strNew
End Function

Extracting multiple numbers from single string cell in order to look results

I am working on small project. I have encountered a problem that I am not able to bypass. Any help would be highly appreciated.
I have the following sheets:
Sheet1
Sheet2
I need a function that extracts those 3 figures from Sheet1 (there can be more or less than 3), they are always limited by "()" and look for values in Sheet2 based on figures in column A1.
I was able to write the following code (with help of this question) for extracting figures, but I do not know how to isolate figures from single cell and look based on it in sheet2:
Edit:
I thought I will manage with the rest, but I was wrong. I would appreciate additional help to expand the code to return column B from Sheet2. Generally, logic is that function splits cell from sheet1 and then each item is looked in Sheet2. The final result of this function would be:
Test1
Test2
Test3
I have updated the code with what I tried myself.
Function onlyDigits(s As String) As String
Dim retval As String
Dim i,j As Integer
Dim TestRng as Range
Dim NoArr() as String
Dim TestRes() as String
retval = ""
s = Replace(s, ")", " ")
For i = 1 To Len(s)
If Mid(s, i, 1) >= "0" And Mid(s, i, 1) <= "9" Or Mid(s, i, 1) = " " Then
retval = retval + Mid(s, i, 1)
End If
Next
'deletes last unnecessary space
retval = Left(retval, Len(retval) - 1)
'array with results after extracting numbers
NoArr() = Split(retval, " ", , vbTextCompare)
'vlookedup range
set TestRng = Worksheets("Sheet2").Range("A1:B3")
For j = LBound(NoArr) To UBound(NoArr)
TestRes(j) = Application.WorksheetFunction.VLookup(NoArr(j), TestRng, 2, 0)
Next j
onlyDigits = TestRes
End Function
Keeping with your current method, I modified your function to return the value you need by passing in a place holder. I modified the first and second to last lines.
Function onlyDigits(s As String, pos As Integer) As String
Dim retval As String
Dim i As Integer
retval = ""
s = Replace(s, ")", " ")
For i = 1 To Len(s)
If Mid(s, i, 1) >= "0" And Mid(s, i, 1) <= "9" Or Mid(s, i, 1) = " " Then
retval = retval + Mid(s, i, 1)
End If
Next
'deletes last unnecessary space
retval = Left(retval, Len(retval) - 1)
onlyDigits = Split(retval, " ", , vbTextCompare)(pos)
End Function
To call in cell write: =onlyDigits(A1,0) the zero is the position to return
Example
Column E shows the equation used in column D
ok I solved my problem with following code:
F Function onlyDigits(s As String) As String
Dim retval As String
Dim i, j As Integer
Dim TestRng As Range
Dim NoArr() As String
Dim TestRes() As String
retval = ""
s = Replace(s, ")", " ")
For i = 1 To Len(s)
If Mid(s, i, 1) >= "0" And Mid(s, i, 1) <= "9" Or Mid(s, i, 1) = " " Then
retval = retval + Mid(s, i, 1)
End If
Next
'deletes last unnecessary space
retval = Left(retval, Len(retval) - 1)
'array with results after extracting numbers
NoArr() = Split(retval, " ", , vbTextCompare)
'vlookedup range
Set TestRng = Worksheets("Sheet2").Range("A1:B3")
For j = LBound(NoArr) To UBound(NoArr)
ReDim Preserve TestRes(j)
TestRes(j) = Application.WorksheetFunction.VLookup(CLng(NoArr(j)), TestRng, 2, False)
Next j
onlyDigits = Join(TestRes, vbNewLine)
End Function

Remove selected numbers from a comma separated list management in Excel?

This might be a little tricky, even with VBA...
I have comma separated lists in cells based on start times over 5 minutes intervals but I need to remove times that are only 5 apart.
The numbers are text, not time at this point. For example, one list would be 2210, 2215, 2225, 2230, 2240 (the start times).
In this case, 2215 and 2230 should be removed but I also need to remove the opposite numbers (i.e.,2210 and 2225) in other cases (the end times).
Someone helped me with my specs:
A cell contains times: t(1), t(2), t(3), ... t(n). Starting at time t(1), each value in the list is examined. If t(x) is less than 6 minutes after t(x-1) delete t(x) and renumber t(x+1) to t(n).
Input:
2210, 2215, 2225, 2230, 2240
Output:
column1: 2210
column2: 2240
This does what I think you require.
Option Explicit
Sub DeleteSelectedTimes()
Dim RowCrnt As Long
RowCrnt = 2
Do While Cells(RowCrnt, 1).Value <> ""
Cells(RowCrnt, 1).Value = ProcessSingleCell(Cells(RowCrnt, 1).Value, 1)
Cells(RowCrnt, 2).Value = ProcessSingleCell(Cells(RowCrnt, 2).Value, -1)
RowCrnt = RowCrnt + 1
Loop
End Sub
Function ProcessSingleCell(ByVal CellValue As String, ByVal StepFactor As Long) As String
Dim CellList() As String
Dim CellListCrntStg As String
Dim CellListCrntNum As Long
Dim InxCrnt As Long
Dim InxEnd As Long
Dim InxStart As Long
Dim TimeCrnt As Long ' Time in minutes
Dim TimeLast As Long ' Time in minutes
CellList = Split(CellValue, ",")
If StepFactor = 1 Then
InxStart = LBound(CellList)
InxEnd = UBound(CellList)
Else
InxStart = UBound(CellList)
InxEnd = LBound(CellList)
End If
CellListCrntStg = Trim(CellList(InxStart))
If (Not IsNumeric(CellListCrntStg)) Or InStr(CellListCrntStg, ".") <> 0 Then
' Either this sub-value is not numeric or if contains a decimal point
' Either way it cannot be a time.
ProcessSingleCell = CellValue
Exit Function
End If
CellListCrntNum = Val(CellListCrntStg)
If CellListCrntNum < 0 Or CellListCrntNum > 2359 Then
' This value is not a time formatted as hhmm
ProcessSingleCell = CellValue
Exit Function
End If
TimeLast = 60 * (CellListCrntNum \ 100) + (CellListCrntNum Mod 100)
For InxCrnt = InxStart + StepFactor To InxEnd Step StepFactor
CellListCrntStg = Trim(CellList(InxCrnt))
If (Not IsNumeric(CellListCrntStg)) Or InStr(CellListCrntStg, ".") <> 0 Then
' Either this sub-value is not numeric or if contains a decimal point
' Either way it cannot be a time.
ProcessSingleCell = CellValue
Exit Function
End If
CellListCrntNum = Val(CellListCrntStg)
If CellListCrntNum < 0 Or CellListCrntNum > 2359 Then
' This value is not a time formatted as hhmm
ProcessSingleCell = CellValue
Exit Function
End If
TimeCrnt = 60 * (CellListCrntNum \ 100) + (CellListCrntNum Mod 100)
If Abs(TimeCrnt - TimeLast) < 6 Then
' Delete unwanted time from list
CellList(InxCrnt) = ""
Else
' Current time becomes Last time for next loop
TimeLast = TimeCrnt
End If
Next
CellValue = Join(CellList, ",")
If Left(CellValue, 1) = "," Then
CellValue = Mid(CellValue, 2)
CellValue = Trim(CellValue)
End If
Do While InStr(CellValue, ",,") <> 0
CellValue = Replace(CellValue, ",,", ",")
Loop
ProcessSingleCell = CellValue
End Function
Explanation
Sorry for the lack of instructions in the first version. I assumed this question was more about the technique for manipulating the data than about VBA.
DeleteSelectedTimes operates on the active worksheet. It would be easy to change to work on a specific worksheet or a range of worksheets if that is what you require.
DeleteSelectedTimes ignores the first row which I assume contains column headings. Certainly my test worksheet has headings in row 1. It then processes columns A and B of every row until it reaches a row with an empty column A.
ProcessSingleCell has two parameters: a string and a direction. DeleteSelectedTimes uses the direction so values in column A are processed left to right while values in column B are processed right to left.
I assume the #Value error is because ProcessSingleCell does not check that the string is of the format "number,number,number". I have changed ProcessSingleCell so if the string is not of this format, it does change the string.
I have no clear idea of what you do or do not know so come back with more questions as necessary.
Still not clear on your exact requirements, but this might help get you started....
Sub Tester()
Dim arr
Dim out As String, x As Integer, c As Range
Dim n1 As Long, n2 As Long
For Each c In ActiveSheet.Range("A1:A10")
If InStr(c.Value, ",") > 0 Then
arr = Split(c.Value, ",")
x = LBound(arr)
out = ""
Do
n1 = CLng(Trim(arr(x)))
n2 = CLng(Trim(arr(x + 1)))
'here's where your requirements get unclear...
out = out & IIf(Len(out) > 0, ", ", "")
If n2 - n1 <= 5 Then
out = out & n1 'skip second number
x = x + 2
Else
out = out & n1 & ", " & n2 'both
x = x + 1
End If
Loop While x <= UBound(arr) - 1
'pick up any last number
If x = UBound(arr) Then
out = out & IIf(Len(out) > 0, ", ", "") & arr(x)
End If
c.Offset(0, 1).Value = out
End If
Next c
End Sub
Obviously many ways to skin this cat ... I like to use collections for this sort of thing:
Private Sub PareDownList()
Dim sList As String: sList = ActiveCell ' take list from active cell
Dim vList As Variant: vList = Split(sList, ",") ' convert to variant array
' load from var array into collection
Dim cList As New Collection
Dim i As Long
For i = 0 To UBound(vList): cList.Add (Trim(vList(i))): Next
' loop over collection removing unwanted entries
' (in reverse order, since we're removing items)
For i = cList.Count To 2 Step -1
If cList(i) - cList(i - 1) = 5 Then cList.Remove (i)
Next i
' loop to put remaining items back into a string fld
sList = cList(1)
For i = 2 To cList.Count
sList = sList + "," + cList(i)
Next i
' write the new string to the cell under the activecell
ActiveCell.Offset(1) = "'" + sList ' lead quote to ensure output cell = str type
End Sub
' If activecell contains: "2210, 2215, 2225, 2230, 2240"
' the cell below will get: "2210,2225,2240"
Note: this sample code should be enhanced w some extra validation & checking (e.g. as written assumes all good int values sep by commas & relies in implicit str to int conversions). Also as written will convert "2210, 2215, 2220, 2225, 2230, 2240" into "2210, 2040" - you'll need to tweak the loop, loop ctr when removing an item if that's not what you want.