I want to trim a string in MS Excel each cell to 100 characters in a column with 500 cells.
Starting with first cell, check if string length is or equal 100 characters. If the words are more than 100, then remove 1 word in the cell, then check again, if it's more than 100 remove another word until the string is less to 100. Then paste the less than 100 character string into the same cell replacing previous more than 100 character string.
Then move to another cell and replete the previous step.
The words to be removed are in an array
Here is my code so far
Sub RemoveWords()
Dim i As Long
Dim cellValue As String
Dim stringLenth As Long
Dim myString As String
Dim words() As Variant
words = Array("Many", "specific ", "Huawei", "tend", "Motorolla", "Apple")
myString = "Biggest problem with many phone reviews from non-tech specific publications is that its reviewers tend to judge the phones in a vacuum"
For i = 1 To 13
cellValue = Cells(i, 4).Value
If Not IsEmpty(cellValue) Then
stringLength = Len(cellValue)
' test if string is less than 100
If stringLength > 100 Then
Call replaceWords(cellValue, stringLength, words)
Else
' MsgBox "less than 100 "
End If
End If
Next i
End Sub
Public Sub replaceWords(cellValue, stringLength, words)
Dim wordToRemove As Variant
Dim i As Long
Dim endString As String
Dim cellPosition As Variant
i = 0
If stringLength > 100 Then
For Each wordToRemove In words
If InStr(1, UCase(cellValue), UCase(wordToRemove )) = 1 Then
MsgBox "worked word found" & " -- " & cellValue & " -- " & key
Else
Debug.Print "Nothing worked" & " -- " & cellValue & " -- " & key
End If
Next wordToRemove
Else
MsgBox "less than 100 "
End If
End Sub
Sub NonKeyWords()
' remove non key words
'
Dim i As Long
Dim cellValue As String
Dim stringLenth As Long
Dim wordToRemove As Variant
Dim words() As Variant
Dim item As Variant
' assign non-key words to array
words = words = Array("Many", "specific ", "Huawei", "tend", "Motorolla", "Apple")
' loop though all cells in column D
For i = 2 To 2000
cellValue = Cells(i, 4).Value
If Not IsEmpty(cellValue) Then
' test if string is less than 100
If Len(cellValue) > 100 Then
'Debug.Print "BEFORE REMOVING: " & cellValue
Call replaceWords(cellValue, words, i)
Else
' MsgBox "less than 100"
End If
End If
Next i
End Sub
Public Sub replaceWords(cellValue, words, i)
If Len(cellValue) > 100 Then
For Each wordsToDelete In words
If Len(cellValue) > 100 Then
cellValue = Replace(cellValue, wordsToDelete, "")
'Debug.Print cellValue
Debug.Print "String length after removal = " & Len(cellValue)
Debug.Print "remove another word................"
'cells(i, 4).ClearContents
Cells(i, 4).Value = cellValue
Else
'exit
End If
Next
Else
Debug.Print "SAVE: " & cellValue
End If
End Sub
Related
I was trying to create a universal, error resistant VBA code that would count words in selected ranges as MS Word does. This below is the best I could do and I was hoping that somebody would have a look and let me know if I missed something or suggest any improvements. The code is quite fast and works with single cell, non-adjacent cells and whole columns, I need it to be as universal as possible. I'll be looking forward to feedback.
Option Explicit
Sub word_count()
Dim r() As Variant 'array
Dim c As Long 'total counter
Dim i As Long
Dim l As Long 'string lenght
Dim c_ch As Long 'character counter
Dim c_s As String 'string variable
Dim cell As range
Dim rng As range
If Selection Is Nothing Then
MsgBox "Sorry, you need to select a cell/range first", vbCritical
Exit Sub
ElseIf InStr(1, Selection.Address, ":", vbTextCompare) = 0 And InStr(1, Selection.Address, ",", vbTextCompare) = 0 Then 'for when only one cell is selected
word_count_f Selection.Value, c
MsgBox "Your selected cell '" & Replace(Selection.Address, "$", "") & "' in '" & Selection.Parent.Name & "' has " & c & " words."
Exit Sub
ElseIf InStr(1, Selection.Address, ",", vbTextCompare) > 0 Then 'when user selects more than one cell by clicking one by one -> address looks like ('A1,A2,A3') etc
Application.ScreenUpdating = False
Dim help() As Variant
ReDim help(1 To Selection.Cells.Count)
i = 1
For Each cell In Selection 'loading straigh to array wouldn't work, so I create a helper array
help(i) = cell.Value
i = i + 1
Next cell
r = help
Else 'load selection to array to improve speed
Application.ScreenUpdating = False
r = Selection.Value
End If
Dim item As Variant
For Each item In r
word_count_f item, c
Next item
MsgBox "Your selected range '" & Replace(Selection.Address, "$", "") & "' in '" & Selection.Parent.Name & "' has " & c & " words."
End Sub
Private Function word_count_f(ByVal item As Variant, ByRef c As Long)
Dim l As Long 'lenght variable
Dim c_s As String 'whole string variable
Dim c_ch As Long 'characted count variable
l = Len(item)
If l = 0 Then Exit Function
c_s = item
c_s = Trim(c_s)
Do While InStr(1, c_s, " ", vbTextCompare) > 0 'remove double spaces to improve accuracy
c_s = Replace(c_s, " ", " ")
Loop
If InStr(1, c_s, " ", vbTextCompare) = 0 And l > 0 Then 'if there was just one word in the cell
c = c + 1
ElseIf InStr(1, c_s, " ", vbTextCompare) > 0 Then 'else loop through string to count words
For c_ch = 1 To l 'loop through charactes of the string
If (Mid(c_s, c_ch, 1)) = " " Then
c = c + 1 'for each word
End If
Next c_ch
c = c + 1 'add one for the first word in cell
Else 'hopefully useless msgbox, but I wanted to be sure to inform the user correctly
MsgBox "Sorry, there was an error while processing one of the cells, the result might not be accurate", vbCritical
End If
End Function
You can achieve this in a similar way but with less code if you are interested to see?:
Sub word_count()
start_time = Timer
Dim r As Variant 'temp split array
Dim arr As Variant 'array
Dim c As Long 'total counter
If Selection Is Nothing Then
MsgBox "Sorry, you need to select a cell/range first", vbCritical
Exit Sub
Else
c = 0
For Each partial_selection In Split(Selection.Address, ",")
If Range(partial_selection).Cells.Count > 1 Then
arr = Range(partial_selection).Value
Else
Set arr = Range(partial_selection)
'single cell selected don't convert to array
End If
For Each temp_cell In arr
If Len(Trim(temp_cell)) > 0 Then
r = Split(temp_cell, " ")
For Each temp_word In r
If Len(Trim(temp_word)) > 0 Then
c = c + 1
'If the word is just a blank space don't count
End If
Next
'c = c + (UBound(r) - LBound(r) + 1)
'trimmed = Trim(temp_cell)
'c = c + 1 + (Len(trimmed) - Len(Replace(trimmed, " ", "")))
Else 'Blank cell
'Do nothing
End If
Next
Next
End If
Dim item As Variant
time_taken = Round(Timer - start_time, 3)
MsgBox "Your selected range '" & Replace(Selection.Address, "$", "") _
& "' in '" & Selection.Parent.Name & "' has " & c & " words." _
& vbNewLine & "Time Taken: " & time_taken & " secs"
Debug.Print c & " in "; time_taken; " secs"
End Sub
You could try this sort of approach? There may be the need to check for the next character to the space being another space, which would need some additions made. To detect word one as being the same as word one in the count. Also, transferring the range to an array would make it a touch faster.
Function Word_Count(rng As Excel.Range) As Long
Dim c As Excel.Range
Dim s As String
Dim l As Long
For Each c In rng.Cells
s = Trim(c.Value)
If s <> "" Then
If InStr(1, s, " ") > 0 Then
' Find number of spaces. You can use the ubound of split maybe here instead
l = l + (Len(s) - Len(Replace(s, " ", "")))
Else
End If
' Will always be 1 word
l = l + 1
End If
Next c
Word_Count = l
Set c = Nothing
End Function
I am converting Eng names to Gujarati (Indian) language using IME Keyboard.
while executing script DO WHILE.. the script converts English names to gujarati using 'SendKeys' successfully,
Sub engtoguj_ref()
'
' Macro1 Macro
'
'
Dim currange As Range
Dim strlen As Integer
Dim newstr As String
Dim countr As Integer
Dim engstr As String
Dim gujloc As String
countr = 0
engstr = ActiveCell.Offset(0, -2).Value
Do While (Len(engstr) > 0 And countr < 30)
engstr = Range(ActiveCell.Offset(0, -2).Value).Value
MsgBox (engstr)
countr = countr + 1
newstr = " " & engstr & " "
SendKeys newstr, True
DoEvents
ActiveCell.Offset(1, 0).Select
MsgBox (ActiveCell.Address)
Loop
Application.MoveAfterReturn = True
End Sub
the Eng name is in column B, the gujarati name should be in column C.., instead it copies all the name together in Column C in the row next to last English Name.
Why is my first iteration in Sub throughCols that is intended to move one row down each time jumping four rows?
Option Explicit
Dim txt As String
Dim i As Long
Dim strTest As String
Dim strArray() As String
Dim lCaseOn As Boolean
Dim firstRow As Long, startIt As Long
Dim thisCell As Range
Dim lastRow As Long
Dim resetAddress As Range
Sub throughCols()
' Dim thisCell As Range
' get start and end of column data
' NB sheet name is hard coded twice
Call dataRange
startIt = firstRow + 1
For i = 1 To 8 Step 1
' after testing use startIt To lastRow Step 1
' by using activeCell I dont have to pass range through to the sub
Sheets("test").Range("B" & i).Select
MsgBox "this is itteration " & i & " which will output to " & ActiveCell.Offset(0, 2).Address
Call arrayManip
Call cleanTxt(txt)
Next i
End Sub
Sub arrayManip()
' clear out all data
Erase strArray
txt = ""
'set default case
lCaseOn = False
' string into an array using a " " separator
strTest = WorksheetFunction.Proper(ActiveCell.Value)
strTest = Replace(strTest, "-", " - ")
strTest = Replace(strTest, "‘", " ‘ ")
strArray = Split(strTest, " ")
' itterate through array looking to make text formats
For i = LBound(strArray) To UBound(strArray)
If strArray(i) = "-" Then
lCaseOn = True
GoTo NextIteration
End If
If strArray(i) = "‘" Then
lCaseOn = True
GoTo NextIteration
End If
If lCaseOn Then
strArray(i) = LCase(strArray(i))
lCaseOn = False
NextIteration:
End If
Next
End Sub
Function cleanTxt(txt)
' loop through the array to build up a text string
For i = LBound(strArray) To UBound(strArray)
txt = txt & strArray(i) & " "
Next i
' remove the space
txt = Trim(Replace(txt, " - ", "-"))
txt = Trim(Replace(txt, " ‘ ", "‘"))
' MsgBox "active cell is " & activeCell.Address
ActiveCell.Offset(0, 2).Select: ActiveCell.Value = txt
' MsgBox "final output would be " & txt & " to " & activeCell.Address
' this is a thumb suck to attempt to reset the active cell to the itteration address that started it
ActiveCell.Offset(0, -2).Select
MsgBox "next itteration should start with active cell set as " & ActiveCell.Address
End Function
Sub dataRange()
With Sheets("test").Columns("B")
If WorksheetFunction.CountA(.Cells) = 0 Then '<--| if no data whatever
MsgBox "Sorry: no data"
Else
With .SpecialCells(xlCellTypeConstants) '<--| reference its cells with constant (i.e, not derived from formulas) values)
firstRow = .Areas(1).Row
lastRow = .Areas(.Areas.Count).Cells(.Areas(.Areas.Count).Rows.Count).Row
End With
' MsgBox "the first row is " & firstRow
' MsgBox "last row is " & lastRow
End If
End With
End Sub
You are declaring your i variable at module scope, which makes it accessible everywhere within the module; it's modified when you call arrayManip and the value changes.
If you declare a local ind variable inside this routine it won't happen, because the variable will only be accessible to the scope it's declared in. Try the code below:
Sub throughCols()
' Dim thisCell As Range
Dim ind As Long '<-- DECLARE local variable
' get start and end of column data
' NB sheet name is hard coded twice
Call dataRange
startIt = firstRow + 1
' ===== loop on ind and not i (changes when you call arrayManip) ====
For ind = 1 To 8 ' Step 1 <-- actually not needed, that's the default increment value
' after testing use startIt To lastRow Step 1
' by using activeCell I dont have to pass range through to the sub
Sheets("test").Range("B" & ind).Select
MsgBox "this is itteration " & ind & " which will output to " & ActiveCell.Offset(0, 2).Address
Call arrayManip
Call cleanTxt(txt)
Next ind
End Sub
This is not a question, so much as a solution, but I wanted to share it here as I had gotten help for things I needed here.
I wanted to find a specific Excel sheet, in the Active Workbook, searching by the name of the sheet. I built this to find it. It is a "contains" search, and will automatically go to the sheet if it is found, or ask the user if there are multiple matches:
To end at any time, just enter a blank in the input box.
Public Sub Find_Tab_Search()
Dim sSearch As String
sSearch = ""
sSearch = InputBox("Enter Search", "Find Tab")
If Trim(sSearch) = "" Then Exit Sub
'MsgBox (sSearch)
Dim sSheets() As String
Dim sMatchMessage As String
Dim iWorksheets As Integer
Dim iCounter As Integer
Dim iMatches As Integer
Dim iMatch As Integer
Dim sGet As String
Dim sPrompt As String
iMatch = -1
iMatches = 0
sMatchMessage = ""
iWorksheets = Application.ActiveWorkbook.Sheets.Count
ReDim sSheets(iWorksheets)
'Put list of names in array
For iCounter = 1 To iWorksheets
sSheets(iCounter) = Application.ActiveWorkbook.Sheets(iCounter).Name
If InStr(1, sSheets(iCounter), sSearch, vbTextCompare) > 0 Then
iMatches = iMatches + 1
If iMatch = -1 Then iMatch = iCounter
sMatchMessage = sMatchMessage + CStr(iCounter) + ": " + sSheets(iCounter) + vbCrLf
End If
Next iCounter
Select Case iMatches
Case 0
'No Matches
MsgBox "No Match Found for " + sSearch
Case 1
'1 match activate the sheet
Application.ActiveWorkbook.Sheets(iMatch).Activate
Case Else
'More than 1 match. Ask them which sheet to go to
sGet = -1
sPrompt = "More than one match found. Please enter number from following list"
sPrompt = sPrompt + "to display the sheet" + vbCrLf + vbCrLf + sMatchMessage
sPrompt = sPrompt + vbCrLf + vbCrLf + "Enter blank to cancel"
sGet = InputBox(sPrompt, "Please select one")
If Trim(sGet) = "" Then Exit Sub
sPrompt = "Value must be a number" + vbCrLf + vbCrLf + sPrompt
Do While IsNumeric(sGet) = False
sGet = InputBox(sPrompt, "Please select one")
If Trim(sGet) = "" Then Exit Sub
Loop
iMatch = CInt(sGet)
Application.ActiveWorkbook.Sheets(iMatch).Activate
End Select
End Sub
I hope someone finds this useful, and would also welcome enhancement suggestions.
For fun tried to do this in as few lines as possible with loops
Uses a range name, xlm, and VBS under utilised Filter to provide the same multi-sheet search functionality as above.
The bulk of the code relates to the sheet selection portion
Sub GetNAmes()
Dim strIn As String
Dim X
strIn = Application.InputBox("Search string", "Enter string to find", ActiveSheet.Name, , , , , 2)
If strIn = "False" Then Exit Sub
ActiveWorkbook.Names.Add "shtNames", "=RIGHT(GET.WORKBOOK(1),LEN(GET.WORKBOOK(1))-FIND(""]"",GET.WORKBOOK(1)))"
X = Filter([index(shtNames,)], strIn, True, 1)
Select Case UBound(X)
Case Is > 0
strIn = Application.InputBox(Join(X, Chr(10)), "Multiple matches found - type position to select", , , , , 1)
If strIn = "False" Then Exit Sub
On Error Resume Next
Sheets(CStr(X(strIn))).Activate
On Error GoTo 0
Case 0
Sheets(X(0)).Activate
Case Else
MsgBox "No match"
End Select
End Sub
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