How to remove empty lines from cells in Excel (VBA) - vba

Here's what I'm trying to achieve through all the cells in the worksheet containing a string, with limited success so far:
| EXAMPLE |
cell1_empty_line
cell1_text1
cell1_empty_line
+---------------------+
cell2_text1
cell2_emptyline
cell2_text2
+---------------------+
cell3_emptyline
cell3_emptyline
cell3_text1
+---------------------+
| EXPECTED RESULT |
cell1_text1
+---------------------+
cell2_text1
cell2_text2
+---------------------+
cell3_text1
+---------------------+
Any suggestion for such a macro?
Many thanks.

Use this macro to remove any empty lines inside all cells:
Sub TrimEmptyLines()
Dim cel As Range, s As String, len1 As Long, len2 As Long
For Each cel In ActiveSheet.UsedRange
If Not IsError(cel.Value2) Then
If InStr(1, cel.text, vbLf) > 0 Then
s = Trim(cel.Value2)
Do ' remove duplicate vbLf
len1 = Len(s)
s = Replace$(s, vbLf & vbLf, vbLf)
len2 = Len(s)
Loop Until len2 = len1
' remove vblf at beginning or at end
If Left$(s, 1) = vbLf Then s = Right$(s, Len(s) - 1)
If Right$(s, 1) = vbLf Then s = Left$(s, Len(s) - 1)
cel.value = Trim$(s)
End If
End If
Next
End Sub

This is general enough to handle any column of cells with any # of line feeds in each cell. It assumes all your values are in column "A" starting at row 1 of the active sheet:
Public Function RemoveDoubleLfs(str As String) As String
If InStr(str, vbLf & vbLf) > 0 Then
str = RemoveDoubleLfs(Replace(str, vbLf & vbLf, vbLf))
End If
RemoveDoubleLfs = str
End Function
Sub RemoveEmptyLines()
Dim i As Integer, lastRow As Integer
lastRow = ActiveSheet.Cells(Rows.Count, "A").End(xlUp).Row '
Dim val As String
For i = 1 To lastRow:
val = Cells(i, "A").Value
If InStr(1, val, vbLf) > 0 Then
val = RemoveDoubleLfs(val)
If Left(val, 1) = vbLf Then val = Right(val, Len(val) - 1)
If Right(val, 1) = vbLf Then val = Left(val, Len(val) - 1)
Cells(i, "A").Value = val
End If
Next
ActiveSheet.Rows.EntireRow.AutoFit
End Sub
The recursive replace function gets rid of double line feeds in the text of the cell. Once that's done there will be at most one VbLf at the beginning and end of the string. The last two if statements look for and remove the latter.
The autofit at the end is optional and is there purely to prettify the result; it just compacts the cells to their minimum height.

If you are working with just one cell and its blank lines within then one of these should work:
Cells.Replace what:=Chr(13), Replacement:="", LookAt:=xlPart
Cells.Replace what:=Chr(10), Replacement:="", LookAt:=xlPart

Before implementing this solution please set the values of the two variables at the top.
FirstDataColumn = 1
FirstDataRow = 2
This setting leaves starts with the first column but leaves out the first row which might contain column captions.
Sub RemoveBlanks()
Dim FirstDataColumn As Long, FirstDataRow As Long
Dim LastColumn As Long, LastRow As Long
Dim Tmp As Variant, Arr As Variant
Dim Counter As Integer
Dim C As Long, R As Long
FirstDataColumn = 1
FirstDataRow = 2
Application.ScreenUpdating = False
With ActiveSheet
With .UsedRange
LastColumn = .Columns.Count
LastRow = .Rows.Count
End With
For C = FirstDataColumn To LastColumn
ReDim Arr(LastRow, 0)
Counter = 0
For R = FirstDataRow To LastRow
Tmp = Trim(.Cells(R, C).Value)
If Len(Tmp) Then
Arr(Counter, 0) = Tmp
Counter = Counter + 1
End If
Next R
.Cells(FirstDataRow, C).Resize(LastRow, 1).Value = Arr
Next C
End With
Application.ScreenUpdating = True
End Sub

Related

Adding a character at the end of text & increment letter A to B

