I just released an Excel Add-In in my department today that I've been working on for the last 2+ months that checks for about 30 validation errors. I have the error trapping handled in all situations (as it appears right now), but I received a horrible wake-up call today as I received automatic emails (a feature I built into the error handling) for two vital bugs. I already posted a question about the first bug here and figured I'd start a fresh question for the second bug as it's about something unrelated to the first.
My code is as follows
Private Sub symbolCheck()
On Error GoTo ErrHandler
Application.StatusBar = "(3/16) Checking for invalid symbols"
Dim MyArray As Variant
Dim replacementsMade As Boolean
replacementsMade = False
MyArray = ActiveSheet.UsedRange
For i = LBound(MyArray) To UBound(MyArray)
For j = LBound(MyArray, 2) To UBound(MyArray, 2)
If MyArray(i, j) <> "" Then
'Apostrophe/Closing Single Quote
If InStr(1, MyArray(i, j), "’") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "’", Chr(39))
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Apostrophe
If InStr(1, MyArray(i, j), "`") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "`", Chr(39))
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Opening Single Quote
If InStr(1, MyArray(i, j), "‘") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "‘", Chr(39))
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Double Open Quotes
If InStr(1, MyArray(i, j), "“") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "“", """")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Double Closing Quotes
If InStr(1, MyArray(i, j), "”") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "”", """")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Dash
If InStr(1, MyArray(i, j), "–") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "–", "-")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Registered Trademark (R)
If InStr(1, MyArray(i, j), "®") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "®", "(R)")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Trademark (TM)
If InStr(1, MyArray(i, j), "™") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "™", "(TM)")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Degree Symbol
If InStr(1, MyArray(i, j), "°") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "°", " degrees")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Multiplication/x Symbol
If InStr(1, MyArray(i, j), "×") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "×", "x")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Upside-Down Question Mark Symbol
If InStr(1, MyArray(i, j), "¿") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "¿", "")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Solid Bullet Symbol
If InStr(1, MyArray(i, j), "•") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "•", "")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Triple Dots Symbol
If InStr(1, MyArray(i, j), "…") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "…", "...")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Euro Symbol
If InStr(1, MyArray(i, j), "€") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "€", "")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Linebreak Symbol
If InStr(1, MyArray(i, j), "|") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "|", ",")
If replacementsMade = False Then
replacementsMade = True
End If
End If
' 'Less Than Symbol
' If InStr(1, MyArray(i, j), "<") > 0 Then
' MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "<", "<")
' End If
' 'Greater Than Symbol
' If InStr(1, MyArray(i, j), ">") > 0 Then
' MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), ">", ">")
' End If
'Half Fraction
If InStr(1, MyArray(i, j), "½") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "½", " 1/2")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Three Quarter Fraction
If InStr(1, MyArray(i, j), "¾") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "¾", " 3/4")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'One Quarter Fraction
If InStr(1, MyArray(i, j), "¼") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "¼", " 1/4")
If replacementsMade = False Then
replacementsMade = True
End If
End If
End If
Next j
Next i
If replacementsMade Then
ActiveSheet.UsedRange = MyArray
End If
Set MyArray = Nothing
Exit Sub
ErrHandler:
Err.Raise Err.Number, "symbolCheck", Err.Description
End Sub
This bug occurs on the line
If MyArray(i, j) <> "" Then
When i = 209 and j = 60, so I did some poking around and looked inside the array to see what value was at that location. The value just says Error 2023 when I looked at the Watchlist values for the array slot. So, I looked at the cell that corresponded with those i and j values and alas I finally saw why the error was raised. The value in the cell was originally a formula with reference errors and since I copy/pasted as values prior to running this sub I thought I'd be fine. I had no idea that #REF! wasn't seen as plaintext?
Which leads me to my question
How can I handle this situation? More precisely, how would I be able to get rid of the #REF! values in a spreadsheet (without using Find/Replace) if #REF! isn't seen as plaintext even after being Copy/Pasted as a value?
Solution to Clear #REF! Values in Spreadsheet
You can use SpecialCells to clear the errors.
ActiveSheet.UsedRange.SpecialCells(xlCellTypeFormulas, xlErrors).ClearContents 'Or change .Value to another value, delete cells, etc. as desired
Solution to handle #REF! Errors in Array
You can use the ISERROR() VBA function to capture each #REF! and then handle as desired.
Modify your code as follows:
Private Sub symbolCheck()
On Error GoTo ErrHandler
Application.StatusBar = "(3/16) Checking for invalid symbols"
Dim MyArray As Variant
Dim replacementsMade As Boolean
replacementsMade = False
MyArray = ActiveSheet.UsedRange
For i = LBound(MyArray) To UBound(MyArray)
For j = LBound(MyArray, 2) To UBound(MyArray, 2)
If IsError(MyArray(i, j)) Then
'Handle the #REF! here
ElseIf MyArray(i, j) <> "" Then
'Apostrophe/Closing Single Quote
If InStr(1, MyArray(i, j), "’") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "’", Chr(39))
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Apostrophe
If InStr(1, MyArray(i, j), "`") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "`", Chr(39))
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Opening Single Quote
If InStr(1, MyArray(i, j), "‘") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "‘", Chr(39))
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Double Open Quotes
If InStr(1, MyArray(i, j), "“") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "“", """")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Double Closing Quotes
If InStr(1, MyArray(i, j), "”") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "”", """")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Dash
If InStr(1, MyArray(i, j), "–") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "–", "-")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Registered Trademark (R)
If InStr(1, MyArray(i, j), "®") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "®", "(R)")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Trademark (TM)
If InStr(1, MyArray(i, j), "™") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "™", "(TM)")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Degree Symbol
If InStr(1, MyArray(i, j), "°") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "°", " degrees")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Multiplication/x Symbol
If InStr(1, MyArray(i, j), "×") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "×", "x")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Upside-Down Question Mark Symbol
If InStr(1, MyArray(i, j), "¿") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "¿", "")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Solid Bullet Symbol
If InStr(1, MyArray(i, j), "•") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "•", "")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Triple Dots Symbol
If InStr(1, MyArray(i, j), "…") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "…", "...")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Euro Symbol
If InStr(1, MyArray(i, j), "€") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "€", "")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Linebreak Symbol
If InStr(1, MyArray(i, j), "|") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "|", ",")
If replacementsMade = False Then
replacementsMade = True
End If
End If
' 'Less Than Symbol
' If InStr(1, MyArray(i, j), "<") > 0 Then
' MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "<", "<")
' End If
' 'Greater Than Symbol
' If InStr(1, MyArray(i, j), ">") > 0 Then
' MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), ">", ">")
' End If
'Half Fraction
If InStr(1, MyArray(i, j), "½") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "½", " 1/2")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'Three Quarter Fraction
If InStr(1, MyArray(i, j), "¾") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "¾", " 3/4")
If replacementsMade = False Then
replacementsMade = True
End If
End If
'One Quarter Fraction
If InStr(1, MyArray(i, j), "¼") > 0 Then
MyArray(i, j) = WorksheetFunction.Substitute(MyArray(i, j), "¼", " 1/4")
If replacementsMade = False Then
replacementsMade = True
End If
End If
End If
Next j
Next i
If replacementsMade Then
ActiveSheet.UsedRange = MyArray
End If
Set MyArray = Nothing
Exit Sub
ErrHandler:
Err.Raise Err.Number, "symbolCheck", Err.Description
End Sub
Related
Caution, I am a novice.
Objective: To create a "Comments" sheet that includes all comments from the current sheet selected. Here is what my sheet looks like:
The way I want the sheet to look is:
The way the sheet actually appears is:
Essentially, I do not want to use the "Parent Address" for the "Comment In" column but rather the heading above the cell. For example, I do not want $A$2 but actually want it to refer to the heading "Responsible Party". My initial thought was that I could use named ranges but it proved to be out of my capabilities.
I am not a strong coder. Please keep this in mind.
The code is as follows:
Sub ExtractComments()
Dim ExComment As Comment
Dim i As Integer
Dim ws As Worksheet
Dim CS As Worksheet
Set CS = ActiveSheet
If ActiveSheet.Comments.Count = 0 Then Exit Sub
For Each ws In Worksheets
If ws.Name = "Comments" Then i = 1
Next ws
If i = 0 Then
Set ws = Worksheets.Add(After:=ActiveSheet)
ws.Name = "Comments"
Else: Set ws = Worksheets("Comments")
End If
For Each ExComment In CS.Comments
ws.Range("A1").Value = "Comment In"
ws.Range("B1").Value = "Comment By"
ws.Range("C1").Value = "Comment"
With ws.Range("A1:C1")
.Font.Bold = True
.Interior.Color = RGB(189, 215, 238)
.Columns.ColumnWidth = 20
End With
If ws.Range("A2") = "" Then
ws.Range("A2").Value = ExComment.Parent.Address
ws.Range("B2").Value = Left(ExComment.Text, InStr(1, ExComment.Text, ":") - 1)
ws.Range("C2").Value = Right(ExComment.Text, Len(ExComment.Text) - InStr(1, ExComment.Text, ":"))
Else
ws.Range("A1").End(xlDown).Offset(1, 0) = ExComment.Parent.Address
ws.Range("B1").End(xlDown).Offset(1, 0) = Left(ExComment.Text, InStr(1, ExComment.Text, ":") - 1)
ws.Range("C1").End(xlDown).Offset(1, 0) = Right(ExComment.Text, Len(ExComment.Text) - InStr(1, ExComment.Text, ":"))
End If
Next ExComment
End Sub
Thank you for your time.
It's definitely not bad from a novice :) Try this:
...
Else: Set ws = Worksheets("Comments")
End If
Dim iRow As Long ' you have a better control this way directly specifying the target cell
' header needs to written only once - out of loop
ws.Range("A1").Value = "Comment In"
ws.Range("B1").Value = "Comment By"
ws.Range("C1").Value = "Comment"
With ws.Range("A1:C1")
.Font.Bold = True
.Interior.Color = RGB(189, 215, 238)
.Columns.ColumnWidth = 20
End With
iRow = 2 ' first empty row
For Each ExComment In CS.Comments
ws.Cells(iRow, 1).Value = CS.Cells(1, ExComment.Parent.Column) ' value in 1st row of column of comment
ws.Cells(iRow, 2).Value = Left(ExComment.Text, InStr(1, ExComment.Text, ":") - 1)
ws.Cells(iRow, 3).Value = Right(ExComment.Text, Len(ExComment.Text) - InStr(1, ExComment.Text, ":"))
iRow = iRow + 1
Next ExComment
End Sub
Just one change to add the header value, and shortened your code a little by working up from the bottom when adding comments, and remove some stuff from the loop.
Sub ExtractComments()
Dim ExComment As Comment
Dim i As Long
Dim ws As Worksheet
Dim CS As Worksheet
Set CS = ActiveSheet
If ActiveSheet.Comments.Count = 0 Then Exit Sub
For Each ws In Worksheets
If ws.Name = "Comments" Then i = 1
Next ws
If i = 0 Then
Set ws = Worksheets.Add(After:=ActiveSheet)
ws.Name = "Comments"
Else: Set ws = Worksheets("Comments")
End If
With ws
.Range("A1").Value = "Comment In"
.Range("B1").Value = "Comment By"
.Range("C1").Value = "Comment"
With .Range("A1:C1")
.Font.Bold = True
.Interior.Color = RGB(189, 215, 238)
.Columns.ColumnWidth = 20
End With
For Each ExComment In CS.Comments
.Range("A" & Rows.Count).End(xlUp)(2).Value = CS.Cells(1, ExComment.Parent.Column)
.Range("B" & Rows.Count).End(xlUp)(2).Value = Left(ExComment.Text, InStr(1, ExComment.Text, ":") - 1)
.Range("C" & Rows.Count).End(xlUp)(2).Value = Right(ExComment.Text, Len(ExComment.Text) - InStr(1, ExComment.Text, ":"))
Next ExComment
End With
End Sub
use:
ws.Range("A2").Value = ExComment.Parent.End(xlUp).Value
so:
If ws.Range("A2") = "" Then
ws.Range("A2").Value = ExComment.Parent.End(xlUp).Value
ws.Range("B2").Value = Left(ExComment.Text, InStr(1, ExComment.Text, ":") - 1)
ws.Range("C2").Value = Right(ExComment.Text, Len(ExComment.Text) - InStr(1, ExComment.Text, ":"))
Else
ws.Range("A1").End(xlDown).Offset(1, 0) = ExComment.Parent.End(xlUp).Value
ws.Range("B1").End(xlDown).Offset(1, 0) = Left(ExComment.Text, InStr(1, ExComment.Text, ":") - 1)
ws.Range("C1").End(xlDown).Offset(1, 0) = Right(ExComment.Text, Len(ExComment.Text) - InStr(1, ExComment.Text, ":"))
End If
while you could consider the following refactoring of your code
Sub ExtractComments()
If ActiveSheet.Comments.count = 0 Then Exit Sub
Dim ws As Worksheet
On Error Resume Next
Set ws = Worksheets("Comments")
On Error GoTo 0
If ws Is Nothing Then
Set ws = Worksheets.Add(After:=ActiveSheet)
ws.Name = "Comments"
End If
Dim ExComment As Comment
With ws
With .Range("A1:C1")
.Value = Array("Comment In", "Comment By", "Comment")
.Font.Bold = True
.Interior.Color = RGB(189, 215, 238)
.Columns.ColumnWidth = 20
End With
For Each ExComment In ActiveSheet.Comments
.Cells(.Rows.count, 1).End(xlUp).Offset(1).Resize(, 3) = Array(ExComment.Parent.End(xlUp).Value, _
Left(ExComment.Text, InStr(1, ExComment.Text, ":") - 1), _
Right(ExComment.Text, Len(ExComment.Text) - InStr(1, ExComment.Text, ":")))
Next ExComment
End With
End Sub
I have created an "If ElseIf Else" statement that will count the strings in a strings. It does count the keywords/strings that I want to be counted but it is counting the "others"/ Else items wrongly. As shown below highlighted in red, I should only have 6 strings that should be labelled as "others" but it counted as 8. It is a total of 18 rows but on the total results it counted it as 20.
I am newby in VBA and needing experts advise. Thank you.
Option Compare Text
Public Sub Keywords()
Dim row_number As Long
Dim count_of_corp_or_windows As Long
Dim count_of_mcafee As Long
Dim count_of_token As Long
Dim count_of_host_or_ipass As Long
Dim count_of_others As Long
Dim count_of_X As Long
Dim count_of_all As Long
Dim items As Variant
row_number = 0
count_of_corp_or_windows = 0
count_of_mcafee = 0
count_of_token = 0
count_of_host_or_ipass = 0
count_of_X = 0
count_of_others = 0
count_of_all = 0
Do
row_number = row_number + 1
items = Sheets("LoginPassword").Range("N" & row_number)
If InStr(items, "corp") Or InStr(items, "windows") Then
count_of_corp_or_windows = count_of_corp_or_windows + 1
ElseIf InStr(items, "mcafee") Then
count_of_mcafee = count_of_mcafee + 1
ElseIf InStr(items, "token") Then
count_of_token = count_of_token + 1
ElseIf InStr(items, "host") Or InStr(items, "ipass") Then
count_of_host_or_ipass = count_of_host_or_ipass + 1
ElseIf InStr(items, "X A") Then
count_of_X = count_of_X + 1
Else:
count_of_others = count_of_others + 1
End If
Loop Until items = ""
count_of_all = count_of_corp_or_windows + count_of_mcafee + count_of_token + count_of_host_or_ipass + count_of_X + count_of_others
Range("N2").Select
Selection.End(xlDown).Select
lastCell = ActiveCell.Address
ActiveCell.Offset(3, 0).Value = "Count"
ActiveCell.Offset(4, 0).Value = count_of_corp_or_windows
ActiveCell.Offset(5, 0).Value = count_of_mcafee
ActiveCell.Offset(6, 0).Value = count_of_token
ActiveCell.Offset(7, 0).Value = count_of_host_or_ipass
ActiveCell.Offset(8, 0).Value = count_of_X
ActiveCell.Offset(9, 0).Value = count_of_others
ActiveCell.Offset(11, 0).Value = count_of_all
ActiveCell.Offset(3, 1).Value = "Keywords"
ActiveCell.Offset(4, 1).Value = "Corp or Windows"
ActiveCell.Offset(5, 1).Value = "Mcafee"
ActiveCell.Offset(6, 1).Value = "Token"
ActiveCell.Offset(7, 1).Value = "Host or ipass"
ActiveCell.Offset(8, 1).Value = "X accounts"
ActiveCell.Offset(9, 1).Value = "Others"
ActiveCell.Offset(11, 1).Value = "Total"
ActiveCell.Offset(3, -1).Value = "Percent"
ActiveCell.Offset(4, -1).Value = count_of_corp_or_windows / count_of_all
ActiveCell.Offset(5, -1).Value = count_of_mcafee / count_of_all
ActiveCell.Offset(6, -1).Value = count_of_token / count_of_all
ActiveCell.Offset(7, -1).Value = count_of_host_or_ipass / count_of_all
ActiveCell.Offset(8, -1).Value = count_of_X / count_of_all
ActiveCell.Offset(9, -1).Value = count_of_others / count_of_all
End Sub
You should start row_number at 2 instead of 1 because cell N1 contains "Short Description" which you probably do not want to match to anything?
Also you are looping Until a blank cell but the blank cell has already been counted as an "other" cell so this plus the "Short Description" cell probably accounts for the 2 unexpected other cells that are counted. So probably just change row_number = 0 to row_number = 1 and Else: to ElseIf items <> "" then
count_of_others is never set to zero after an iteration since it is out of the loop. This should happen for other counter variables too.
Delete the colon from after your Else and try again.
See this answer for reference.
I have a range of data , where some of the cells have line breaks, and I need to split the line breaks into rows below where the line break occurs, but leave the other cells as is. There are also multiple columns if that makes a difference.
I have used the two answers provided below, with some adjustments to fit my worksheet, but neither is working for splitting ALL the cells. I ended up even trying both, but that does not work either.
When there is a line break in column A, it is working, but when there is not a line break in column A, and there is in another column, it does not work. If there is NOT a line break in column A, I just need to split the row where there is a line break and merge it into the row below.
Here are the codes:
end_row = range("A" & Rows.count).End(xlUp).row
range("A:A").TextToColumns Destination:=range("a1"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=True, Tab:=False, _
Semicolon:=False, Comma:=False, Space:=False, Other:=False, OtherChar _
:=" ", FieldInfo:=Array(1, 1), TrailingMinusNumbers:=True
For i = 1 To end_row
row_added = False
For j = 1 To 4
If InStr(1, Cell, Chr(10)) <> 0 Then
If Not row_added Then
Rows(i + 1).Insert
row_added = True
end_row = end_row + 1
End If
Cells(i + 1, j) = Right(Cells(i, j), Len(Cells(i, j)) - InStr(1, Cell, Chr(10)))
Cells(i, j) = Left(Cells(i, j), InStr(1, Cell, Chr(10)) - 1)
End If
Next j
Next i
And
Sub LFtoRow()
Dim myWS As Worksheet, myRng As range
Dim LastRow As Long, iLoop As Long, jLoop As Long
Dim myString() As String
Set myWS = ActiveSheet
LastRow = myWS.Cells(myWS.Rows.count, 1).End(xlUp).row
For iLoop = LastRow To 1 Step -1
myString = Split(myWS.Cells(iLoop, 1), Chr(10))
If UBound(myString, 1) > 0 Then
myWS.Rows(iLoop + 1 & ":" & iLoop + UBound(myString, 1)).Insert shift:=xlShiftDown
For jLoop = 0 To UBound(myString, 1)
myWS.Cells(iLoop + jLoop, 1) = myString(jLoop)
Next jLoop
End If
Next iLoop
End Sub
Either a new code entirely, or just something to add to the end would work. I have an example of what is happening, and what I would like it to look like below. (I know it shows column B in the photo, but at this point in the MACRO it is in column A)
What is happening:
What I need to happen:
I would recommend code similar to the following to solve your problem. It has the following properties:
Uses the Split function to on Chr(10) to determine the strings you need on each line. Chr(10) is the linefeed character. Split generates an array of strings for you.
Inserts the correct number of rows for you.
Loops through your range from the bottom-up, so you process the complete range.
The code ...
Sub LFtoRow()
Dim myWS As Worksheet, myRng As Range
Dim LastRow As Long, iLoop As Long, jLoop As Long
Dim myString() As String
Set myWS = Worksheets("Sheet1")
LastRow = myWS.Cells(myWS.Rows.Count, 1).End(xlUp).Row
For iLoop = LastRow To 1 Step -1
myString = Split(myWS.Cells(iLoop, 1), Chr(10))
If UBound(myString, 1) > 0 Then
myWS.Rows(iLoop + 1 & ":" & iLoop + UBound(myString, 1)).Insert Shift:=xlShiftDown
For jLoop = 0 To UBound(myString, 1)
myWS.Cells(iLoop + jLoop, 1) = myString(jLoop)
Next jLoop
End If
Next iLoop
End Sub
When presented with this input ...
... generates this result ...
This most likely is not the most concise way to do this, but this ended up working for me using #OldUgly's code.
Sub LFtoRow()
Dim myWS As Worksheet, myRng As range
Dim LastRow As Long, iLoop As Long, jLoop As Long
Dim myString() As String
Set myWS = ActiveSheet
LastRow = myWS.Cells(myWS.Rows.count, 1).End(xlUp).row
For iLoop = LastRow To 1 Step -1
myString = Split(myWS.Cells(iLoop, 1), Chr(10))
If UBound(myString, 1) > 0 Then
myWS.Rows(iLoop + 1 & ":" & iLoop + UBound(myString, 1)).Insert shift:=xlShiftDown
For jLoop = 0 To UBound(myString, 1)
myWS.Cells(iLoop + jLoop, 1) = myString(jLoop)
Next jLoop
End If
Next iLoop
For iLoop = LastRow To 1 Step -1
myString = Split(myWS.Cells(iLoop, 2), Chr(10))
If UBound(myString, 1) > 0 Then
For jLoop = 0 To UBound(myString, 1)
myWS.Cells(iLoop + jLoop, 2) = myString(jLoop)
Next jLoop
End If
Next iLoop
For iLoop = LastRow To 1 Step -1
myString = Split(myWS.Cells(iLoop, 3), Chr(10))
If UBound(myString, 1) > 0 Then
For jLoop = 0 To UBound(myString, 1)
myWS.Cells(iLoop + jLoop, 3) = myString(jLoop)
Next jLoop
End If
Next iLoop
For iLoop = LastRow To 1 Step -1
myString = Split(myWS.Cells(iLoop, 4), Chr(10))
If UBound(myString, 1) > 0 Then
For jLoop = 0 To UBound(myString, 1)
myWS.Cells(iLoop + jLoop, 4) = myString(jLoop)
Next jLoop
End If
Next iLoop
For iLoop = LastRow To 1 Step -1
myString = Split(myWS.Cells(iLoop, 5), Chr(10))
If UBound(myString, 1) > 0 Then
For jLoop = 0 To UBound(myString, 1)
myWS.Cells(iLoop + jLoop, 5) = myString(jLoop)
Next jLoop
End If
Next iLoop
End Sub
Here is my suggestion that should handle line breaks in all columns.
Also i removed the replacement that inserts a ";" and then splits on that. Complete code will be:
end_row = Range("A" & Rows.Count).End(xlUp).Row
Range("A:A").TextToColumns Destination:=Range("a1"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=True, Tab:=False, _
Semicolon:=False, Comma:=False, Space:=False, Other:=False, OtherChar _
:=" ", FieldInfo:=Array(1, 1), TrailingMinusNumbers:=True
For i = 1 To end_row
row_added = False
For j = 1 To 4
If InStr(1, Cell, Chr(10)) <> 0 Then
If Not row_added Then
Rows(i + 1).Insert
row_added = True
end_row = end_row + 1
End If
Cells(i + 1, j) = Right(Cells(i, j), Len(Cells(i, j)) - InStr(1, Cell, Chr(10)))
Cells(i, j) = Left(Cells(i, j), InStr(1, Cell, Chr(10)) - 1)
End If
Next j
Next i
This macro takes 2+ minutes to run. What are the best methods to optimize the macro?
Sub Time_Color(z, k)
Application.DisplayAlerts = False
For Each cell In Sheet1.Range("C" & z & ":" & "LZ" & z)
If cell.Value <> "x" Then
If cell.Value < Sheet3.Range("D" & k) Then
cell.Interior.ColorIndex = 37
cell.Offset(1, 0).Value = Sheet4.Range("D" & k).Value & "_" & Sheet4.Cells(k, 5).Value
End If
For j = 5 To 1000 Step 2
If cell.Value > Sheet3.Cells(k, j).Value And cell.Value < Sheet3.Cells(k, j + 1).Value Then
cell.Interior.ColorIndex = 37
cell.Offset(1, 0).Value = Sheet4.Cells(k, j + 1).Value & "_" & Sheet4.Cells(k, j + 2).Value
End If
Next j
For j = 4 To 1000 Step 2
If cell.Value >= Sheet3.Cells(k, j).Value And cell.Value <= Sheet3.Cells(k, j + 1).Value Then
cell.Interior.ColorIndex = 43
cell.Offset(1, 0).Value = Sheet4.Cells(k, j).Value & "_" & Sheet4.Cells(k, j + 1).Value
End If
Next j
End If
Next cell
Application.DisplayAlerts = True
End Sub
I am running this macro for 24 different combinations of z,k.
Try caching as much data as possible, for instance Sheet3.Range("D" & k) is constant throughout this function.
Every instance of the inner most loop will query that cell. If you put it at the beginning of this function, it will be looked up once and then used for the remainder of the function.
Edit:
In the comments on this question is - I think - a better answer by Tim Williams, which is specific to VBA:
Turn off ScreenUpdating and Calculation while running. Calculation
should be reset before your Sub ends (ScreenUpdating will reset
itself)
I'm not entirely sure what you are trying to accomplish, but it seems that your loop iterates over a large range to find the last-most instance of a cell that satisfies one of the two given criteria (your two loops).
If that is the goal, why not start from the back? Depending on how your sheet looks, this is potentially a lot faster!
I also made some other changes. Let me know how it works.
Take care to also include the function at the bottom (heisted from this answer), or substitute it for your function of choice.
Sub Time_Color(z, k)
Application.DisplayAlerts = False
Application.ScreenUpdating = False
Dim loopVal, loopVal2, loopVal3 As Variant
Dim setOdd, setEven, OddEven As Boolean
Dim compVal, compVal2, compVal3 As Variant
compVal = Sheet3.Range("D" & k).Value
compVal2 = Sheet4.Range("D" & k).Value
compVal3 = Sheet4.Cells(k, 5).Value
For Each cell In Sheet1.Range("C" & z & ":" & "LZ" & z)
If cell.Value <> "x" Then
If cell.Value < compVal Then
cell.Interior.ColorIndex = 37
cell.Offset(1, 0).Value = compVal2 & "_" & compVal3
End If
For j = 1000 To 4 Step -1
loopVal = Sheet3.Cells(k, j).Value
loopVal2 = Sheet3.Cells(k, j + 1).Value
loopVal3 = Sheet4.Cells(k, j + 1).Value
OddEven = OddOrEven(j)
If OddEven = True Then
If cell.Value > loopVal And cell.Value < loopVal2 Then
cell.Interior.ColorIndex = 37
cell.Offset(1, 0).Value = loopVal3 & "_" & Sheet4.Cells(k, j + 2).Value
setOdd = True
End If
Else
If cell.Value >= loopVal And cell.Value <= loopVal2 Then
cell.Interior.ColorIndex = 43
cell.Offset(1, 0).Value = Sheet4.Cells(k, j).Value & "_" & loopVal3
setEven = True
End If
End If
If setEven = True And setOdd = True Then Exit For
Next j
End If
Next cell
Application.DisplayAlerts = True
End Sub
Function OddOrEven(a As Integer) As Boolean ' Returns TRUE if a is an odd number
If a - (2 * (Fix(a / 2))) <> 0 Then OddOrEven = True
End Function
I have wrote the following code to merge cells in excel, the data is around 26000 rows, the code is running on core I7 CPU with 8 GB RAM, the problem that it still working since 4 days, the average rows per day is 3000 row!, any one know how to get the result, because its a report that should be delivered since three days!
Sub MergeCellss()
lastRow = Worksheets("A").Range("A65536").End(xlUp).Row
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.DisplayAlerts = False
For i = 2 To lastRow
If Cells(i, 2).Value <> Cells(i - 1, 2).Value And Cells(i, 2).Value <> Cells(i + 1, 2).Value Then
intUpper = i
Debug.Print ("<> -1 and <> +1 " & intUpper)
End If
If Cells(i, 2).Value <> Cells(i - 1, 2).Value And Cells(i, 2).Value = Cells(i + 1, 2).Value Then
intUpper = i
Debug.Print ("<> -1 and = +1 " & intUpper & " UPPPER LIMIT")
End If
If Cells(i, 2).Value <> Cells(i + 1, 2).Value And Cells(i, 2).Value = Cells(i - 1, 2).Value Then
Application.DisplayAlerts = False
Debug.Print ("<> +1 and = -1:" & i & "LOWER LIMIT")
DoEvents
For x = 1 To 8
Range(Cells(intUpper, x), Cells(i, x)).Merge
Next x
For j = 18 To 26
Range(Cells(intUpper, j), Cells(i, j)).Merge
Next j
Cells(intUpper, 14).Value = "=sumif(M" & CStr(intUpper) & ":M" & CStr(i) & ","">0"")"
Range(Cells(intUpper, 14), Cells(i, 14)).Merge
Range(Cells(i, 1), Cells(i, 26)).Borders(xlEdgeBottom).LineStyle = xlDouble
End If
If Cells(i, 2).Value <> Cells(i + 1, 2).Value And Cells(i, 2).Value <> Cells(i - 1, 2).Value Then
Debug.Print ("One Cells: " & i)
Range(Cells(i, 1), Cells(i, 26)).Borders(xlEdgeBottom).LineStyle = xlDouble
Cells(intUpper, 14).Value = Cells(intUpper, 13).Value
DoEvents
End If
Next i
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.DisplayAlerts = True
End Sub
the code above will merge the all cells containing repeated data like User Name, Date of Birth, .... into one cell, and leave the training courses and experiences as it is.
I wonder how can I run this code in less than 1 hour.
Here is some rewrite on your code. The two primary differences are the use of If ... ElseIf ... End If and the grouping of the first and fourth conditional operations (the conditions were the same).
Sub Merge_Cells()
Dim lastRow As Long, rw As Long
Dim intUpper As Long, x As Long
Dim vVALs As Variant
appTGGL bTGGL:=False
Debug.Print Timer
With Worksheets("A")
.Cells(1, 1) = Timer
lastRow = .Cells(Rows.Count, 1).End(xlUp).Row
For rw = 2 To lastRow
vVALs = Array(.Cells(rw - 1, 2).Value, .Cells(rw, 2).Value, .Cells(rw + 1, 2).Value)
If vVALs(1) <> vVALs(0) And vVALs(1) <> vVALs(2) Then
'the first and fourth conditions were the same so they are both here
'original first If condition
intUpper = rw
'Debug.Print ("<> -1 and <> +1 " & intUpper)
'original fourth If condition
'Debug.Print ("One Cells: " & rw)
.Range(.Cells(rw, 1), .Cells(rw, 26)).Borders(xlEdgeBottom).LineStyle = xlDouble
.Cells(intUpper, 14).Value = .Cells(intUpper, 13).Value
ElseIf vVALs(1) <> vVALs(0) And vVALs(1) = vVALs(2) Then
intUpper = rw
'Debug.Print ("<> -1 and = +1 " & intUpper & " UPPPER LIMIT")
ElseIf vVALs(1) = vVALs(0) And vVALs(1) <> vVALs(2) Then
'Debug.Print ("<> +1 and = -1:" & rw & "LOWER LIMIT")
For x = 1 To 26
If x < 9 Or x > 17 Then _
.Range(.Cells(intUpper, x), .Cells(rw, x)).Merge
Next x
.Cells(intUpper, 14).Value = "=sumif(M" & CStr(intUpper) & ":M" & CStr(rw) & ","">0"")"
.Range(.Cells(intUpper, 14), .Cells(rw, 14)).Merge
.Cells(rw, 1).Resize(1, 26).Borders(xlEdgeBottom).LineStyle = xlDouble
End If
Next rw
.Cells(1, 2) = Timer
End With
Debug.Print Timer
appTGGL
End Sub
Sub appTGGL(Optional bTGGL As Boolean = True)
Application.Calculation = IIf(bTGGL, xlCalculationAutomatic, xlCalculationManual)
Application.ScreenUpdating = bTGGL
Application.EnableEvents = bTGGL
Application.DisplayAlerts = bTGGL
End Sub
I've also read the three primary conditional values into a variant array to reduce repeated worksheet value reads.