I currently have a VBA sub routine in an Excel sheet that prompts the user with an input box, inserts the data into a cell, and automatically advances to the cell below if the entire string will not fit into a single cell. It works, but the code will advance to the next line even if it has to split a word to do it. I do not want this, and I would appreciate some suggestions on how to improve my code so that Excel not only advances cells, but advances cells with words that don't get cut off.
Sub AutoCellAdvance()
If bolEditMode = True Then
Exit Sub
End If
Dim str As String, x As Integer, y As Integer
intPlaceholder = Sheet1.Range("AE1").Value
If IsEmpty(ActiveCell) Then
str = InputBox("Enter Description of Activities (Max 192 characters)", "Incidents, Messages, Orders, Etc.")
y = 0
For x = 1 To Len(str) Step 64
ActiveCell.Offset(y, 0) = "" & Mid(str, x, 64)
If Len(str) > 64 And Len(str) <= 128 And intPlaceholder = 6 Then
ActiveCell.Offset(1, -4).Resize(1, 4).Value = Chr(151) & Chr(151)
End If
If Len(str) > 128 And Len(str) < 192 And intPlaceholder = 6 Then
ActiveCell.Offset(1, -4).Resize(2, 4).Value = Chr(151) & Chr(151)
End If
If Len(str) >= 192 And intPlaceholder = 6 Then
ActiveCell.Offset(1, -4).Resize(3, 4).Value = Chr(151) & Chr(151)
End If
y = y + 1
Next
Else
Exit Sub
End If
End Sub
Private Sub Worksheet_SelectionChange(ByVal target As Range)
'Incident, Messages, Orders, Etc. Input
Dim rng As Range
Set rng = Intersect(target, Range("N12,N13,N14,N15,N16,N17,N18,N19,N20,N21,N22,N23,N24,N25,N26,N27,N28,N29,N30,N31,N32,N33,N34,N35,N36,N37,N38,N39,N40,N41,N42,N43,N44"))
If rng Is Nothing Then
Exit Sub
ElseIf target.Count > 14 Then
Exit Sub
Else
Dim cl As Range
For Each cl In rng
AutoCellAdvance
Next cl
End If
Selection.Font.Name = "arial"
Selection.Font.Size = 10
End Sub
Try this. The code below splits the input string, into an array of strings based on the delimiter " ". It then loops through the string array. Whenever it reaches a size of 64 it goes to the next line.
Sub AutoCellAdvance()
Dim strTemp As String
Dim arrStrings() As String
Dim i As Integer
Dim strNew As String
Range("A1").Activate
strTemp = InputBox("Enter Description of Activities (Max 192 characters)", "Incidents, Messages, Orders, Etc.")
'splits the string based on the delimeter space
arrStrings = Split(strTemp, " ")
strNew = ""
'loops through the strings
For i = LBound(arrStrings) To UBound(arrStrings)
If Len(strNew + arrStrings(i)) < 64 Then
'as long as its smaller than 64 its adds the string to the rest of the strings
strNew = strNew + arrStrings(i) + " "
Else
'if its larger than 64 then it prints the value in the active cell and goes down a row
ActiveCell = strNew
strNew = arrStrings(i)
Range(Cells(ActiveCell.Row + 1, ActiveCell.Column), Cells(ActiveCell.Row + 1, ActiveCell.Column)).Activate
End If
Next i
ActiveCell = strNew
End Sub
Also here's an article I've written about string processing on my blog. It also talks about splitting strings. String Processing
Related
I´m trying to find a way to use Instr to work only with words that have a specific font.
I´m currently using a code that allows me to find differences between two paragraphs and show the changes on another column by chainging the words that are the same to the color green.
The problem is that when using Instr it only finds the first occurence of a word. But with the paragraphs I´m using, the words appear multiple times:
myLastRow = Cells(Rows.Count, "G").End(xlUp).Row
For I = 3 To myLastRow
strTemp = " "
WordsA = Split(Range("F" & I).Text, " ")
Debug.Print WordsA
WordsB = Split(Range("H" & I).Text, " ")
Debug.Print WordsB
For ndxB = LBound(WordsB) To UBound(WordsB)
For ndxA = LBound(WordsA) To UBound(WordsA)
If StrComp(WordsA(ndxA), WordsB(ndxB), vbTextCompare) = 0 Then
FindText = WordsA(ndxA)
Debug.Print FindText
Set TextRange = Range("H" & I)
fontColor = 4
'FindText.Font.ColorIndex = fontColor
For Each part In TextRange
lenOfpart22 = InStr(1, TextRange, FindText, 1)
lenPart = Len(FindText)
part.Characters(Start:=lenOfpart22, Length:=lenPart).Font.ColorIndex = fontColor
Next part
Exit For
End If
Next ndxA
Next ndxB
Next I
What I need is for the Instr to only search the word if its fond is 0 (black).
TextRange is the paragraph. Usually more than 500 caracters long
FindText is the word that I´m searching
This is an example of the problem I´m having:
In this paragraph you can see how some words appear in green. These are the words that are the same on the two paragraphs that I´m comparing (columns F and G). There are some words such as: aeqqw, SAWR, SIGMEL... that are different. The problem is that Instr only finds the first occurrence of a word. That´s why I want a condition were if the word is green, it won´t be considered in the instr and will move on to find the next word.
In the picture you can see that the first "El" is in green, but the rest aren´t. This is because when it searches for the second, thrid, fourth... "el" it comes back to the first "el".
Please, use the next function to do what (I understood) you need (playing with arrays...):
Sub WordsComp(cell1 As Range, cell2 As Range) 'punctuation characters eliminated
Dim arr1() As String, arr2() As String, arrMtch() As String, mtch, El
Dim strArr As String, i As Long, cleanWord As String, endPlus As Long
arr1 = Split(cell1.value): arr2 = Split(cell2.value) 'split the two cells content by words
For Each El In arr1 'iterate between the first cell words
For i = 0 To UBound(arr2)
cleanWord = EndingCharsOut(CStr(El))
endPlus = Len(cleanWord) - Len(El)
If EndingCharsOut(CStr(arr2(i))) = cleanWord Then 'when a match has been found:
arrMtch = Split(cell2, , i + 1, vbTextCompare) 'split the range only up to the searched word (plus the rest of the string)
'eliminate the last element of the array:
arrMtch(UBound(arrMtch)) = "##$%": arrMtch = filter(arrMtch, "##$%", False)
strArr = Join(arrMtch, "|") 'join the array elements to obtain the necessary start, before the word to be colored
cell2.Characters(start:=Len(strArr) + 2, length:=Len(El) + endPlus).Font.Color = vbGreen '+ 2 because of the 1D zero based array and a space
End If
Next i
Next
End Sub
Private Function EndingCharsOut(strMatch As String) As String 'eliminates ending punctuation characters (,.?:;)
With CreateObject("Vbscript.RegExp")
.Pattern = "[.,/?:;]$"
If .test(strMatch) Then
EndingCharsOut = (.Replace(strMatch, ""))
Else
EndingCharsOut = strMatch
End If
End With
End Function
The above Sub should be called by the next one:
Sub testWordsCompare()
Dim ws As Worksheet, rng As Range, lastR As Long, i As Long
Set ws = ActiveSheet
lastR = ws.Range("F" & ws.rows.count).End(xlUp).row
Set rng = ws.Range("F2:G" & lastR)
rng.Columns(2).Font.Color = 0 'make the font color black (default)
Application.EnableEvents = False: Application.ScreenUpdating = False
For i = 1 To rng.rows.count
WordsComp rng.rows(i).cells(1, 1), rng.rows(i).cells(1, 2)
Next i
Application.EnableEvents = True: Application.ScreenUpdating = True
MsgBox "Ready..."
End Sub
The function compares words even containing punctuation (comma, dot, question mark, ":", ";") at the end...
A faster solution but not so compact and easy to be understood, would be the next classic one:
Sub compWdClassic(cell1 As Range, cell2 As Range)
Dim iStart1 As Long, iEnd1 As Long, iStart2 As Long, oldStart As Long, strWd As String
Dim boolEnd As Boolean, boolOut As Boolean, i As Long, frstW As Boolean, midleW As Boolean
iStart1 = 1 'initialize starting position for Cell1 string
Do While Not boolEnd
iEnd1 = InStr(iStart1, cell1, " ", vbBinaryCompare) 'determine the ending of the word to be returned
strWd = Mid(cell1, iStart1, IIf(iEnd1 > 0, iEnd1 - iStart1, Len(cell1) - iStart1 + 1)) ' extraxting the word to be checked
If iEnd1 > 0 Then iStart1 = iEnd1 + 1 Else: boolEnd = True 'determine if is it about the last word (or not)...
strWd = EndingCharsOut(strWd) 'clean the word ending
midleW = False: boolOut = False: iStart2 = 1 'initialize the necessary variables
Do While Not boolOut 'loop in cell2 value string
If Not frstW And iStart2 = 1 Then 'if not a first word has been found:
iStart2 = InStr(IIf(iStart2 = 0, 1, iStart2), cell2, strWd & " ", vbBinaryCompare) 'check against a word without a space in front
If iStart2 > 0 Then frstW = True 'first word in the sentence. If such a word found, make the boolean variable True
Else
oldStart = iStart2 'memorize the previous value of iStart2
iStart2 = InStr(iStart2 + 1, cell2, " " & strWd & " ", vbBinaryCompare) 'search if a word with spaces at both sides
If iStart2 > 0 Then midleW = True 'if founded, make the boolean variable True
If oldStart > 0 And midleW Then 'if nothing found before, but a pevious word with spaces of both sides has been found:
If iStart2 = 0 Then iStart2 = InStr(oldStart, cell2, " " & strWd, vbBinaryCompare): _
If iStart2 > 0 And iStart2 + Len(strWd) = Len(cell2) Then boolOut = True Else: iStart2 = 0: boolOut = True: 'if the last word or only part of a word
ElseIf oldStart = 0 And Not midleW Then
If iStart2 = 0 Then iStart2 = InStr(oldStart + 1, cell2, " " & strWd, vbBinaryCompare):
If iStart2 > 0 Then boolOut = True: ' last word and loop must be exited
End If
End If
If iStart2 > 0 Then
cell2.Characters(iStart2 + IIf(boolOut, 1, IIf(frstW And Not midleW, 0, 1)), Len(strWd)).Font.Color = vbRed 'do the job
iStart2 = iStart2 + Len(strWd) + 1 'increment the variable for the next search
Else
If (frstW And Not boolOut) Or (Not frstW And Not midleW And Not boolOut) Then Exit Do 'exiting loop if conditions are met
End If
Loop
Loop
End Sub
It uses the same EndingCharsOut function to clear punctuation characters. You only must call this Sub instead of previous. I mean, replace:
WordsComp rng.rows(i).cells(1, 1), rng.rows(i).cells(1, 2)
in testWordsCompare sub with:
compWdClassic rng.rows(i).cells(1, 1), rng.rows(i).cells(1, 2)
Please, send some feedback after testing them...
I have a cell with a full address. I want to copy street name and street number to the next cell. E.g "STRANDVEJEN 100 MIDDELFART DENMARK"
In this example I want "STRANDVEJEN 100" to be copied.
Currently everything is being copied to the next cell.
But I need advice how to continue
Sub move()
Range("C3:C2000").Copy Range("D3:D2000")
Do until......
End Sub
I need help with the do until part.
Try:
Option Explicit
Sub CopyYes()
Dim arr As Variant, strSplit As Variant
Dim i As Long
With ThisWorkbook.Worksheets("Sheet1")
arr = .Range("C3:C2000")
For i = LBound(arr) To UBound(arr)
strSplit = Split(arr(i, 1), " ")
.Range("E" & i + 2).Value = strSplit(0)
.Range("F" & i + 2).Value = strSplit(1)
Next i
End With
End Sub
Results:
Here is a function that will perform your 'cut' on a string. It's not pretty and it could no doubt be better written with a simple reg-ex command but..
Function untilnumeric(txt As String) As String
Dim i As Long
Dim started As Boolean
For i = 1 To Len(txt)
If Asc(Mid(txt, i, 1)) > 47 And Asc(Mid(txt, i, 1)) < 58 Then
started = True
Else
If started = True And Asc(Mid(txt, i, 1)) = 32 Then
untilnumeric = Left(txt, i - 1)
Exit For
End If
End If
Next
End Function
You could use it like this to perform it on column C - copying the result to D:
Range("D3:D2000").Value = Range("C3:C2000").Value
For Each c In Range("D3:D2000").Cells
c.Value = untilnumeric(c.Value)
Next
Note: Amended slightly to pick up any letters within the number part. eg.100A
I have tried this code which works fine for a cell that only contain number:
Sub IncreaseCellValue()
'Add 1 to the existing cell value
Range("A1").Value = Range("A1") + 1
End Sub
How can I do something similar if the cell has text and a number. For example, I have "Apple 1" and I want to "increase" the cell text to "Apple 2" and next time I run the macro I want "Apple 3".
Here's another way you could solve this problem:
Sub IncreaseCellValue()
Dim value As Variant
'Add 1 to the existing cell value
If IsNumeric(Range("A1").value) Then
Range("A1").value = Range("A1") + 1
Else
value = Split(Range("A1").value, " ")
Range("A1").value = value(0) & " " & (CInt(value(1)) + 1)
End If
End Sub
It will cover the 2 cases you presented in your question but not every scenario you could throw at it.
Try using the following function
Sub IncreaseCellValue()
'Add 1 to the existing cell value
Range("A1").Value = Replace(Range("A1").Value2, CleanString(Range("A1")), vbNullString) & CInt(CleanString(Range("A1").Value2)) + 1
End Sub
Function CleanString(strIn As String) As String
Dim objRegex
Set objRegex = CreateObject("vbscript.regexp")
With objRegex
.Global = True
.Pattern = "[^\d]+"
CleanString = .Replace(strIn, vbNullString)
End With
End Function
please check:
Option Explicit
Sub IncreaseCellValue()
'Add 1 to the existing cell value
Dim rg As Range
Set rg = Cells(Rows.Count, "A").End(xlUp)
Range("A1" & ":" & rg.Address).AutoFill Destination:=Range("A1" & ":" & rg.Offset(1, 0).Address), Type:=xlFillDefault
End Sub
Or you may try something like this...
Function GetNumber(ByVal rng As Range) As Long
Dim i As Long
For i = Len(rng.Value) To 1 Step -1
If IsNumeric(Mid(rng.Value, i, 1)) Then
GetNumber = GetNumber & Mid(rng.Value, i, 1)
Else
Exit For
End If
Next i
End Function
Sub IncrementNumber()
Dim num As Long
num = GetNumber(Range("A1"))
Range("A1").Value = Replace(Range("A1").Value, num, num + 1)
End Sub
I have this.
3
3.1
3.2
3.3
3.4
4
4.1
NULL
NULL
NULL
NULL
5
I would like this.
3
3.1
3.2
3.3
3.4
4
4.1
4.A
4.B
4.C
4.D
5
I have about 22k rows to fill in. How can I do it? I put together the code below, bu tit doesn't do what I want. I need to start over with prior number + 'A' whenever a new blank cell is found.
This is what I have so far, but it doesn't work.
Sub AlphaFill()
Dim Cell, CellChars
Dim Default, Prompt, Title
Dim rangeSelected As Range
Dim UpperCase As Boolean
On Error Resume Next
Set rangeSelected = Range("F1:F21400")
For Each Cell In rangeSelected
If Cell.Value <> "" Then
i = 1
End If
If Cell.Value = "" Then
CellChars = Chr(64 + i)
If Not UpperCase Then CellChars = UCase(CellChars)
Cell.Value = Cell.Value & CellChars
i = i + 1
End If
Debug.Print Cell.Value
Next
End Sub
The problem is, I can't seem to preserve the prior cell, for instance, the 4.A, 4.B, 4.C, and 4.D
If I understand correctly then you need to store the last whole number you see then use that on the next blank cell, resetting the counter between gaps:
Sub AlphaFill()
Dim Cell As Range
Dim UpperCase As Boolean
Dim LastWholeNumber As String
Dim LastLetter As Long
Dim CurrentNumber As String
UpperCase = True
For Each Cell In Range("F1:F21400")
CurrentNumber = Cell.Value
If CurrentNumber = "" Then
LastLetter = LastLetter + 1
Cell.Value = LastWholeNumber & "." & ChrW$(LastLetter)
ElseIf InStr(CurrentNumber, ".") = 0 Then
'// whole number - store it & reset to A/a
LastLetter = IIf(UpperCase, 64, 97)
LastWholeNumber = CurrentNumber
End If
Next
End Sub
Oh, I got it. This works!!
Sub AlphaFill()
Dim Cell, CellChars
Dim Default, Prompt, Title
Dim rangeSelected As Range
Dim UpperCase As Boolean
On Error Resume Next
Set rangeSelected = Range("F1:F21400")
For Each Cell In rangeSelected
If Cell.Value <> "" Then
KeepValue = Cell.Value
i = 1
End If
If Cell.Value = "" Then
CellChars = Chr(64 + i)
If Not UpperCase Then CellChars = UCase(CellChars)
Cell.Value = KeepValue & CellChars
i = i + 1
End If
Next
End Sub
Assuming your data is as
Try this
Sub AlphaFill()
Dim Cell, CellChars
Dim Default, Prompt, Title
Dim rangeSelected As Range
Dim UpperCase As Boolean
Dim charIndex As Long
On Error Resume Next
Set rangeSelected = Range("F1:F21400")
UpperCase = True
charIndex = 65
For Each Cell In rangeSelected
If Cell.Value = "" Then
If InStr(Cell.Offset(-1, 0), ".") > 0 Then 'check if value contains "."
Cell.Value = Left(Cell.Offset(-1, 0), WorksheetFunction.Find(".", Cell.Offset(-1, 0))) & Chr(charIndex) 'extract the string till "."
Else
Cell.Value = Cell.Offset(-1, 0) & "." & Chr(charIndex) 'get the number and add "."
End If
charIndex = charIndex + 1
Else
If charIndex <> 65 Then charIndex = 65 'if cell is not blank set charIndex to 65
End If
Next
End Sub
This will give result as
I have a string which is in A1 of Sheet1 and it refreshes regularly.
it looks like this -
{"rows":[{"advances":637,"declines":836,"unchanged":76,"total":1549}],"success":"true","results":1}
I want to extract numeric values 637 and 836 and 76 and store it in separate columns. The values keeps on changing, like 637 can be sometimes 1200.
I want a VBA code, not an user defined function to automatically extract the numeric data and store it.
I am using this code, but I am not getting anything. What is wrong?
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$A$1" Then
Dim s As String
Dim fieldNames As Variant
Dim iField As Long
Dim nFields As Long
Dim v As Variant
' specify what you want
fieldNames = Array("advances", "declines", "unchanged")
nFields = UBound(fieldNames) - LBound(fieldNames) + 1
s = [a1].Value ' read in the string
' parse it, put values in a Variant array
ReDim v(1 To 1, 1 To nFields)
For iField = 1 To nFields
s = Mid(s, InStr(s, """" & fieldNames(iField - 1) & """:") _
+ Len(fieldNames(iField - 1)) + 3)
v(1, iField) = Left(s, InStr(s, ",") - 1)
Next iField
' write array to sheet
[b1].Resize(1, nFields).Value = v
End If
Range("=Sheet1!$E$1:$G$1").Copy Destination:=Sheets("Updated").Range("B" & Rows.Count).End(xlUp).Offset(1)
Sheets("Updated").Range("A" & Rows.Count).End(xlUp).Offset(1).Value = Now
End Sub
While I would be tempted to use standard functions for this you could run a VBA UDF like so
to get the first match from A1 (as a number)
=RegexMatches(A1,1)
the second match
=RegexMatches(A1,2)
and so on
The UDF returns "No Match" where no matches are found, or a message "Less than X matches" if you try to extract a match from a position that doesb't exist
Function RegexMatches(strIn As String, LngPos As Long)
Dim objRegex
Dim objRegexMC
Set objRegex = CreateObject("vbscript.regexp")
With objRegex
.Global = True
.Pattern = ":(\d+),"
If .test(strIn) Then
Set objRegexMC = .Execute(strIn)
If objRegexMC.Count >= LngPos Then
RegexMatches = CLng(objRegexMC(LngPos - 1).submatches(0))
Else
RegexMatches = "Less than " & LngPos & " matches"
End If
Else
RegexMatches = "No Match"
End If
End With
End Function
[Update: added sheet event code]
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng1 As Range
Set rng1 = Sheets("Updated").Range("B" & Rows.Count).End(xlUp).Offset(1, 0)
'set row1 as first row if it is blank
If rng1.Row = 2 And rng1.Offset(-1, 0) = vbNullString Then Set rng1 = rng1.Offset(-1, 0)
rng1.Resize(1, 3).Value = Range("B1:D1").Value
rng1.Offset(0, -1).Value = Now
End Sub
You can use an instance of the windows script control to parse the text for you:
Sub Tester()
Dim JSON As String
Dim sc As Object
JSON = "{""rows"":[{""advances"":637,""declines"":836," & _
"""unchanged"":76,""total"":1549}]" & _
",""success"":""true"",""results"":1}"
'JSON = ActiveSheet.Range("A1").Value
Set sc = CreateObject("ScriptControl")
sc.Language = "JScript"
sc.Eval "var o = eval(" & JSON & ")" 'evaluate to an object
Debug.Print "success", sc.Eval("o.success")
Debug.Print "#results", sc.Eval("o.results")
Debug.Print " advances", sc.Eval("o.rows[0].advances")
Debug.Print " declines", sc.Eval("o.rows[0].declines")
Debug.Print " unchanged", sc.Eval("o.rows[0].unchanged")
End Sub
I needed something similar and developed the following code. I have no doubts that it could be made more sophisticated but it provided me with the required result.
Dim cr_ref As String ' Original reference - alpha and numer
Dim new_ref As String ' Resultant numeirc only reference
Dim iAsciiCode As Integer
Dim sCount As Integer ' Length of reference
cr_ref = Trim(Cells(4, 19).Value) ' Get reference from source
sCount = Len(cr_ref)
new_ref = "" ' Start with empty result
For i = 0 To sCount - 1 ' Strip out all except digits
iAsciiCode = Asc(Mid(cr_ref, i + 1, 1)) ' Give me Ascii code
If iAsciiCode >= 48 And iAsciiCode <= 57 Then ' I only want 0-9
new_ref = new_ref & Mid(cr_ref, i + 1, 1) ' Append numeric digit
End If
Next i
' Result is in new_ref
This works:
Dim s As String
Dim fieldNames As Variant
Dim iField As Long
Dim nFields As Long
Dim v As Variant
' specify what you want
fieldNames = Array("advances", "declines", "unchanged")
nFields = UBound(fieldNames) - LBound(fieldNames) + 1
s = [a1].Value ' read in the string
' parse it, put values in a Variant array
ReDim v(1 To 1, 1 To nFields)
For iField = 1 To nFields
s = Mid(s, InStr(s, """" & fieldNames(iField - 1) & """:") _
+ Len(fieldNames(iField - 1)) + 3)
v(1, iField) = Left(s, InStr(s, ",") - 1)
Next iField
' write array to sheet
[b1].Resize(1, nFields).Value = v