What I need help on is to copy the previous cells text into the cell below it and add the letter A at the end of it i.e. before VP0007 after VP0007A. This should continue until all the blank cells have been incremented and it reaches the next VP0008.
Please see the images. I apologise if I am not too clear.
             Before:                         After:
      
Right now I have the following code:
ActiveCell.Offset(1, 0).Select
Letter = "A"
Letters = Chr(Asc(Letter) + 1)
Number = ActiveCell.Offset(-1, 0).Value
If ActiveCell.Value = Number & Letter _ Then
ActiveCell.Offset(1, 0).Select.Value Number & Number
Else
ActiveCell.Value = Number & Letters
End If
Loop Until ActiveCell.Offset(1, 0).Value <> ""
Try this short sub procedure.
Sub fillSubseries()
Dim i As Long, a As Long, str As String
With Worksheets("sheet4")
For i = 2 To .Cells(.Rows.Count, "A").End(xlUp).Row
If IsEmpty(.Cells(i, "A")) Then
.Cells(i, "A") = str & Chr(a)
a = a + 1
Else
a = 65
str = .Cells(i, "A").Value2
End If
Next i
End With
End Sub
Try using the below code
LastRow = ActiveSheet.Range("A" & Rows.Count).End(xlUp).Row
Letter = "A"
For iLoop = 2 To LastRow
If ActiveSheet.Range("A" & iLoop) = "" Then
iValue = ActiveSheet.Range("A" & iLoop - 1)
iiLoop = iLoop
Do
If ActiveSheet.Range("A" & iiLoop) = "" Then
ActiveSheet.Range("A" & iiLoop) = iValue & Letter
Letter = Chr(Asc(Letter) + 1)
Else
Letter = "A"
Exit Do
End If
iiLoop = iiLoop + 1
Loop
iLoop = iiLoop - 1
End If
Next
This code should handle cases where you have more than 26 blank rows and increment past the letter "Z".
Sub FillBlanks()
Dim lastRow As Long, cnt As Long, i As Long
Dim prevItem As String
Dim ws As Worksheet
Set ws = ActiveSheet
lastRow = ws.Cells(Rows.Count, 1).End(xlUp).Row
prevItem = ws.Cells(2, 1).Value
cnt = 0
For i = 2 To lastRow
If ws.Cells(i, 1) = "" Then
cnt = cnt + 1
ws.Cells(i, 1).Value = prevItem & Split(Cells(1, cnt).Address(True, False), "$")(0)
Else
prevItem = ws.Cells(i, 1)
cnt = 0
End If
Next i
End Sub
Alternate:
Sub tgr()
Dim ws As Worksheet
Dim aData As Variant
Dim sTemp As String
Dim sLetter As String
Dim i As Long, j As Long
Set ws = ActiveWorkbook.ActiveSheet
With ws.Range("A2", ws.Cells(ws.Rows.Count, "A").End(xlUp))
If .Row < 2 Then Exit Sub 'No data
aData = .Value
End With
For i = LBound(aData, 1) To UBound(aData, 1)
If Len(Trim(aData(i, 1))) > 0 Then
sTemp = Trim(aData(i, 1))
j = 0
Else
j = j + 1
sLetter = Replace(ws.Cells(1, j).Address(0, 0), 1, vbNullString)
aData(i, 1) = sTemp & sLetter
End If
Next i
ws.Range("A2").Resize(UBound(aData, 1)).Value = aData
End Sub
If you need a pure formula solution, you may try below steps (The first row of your data should be A2, not A1):
First we need a dummy column in order to fill in the blank rows. Use below formula on B2 and copy it down through the last row of column A:
=IF(A2<>"",A2,B1)
Then we will create the final values on column C. Add below formula to C2 and copy down:
=IF(A2<>"",A2,IF(ISNUMBER(VALUE(RIGHT(C1,1)))=TRUE,C1&"A",B2&CHAR(CODE(RIGHT(C1,1))+1)))
Basically we first filled in the blank rows with repeating values on column B. Then copied Col:A value to Col:C if Col:A is not blank. If Col:A is blank and upper row (Col:C) value's last character is numeric we add "A" to that value. If the last character is a letter than we concatenate the next letter with Col:B value.
You should have something like below, when everything is OK:

How can I get the text after the second bracket in VBA?

Trying to get the value after the second bracket, I try to work with this one i created but it's not working properly and getting more complicated while I am having more brackets in each row.. Any idea. I uploaded couple images to understand the input and output.
Sub stringtest()
Dim text As String, i As Long, firstbracket As Long, secondbracket As Long
Dim extractTest As String, y As Long
y = 1
For i = 1 To 10
text = Worksheets("Sheet1").Cells(i, 1).Value
firstbracket = InStr(1, text, "[")
secondbracket = InStr(firstbracket + 1, text, "]")
extractTest = Mid(text, firstbracket + 1, secondbracket)
Worksheets("Sheet1").Cells(y, 2).Value = extractTest
y = y + 1
Next i
End Sub
Try splitting by the second bracket, then using Left() to determine the length of the string
Option Explicit
Public Sub GetStringAfter2ndBracketInSequentialColumns()
Dim i As Long, j As Long, nextRow As Long, ub As Long, lr As Long
Dim extract As Variant, found As Long, nextCol As Long
With Sheet1
lr = .UsedRange.Rows.Count
nextRow = 1
nextCol = 2
For i = 1 To lr
extract = Split(.Cells(i, 1).Value2, "]")
ub = UBound(extract)
If ub > 0 Then
For j = 0 To ub
If Len(extract(j)) > 0 Then
If Left(extract(j), 1) <> "[" Then
found = InStr(1, extract(j), "[")
If found = 0 Then found = Len(extract(j)) + 1
.Cells(nextRow, nextCol).Value2 = Left(extract(j), found - 1)
nextRow = nextRow + 1
If nextRow > lr Then
nextRow = 1
nextCol = nextCol + 1
End If
End If
End If
Next j
End If
Next i
End With
End Sub
Test results:
This is how one of the strings (cell A1) looks like after the split
Edit: measurements for all solutions provided so far:
Timers (with 100,000 rows)
0.824 secs - TextToColumns (0.81054, 0.82812) - Output: same row split to many cols
1.679 secs - Split cells (1.66796, 1.64453) - Output: sequentially by rows, then cols
3.757 secs - ArrayList (3.69140, 3.78125) - Output: sequentially in one column
here's a short and quite fast approach
Sub main()
With Range("A1", Cells(Rows.Count, 1).End(xlUp))
.Replace what:="[*]", replacement:="|", lookat:=xlPart
.TextToColumns DataType:=xlDelimited, Other:=True, OtherChar:="|"
.Columns(1).Delete xlToLeft
End With
End Sub
Here's little different approach using ArrayList
Dim rng As Range
Dim arl As Object
Dim strVal
Dim i As Long
Set arl = CreateObject("System.Collections.ArrayList")
For Each rng In Range("A1:A" & Range("A" & Rows.Count).End(xlUp).Row)
strVal = Split(Replace(rng.Value, "[", "]"), "]")
For i = 2 To UBound(strVal) Step 2
arl.Add CStr(strVal(i))
Next i
Next rng
For i = 0 To arl.Count - 1
Range("B" & i + 1).Value = arl.Item(i)
Next i
Set arl = Nothing

How to remove quotes using vba?

I have:
nuid="!,#,a-z"
But I do not want the double quotes. I want nuid=!,#,a-z
Suggest me ways to remove the start and end quotes
Here is my code:
sub highlight(nuid as string)
dim sh3 as worksheet
Set sh3 = Thisworkbook.Worksheets("Sheet1")
sh3.Select
Cells.Find("User ID").Select
ActiveCell.Offset(1, 0).Select
nuid = Replace(nuid, """", "")
Set rn = sh3.UsedRange
k = rn.Rows.Count + rn.Row - 1
For x = 1 To k
If ActiveCell.Value Like nuid Then
Selection.Interior.Color = vbYellow
Else
Selection.Interior.ColorIndex = xlNone
End If
ActiveCell.Offset(1, 0).Select 'moves activecell down one row.
Next
end sub
From my gui, i will enter special characters which will be stored in the variable nuid.I want only the special characters and not the quotes around it
Also you can try:
nuid = Replace(nuid, Chr(34), vbNullString)
But you can have problem if quotes not the first nor the last character, for example: "!,#,"a-z".
In that case you can try:
nuid = Mid(nuid, 2, Len(nuid) - 1) This will cut the first and last character
Edit:
It seems to me that the quotes that you see indicates the type of a variable string.
Edit2 - watch window
Results:
Edit3 - with sub 4 Sagi:
Sub Highlight4Sagi(SpecChar As String)
Dim Column As Integer
SpecChar = "!##"
ThisWorkbook.Worksheets(1).Select
Column = Cells.Find("User ID").Column
LastRow = Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
For i = 2 To LastRow 'loop each row in column "User ID"
For j = 1 To Len(SpecChar) 'loop every specchar: ! and # and # and find him in each cells
If InStr(1, Cells(i, Column), Mid(SpecChar, j, 1)) > 0 Then
Cells(i, Column).Interior.ColorIndex = 6
Exit For
Else
Cells(i, Column).Interior.ColorIndex = 0
End If
Next j
Next i
End Sub
Proper Function:
Sub Test()
Debug.Print RemoveOuterQuotes(Cells(2, 1).Value)
End Sub
Public Function RemoveOuterQuotes(ByVal Str As String) As String
If Left(Str, 1) = """" Then
Str = Right(Str, Len(Str) - 1)
End If
If Right(Str, 1) = """" Then
Str = Left(Str, Len(Str) - 1)
End If
'Debug.Print Str
'Stop
RemoveOuterQuotes = Str
End Function
Basically escape a " with ""
Below should help
nuid = replace (nuid, """", "")
additional variant
Sub highlight(nuid As String)
Dim sh3 As Worksheet, Cl&, Lrow&, x&, oCell As Range
Set sh3 = ThisWorkbook.Worksheets("Sheet1")
Cl = sh3.Cells.Find("User ID").Column
Frow = sh3.Cells.Find("User ID").Row + 1
Lrow = Cells.Find("*", , , , xlByRows, xlPrevious).Row
For Each oCell In sh3.Range(Cells(Frow, Cl), Cells(Lrow, Cl))
If oCell.Value <> "" Then
For x = 1 To Len(nuid)
If oCell.Value Like "*" & Mid(nuid, x, 1) & "*" Then
oCell.Interior.Color = vbYellow
Exit For
Else
oCell.Interior.Color = xlNone
End If
Next x
End If
Next oCell
End Sub
output
but if you need to find, for instance the cells which contain any char in low case [a-z] then another aproach should be used

How to get particular values from single cell and put into different cells in Excel VBA

I need to do it for more than 1000 cells, to read the particular data and to put under respective cells using Excel VBA.
Example:
Name Age No. .. .
abc 14 123454 ------>this from single cell
Which contains like Name: abc,Age: 14, No: 123454
This should be a good start :
Sub Split_N_Copy()
Dim InFo()
Dim InfSplit() As String
InFo = ActiveSheet.Cells.UsedRange.Value2
Sheets.Add after:=Sheets(Sheets.Count)
For i = LBound(InFo, 1) To UBound(InFo, 1)
'Here I put InFo(i,1), "1" if we take the first column
InfSplit = Split(InFo(i,1), ",")
For k = LBound(InfSplit) To UBound(InfSplit)
Sheets(Sheets.Count).Cells(i + 1, k + 1) = InfSplit(k)
Next k
Next i
End Sub
I write a function based on , for separator sign and : for equal sign, that search a range of data that first row contains headers:
Function UpdateSheet(allData As String, inRange As Range)
Dim strData() As String
Dim i As Long, lastRow As Long
Dim columnName As String, value As String
Dim cell As Range
'You need to change this to finding last row like this answer:
'http://stackoverflow.com/a/15375099/4519059
lastRow = 2
strData = Split(allData, ",")
For i = LBound(strData) To UBound(strData)
columnName = Trim(Left(strData(i), InStr(1, strData(i), ":") - 1))
value = Trim(Mid(strData(i), InStr(1, strData(i), ":") + 1))
For Each cell In inRange
If cell.Cells(1, 1).Rows(1).Row = 1 Then
If cell.Cells(1, 1).value Like "*" & columnName & "*" Then
inRange.Worksheet.Cells(lastRow, cell.Columns(1).Column).value = value
End If
End If
Next
Next
End Function
Now you can use that function like this:
Sub update()
Call UpdateSheet("Name: abc,Age: 14, No: 123454", Sheets(1).UsedRange)
End Sub
Private Sub CommandButton1_Click()
lastRow = Sheet1.Cells(Sheet1.Rows.Count, "G").End(xlUp).Row
Dim i As Integer
i = 2
For i = 2 To lastRow
Dim GetData As String
GetData = Sheet1.Cells(i, 7)
Call UpdateSheet(GetData, Sheets(1).UsedRange, i)
Next
End Sub
Function UpdateSheet(allData As String, inRange As Range, rowno As Integer)
Dim strData() As String
Dim i As Long, lastRow As Long
Dim columnName As String, value As String
Dim cell As Range
strData = Split(allData, ",")
For i = LBound(strData) To UBound(strData)
Value1 = Trim(Mid(strData(i), InStr(1, strData(i), ":") + 1))
If Value1 <> "" Then
columnName = Trim(Left(strData(i), InStr(1, strData(i), ":") - 1))
value = Trim(Mid(strData(i), InStr(1, strData(i), ":") + 1))
For Each cell In inRange
If cell.Cells(1, 1).Rows(1).Row = 1 Then
If cell.Cells(1, 1).value Like "*" & columnName & "*" Then
inRange.Worksheet.Cells(rowno, cell.Columns(1).Column).value = value
End If
End If
Next
End If
Next
End Function

Trying to extract data from curly braces but not working

I need to sync up the values in the curly braces {} found in column C and put them against the user id in column F as seen below.
E.g. on the Emails sheet
becomes this on a new sheet
Sub CopyConditional()
Dim wshS As Worksheet
Dim WhichName As String
Set wshS = ActiveWorkbook.Sheets("Emails")
WhichName = "NewSheet"
Const NameCol = "C"
Const FirstRow = 1
Dim LastRow As Long
Dim SrcRow As Long
Dim TrgRow As Long
Dim wshT As Worksheet
Dim cpt As String
Dim user As String
Dim computers() As String
Dim computer As String
On Error Resume Next
Set wshT = Worksheets(WhichName)
If wshT Is Nothing Then
Set wshT = Worksheets.Add(After:=wshS)
wshT.Name = WhichName
End If
On Error GoTo 0
If wshT.Cells(1, NameCol).value = "" Then
TrgRow = 1
Else
TrgRow = wshT.Cells(wshT.Rows.Count, NameCol).End(xlUp).Row + 1
End If
LastRow = wshS.Cells(wshS.Rows.Count, NameCol).End(xlUp).Row
For SrcRow = FirstRow To LastRow
cpt = wshS.Range("C" & SrcRow).value
user = wshS.Range("F" & SrcRow).value
If InStr(cpt, ":") Then
cpt = Mid(cpt, InStr(1, cpt, ":") + 1, Len(cpt))
End If
If InStr(cpt, ";") Then
computers = Split(cpt, ";")
For i = 0 To UBound(computers)
If computers(i) <> "" Then
wshT.Range("A" & TrgRow).value = user
wshT.Range("B" & TrgRow).value = Mid(Left(computers(i), Len(computers(i)) - 1), 2)
TrgRow = TrgRow + 1
End If
Next
Else
computer = cpt
If computer <> "" Then
wshT.Range("A" & TrgRow).value = user
wshT.Range("B" & TrgRow).value = Mid(Left(computer, Len(computer) - 1), 2)
TrgRow = TrgRow + 1
End If
End If
Next SrcRow
End Sub
I managed to resolve it with the above code but there are 3 niggling issues:
1) The first curly brace is always copied, how do I omit this so something like {Computer1 looks like Computer 1
2) Where there are two computers in a row, then the output looks something like this:
when it should really be split into two different rows i.e.
User 1 | Computer 1
User 1 | Computer 2
3) If there is text after the last curly brace with text in it e.g. {Computer1};{Computer2};Request submitted then that text is added as a new row, I don't want this, I want it to be omitted e.g.
should just be:
User 1 | Computer 1
User 1 | Computer 2
How do I go about rectifying these issues?
Try this:
Sub Collapse()
Dim uRng As Range, cel As Range
Dim comps As Variant, comp As Variant, r As Variant, v As Variant
Dim d As Dictionary '~~> Early bind, for Late bind use commented line
'Dim d As Object
Dim a As String
With Sheet1 '~~> Sheet that contains your data
Set uRng = .Range("F1", .Range("F" & .Rows.Count).End(xlUp))
End With
Set d = CreateObject("Scripting.Dictionary")
With d
For Each cel In uRng
a = Replace(cel.Offset(0, -3), "{", "}")
comps = Split(a, "}")
Debug.Print UBound(comps)
For Each comp In comps
If InStr(comp, "Computer") <> 0 _
And Len(Trim(comp)) <= 10 Then '~~> I assumed max Comp# is 99
If Not .Exists(cel) Then
.Add cel, comp
Else
If IsArray(.Item(cel)) Then
r = .Item(cel)
ReDim Preserve r(UBound(r) + 1)
r(UBound(r)) = comp
.Item(cel) = r
Else
r = Array(.Item(cel), comp)
.Item(cel) = r
End If
End If
End If
Next
Next
End With
For Each v In d.Keys
With Sheet2 '~~> sheet you want to write your data to
If IsArray(d.Item(v)) Then
.Range("A" & .Rows.Count).End(xlUp).Offset(1, 0) _
.Resize(UBound(d.Item(v)) + 1) = v
.Range("B" & .Rows.Count).End(xlUp).Offset(1, 0) _
.Resize(UBound(d.Item(v)) + 1) = Application.Transpose(d.Item(v))
Else
.Range("A" & .Rows.Count).End(xlUp).Offset(1, 0) = v
.Range("B" & .Rows.Count).End(xlUp).Offset(1, 0) = d.Item(v)
End If
End With
Next
Set d = Nothing
End Sub
Above code uses Replace and Split Function to pass your string to array.
a = Replace(cel.Offset(0, -3), "{", "}") '~~> standardize delimiter
comps = Split(a, "}") '~~> split using standard delimiter
Then information are passed to dictionary object using User as key and computers as items.
We filter the items passed to dictionary using Instr and Len Function
If InStr(comp, "Computer") <> 0 _
And Len(Trim(comp)) <= 10 Then
As I've commented, I assumed your max computer number is 99.
Else change 10 to whatever length you need to check.
Finally we return the dictionary information to the target worksheet.
Note: You need to add reference to Microsoft Scripting Runtime if you prefer early bind
Result: I tried it on a small sample data patterned on how I see it in you SS.
So assuming you have this data in Sheet1:
Will output data in Sheet2 like this:
I use a custom parse function for this type of operation:
Sub CopyConditional()
' some detail left out
Dim iRow&, Usern$, Computer$, Computers$
For iRow = ' firstrow To lastrow
Usern = Sheets("Emails").Cells(iRow, "F")
Computers = Sheets("Emails").Cells(iRow, "C")
Do
Computer = zParse(Computers) ' gets one computer
If Computer = "" Then Exit Do
' Store Computer and Usern
Loop
Next iRow
End Sub
Function zParse$(Haystack$) ' find all {..}
Static iPosL& '
Dim iPosR&
If iPosL = 0 Then iPosL = 1
iPosL = InStr(iPosL, Haystack, "{") ' Left
If iPosL = 0 Then Exit Function ' no more
iPosR = InStr(iPosL, Haystack, "}") ' Right
If iPosR = 0 Then MsgBox "No matching }": Stop
zParse = Mid$(Haystack, iPosL + 1, iPosR - iPosL - 1)
iPosL = iPosR
End Function
1) Use the Mid function to drop the first character:
str = "{Computer1"
str = Mid(str,2)
now str = "Computer1"
2) You can use the Split function to separate these out and combine with the Mid function above
str = "{Computer1}{Computer2}"
splt = Split(str,"}")
for a = 0 to Ubound(splt)
result = Mid(splt(a),2)
next a
3) Add a conditional statement to the above loop
str = "{Computer1}{Computer2}"
splt = Split(str,"}")
for a = 0 to Ubound(splt)
if Left(splt(a),1) = "{" then result = Mid(splt(a),2)
next a
Use this loop and send each result to the desired cell (in the for-next loop) and you should be good to go.