VBA Remove Columns and Strings in excel - excel

I am working in a VBA Code that will remove unnecesary columns upon certain conditions
Dim keepColumn As Boolean
Dim currentColumn As Integer
Dim columnHeading As String
currentColumn = 1
While currentColumn <= ActiveSheet.UsedRange.Columns.Count
columnHeading = ActiveSheet.UsedRange.Cells(1, currentColumn).Value
'CHECK WHETHER TO KEEP THE COLUMN
keepColumn = False
If columnHeading = "Agent" Then keepColumn = True
If columnHeading = "Interval" Then keepColumn = True
If columnHeading = "Break Time" Then keepColumn = True
If columnHeading = "Staffed Time" Then keepColumn = True
If columnHeading = "Lunch Time" Then keepColumn = True
If columnHeading = "Email Time" Then keepColumn = True
If columnHeading = "System Time" Then keepColumn = True
If columnHeading = "Personal Time" Then keepColumn = True
If keepColumn Then
'IF YES THEN SKIP TO THE NEXT COLUMN,
currentColumn = currentColumn + 1
Else
'IF NO DELETE THE COLUMN
ActiveSheet.Columns(currentColumn).Delete
End If
'LASTLY AN ESCAPE IN CASE THE SHEET HAS NO COLUMNS LEFT
If (ActiveSheet.UsedRange.Address = "$A$1") And (ActiveSheet.Range("$A$1").Text = "") Then Exit Sub
Wend
The second part of the codes requires to do 2 things, removes from column A, all characters but the letters, and also in column B changes 05/04/2021 00:00 -0600 - 05/05/2021 00:00 -0601 to just 05/04/21 (meaning, removes evrything after first space), this second part does take a while and I will like to make it faster. Any suggestions?
Function
`Function cleanString(str As String) As String
Dim ch, bytes() As Byte: bytes = str
For Each ch In bytes
If Chr(ch) Like "[A-Za-z]" Then cleanString = cleanString & Chr(ch)
Next ch
End Function`
And here is when i want to run it
Dim rng As Range
For Each rng In Sheets("Sheet2").Range("A1:A5000").Cells 'adjust sheetname and range accordingly
rng.Value = cleanString(rng.Value)
Next
Dim r As Range
For Each r In Range("B2:B" & Cells(Rows.Count, "B").End(xlUp).Row).Cells.SpecialCells(xlCellTypeConstants)
r.Value = Split(r.Value, " ")(0)
Next r
Finally I will like to run the code automatically when the info is updated

I will like to make it faster. Any suggestions?
Like I mentioned, read the range into an array and then perform the cleanup. It will be superfast!
LOGIC
Find last row in the relevant column
Store the range values in array
Perform the actions on the array rather than doing it directly on the range. For example extract alpha chars or getting part of a string. Remember to prefix the date part with ' to prevent Excel from auto formatting.
CODE
Here is an example. I have commented the code so if you get stuck then simply ask.
Option Explicit
Sub Sample()
Dim wsInput As Worksheet, wsOutput As Worksheet
Dim lRow As Long, i As Long
Dim MyAr As Variant
'~~> Change this to the relevant sheet which has data
Set wsInput = Sheet1
'~~> This is where the output will be dumped.
Set wsOutput = Sheet2
With wsInput
'~~> Find last row in Col A
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
'~~> Store the range values in array
MyAr = .Range("A1:B" & lRow).Value2
'~~> perform the actions on the array
For i = LBound(MyAr) To UBound(MyAr)
'~~> Extract alpha chars
MyAr(i, 1) = GetAlphaChars(MyAr(i, 1))
'~~> Get the part before string. and prefix with ' to
'~~> prevent Excel from auto formatting
If InStr(1, MyAr(i, 2), " ") Then MyAr(i, 2) = "'" & Split(MyAr(i, 2), " ")(0)
Next i
End With
'~~> Send the output
wsOutput.Range("A1").Resize(UBound(MyAr), 2).Value = MyAr
End Sub
'~~> Function to extract Alpha characters from the string
Private Function GetAlphaChars(cellVal As Variant) As String
Dim regX As Object
Set regX = CreateObject("VBScript.Regexp")
With regX
.Pattern = "[^a-zA-Z]+"
.Global = True
GetAlphaChars = .Replace(cellVal, vbNullString)
End With
End Function
IN ACTION

Related

Loop through and copy paste values without repetition if conditions are met

Im trying to create a table that pulls data from my raw data if certain conditions are met. The code I currently have does not seem to be working.
Public Sub insert_rows()
Dim datasheet As Worksheet
Dim datasheet2 As Worksheet
Dim r As Long
Dim tableA As ListObject
Set tableA = Worksheets(Sheet7).ListObject(Preventable)
Set datasheet = Worksheets(Sheet7)
Set datasheet2 = Worksheets("Data")
With datasheet2
nr = Cells(Rows.Count, 1).End(x1up).Row
For r = 1 To nr
If Cells(r, 17) = "Y" Then
Cells(r, 16).Copy Destination:=Sheets("Sheet7").Range("B4")
End If
Next
End With
End Sub
Basically I have several worksheets and need to pull data from one of them to add to this table in another worksheet. My condition is if the Column in the raw data worksheet contains "Y", then pull cell values into the table of the other worksheet. An image below is an example of the data I want to copy and paste over:
As you can see, they are string values separated by "," and can contain duplicates.
I only want to add just the unique entries into the new table; with no repetition of cells. Anyway I could modify this code to suit those conditions?
You could try something like this:
Public Sub insert_rows()
Dim datasheet As Worksheet
Dim datasheet2 As Worksheet
Dim r As Long, i As Long, nr As Long
Dim tableStartingRow As Long, currenttableitem As Long
Dim stringvalues As Variant
Dim stringseparator As String
Dim valueexists As Boolean
tableStartingRow = 4
stringseparator = ","
Set datasheet = Worksheets("Sheet7")
Set datasheet2 = Worksheets("Data")
With datasheet
currenttableitem = .Cells(.Rows.Count, 2).End(xlUp).Row
End With
With datasheet2
nr = .Cells(.Rows.Count, 16).End(xlUp).Row
For r = 1 To nr
If .Cells(r, 17) = "Y" Then
If InStr(.Cells(r, 16), stringseparator) > 0 Then 'If value contains comma
stringvalues = Split(.Cells(r, 16), stringseparator)
For i = LBound(stringvalues) To UBound(stringvalues)
valueexists = False 'Reset boolean
For x = tableStartingRow To currenttableitem
If datasheet.Range("B" & x).Value = Trim(stringvalues(i)) Then
valueexists = True
Exit For
End If
Next x
If Not valueexists Then
currenttableitem = currenttableitem + 1
datasheet.Range("B" & currenttableitem).Value = Trim(stringvalues(i))
End If
Next i
Else
valueexists = False 'Reset boolean
For x = tableStartingRow To currenttableitem
If datasheet.Range("B" & x).Value = .Cells(r, 16).Value Then
valueexists = True
Exit For
End If
Next x
If Not valueexists Then
currenttableitem = currenttableitem + 1
datasheet.Range("B" & currenttableitem).Value = .Cells(r, 16).Value
End If
End If
End If
Next
End With
End Sub
This code will check each value of the cells and will split the contents by ",". Then compare with the content of the table to see if this value is already in there. In case it is not, it will be added, otherwise omitted.
Also, I notice the use of the Cells inside of a With statement. That was making a reference to the active worksheet. To make reference to the item in the With statement, you need to use .Cells
I hope this will help.

Excel VBA parse column, extract all substrings

I'm trying to parse a column that contains data in the following format in each cell -
pull: test1
or
pull: test2|pull: test3|.....
or
other: blah...
I only want a grab each "Pull: test" and place 1 in each row in a new worksheet like below, and ignore any parts of the cell that don't begin with "pull: " -
pull: test1
pull: test2
pull: test3
...
What I have so far just pulls the entire column and pastes into the same spreadsheet, I'm not sure how to separate the items in each cell into their own rows. I also can't get it to pull to a different worksheet correctly either (commented out my attempt)
Sub InStrDemo()
Dim lastrow As Long
Dim i As Integer, icount As Integer
'Sheets.Add.Name = "TEST"
lastrow = ActiveSheet.Range("A10000").End(xlUp).Row
For i = 1 To lastrow
If InStr(1, LCase(Range("E" & i)), "pull:") <> 0 Then
icount = icount + 1
'Sheets("TEST").Range("A" & icount & ":E" & icount) = Worksheets("SearchResults").Range("A" & i & ":E" & i).Value
Range("L" & icount) = Range("E" & i).Value
End If
Next i
End Sub
Untested, written on mobile.
Option Explicit
Sub testDemo()
Dim sourceSheet as worksheet
Set sourceSheet = ActiveSheet ' would be more reliable to qualify the workbook and worksheet by name'
Dim outputSheet as worksheet
Set outputSheet = thisworkbook.worksheets.add
Dim lastRow As Long
lastrow = sourceSheet.Range("A10000").End(xlUp).Row
' I assume column E needs to be parsed'
Dim arrayOfValues() as variant
arrayOfValues = sourceSheet.range("E1:E" & lastRow)
Dim rowIndex as long
Dim columnIndex as long
Dim splitString() as string
Dim cumulativeOffset as long
Dim toJoin(0 to 1) as string
toJoin(0) = "pull: test" ' Might speed up string concatenation below'
Dim outputArray() as string
With outputsheet.range("A1") ' The first row you want to start stacking from'
For rowIndex = 1 to lastRow
' Single dimensional, 0-based array'
splitString = VBA.strings.split(vba.strings.lcase$(arrayOfValues(rowIndex,1)), "pull: test",-1, vbbinarycompare)
Redim outputArray(1 to (ubound(splitString)+1), 1 to 1)
For columnIndex = lbound(splitString) to ubound(splitString)
toJoin(1) = splitString(columnIndex)
Outputarray(columnIndex+1,1) = VBA.strings.join(toJoin, vbnullstring)
Next columnIndex
'Instead of splitting upon a delimiter, then prepending the delimiter to each array element (as is done above), you could repeatedly call instr(), use mid$() to extract the sub-string, then increase the argument passed to the "Start" parameter in instr() (effectively moving from start to end of the string) -- until instr() returns 0. Then move on to the next string in the outer loop.'
.offset(cumulativeOffset,0).resize(Ubound(outputArray, 1), 1).value2 = outputArray
cumulativeOffset = cumulativeOffset + ubound(splitString)
Next rowIndex
End Sub

How to delete rows in Excel based on certain values

I have a workbook with 10 sheets. Each sheet has about 30,000 rows with URL. I have a hand full of URLs (about 10 different URLs) that I need to keep the data. Is there a way to delete all the rows from all the worksheet if the first column (Column A - URL) does not contain one of the URL.
for example, I would like to keep we.abc.us, ss.boli.us and 3m.mark.us and delete rest of the rows from all the worksheet in the workbook.
Sub delete0rows()
Dim Worksheet As Excel.Worksheet
Dim lastRow As Long
Dim i As Integer
For Each Worksheet In Application.ThisWorkbook.Worksheets
lastRow = Worksheet.Cells(Rows.Count, 1).End(xlUp).Row
i = 1
Do While i <= lastRow
If Worksheet.Range("A" & i).Value = 0 Then
Worksheet.Rows(i).Delete i = i - 1
lastRow = lastRow - 1
End
i = i + 1
Loop
Next Worksheet
End Sub
I suggest you introduce reverse For loop using Step -1:
Sub delete0rows()
Dim Worksheet As Excel.Worksheet
Dim lastRow As Long
Dim i As Integer
For Each Worksheet In Application.ThisWorkbook.Worksheets
lastRow = Worksheet.Cells(Rows.Count, 1).End(xlUp).Row
For i = lastRow To 1 Step -1
If Worksheet.Range("A" & i).Value = 0 Then
Worksheet.Rows(i).EntireRow.Delete
End If
Next i
Next Worksheet
End Sub
I found this sub a while back. I cannot remember who the original author was or I would credit them. I did tweak it slightly to pass variables into it
The nice thing about this is you can pass multiple deletion criteria by passing a space separated string
Essentially you can give it a row to start at (in case you have headers) tell it the column to look in, the sheet that column is on and your criteria/criterion. So for example if I want it to start at row 5 checking each row below that on a sheet named 'cleanup' checking column 'D' for the words 'cat' 'dog' and 'fish' I would write
Call DelRow(5,"D","cleanup","cat dog fish")
Public Sub DelRow(DataStartRow As Long, SearchColumn As String, SheetName As String, myTextString As String)
' This macro will delete an entire row based on the presence of a predefined word or set of words.
'If that word or set of words is 'found in a cell, in a specified column, the entire row will be 'deleted
'Note the seperator is a space. To change this modify the split parameter
'EXAMPLE CALL: Call DelRow(1, "AH", "Cut Data", "DEL")
Dim X As Long
Dim Z As Long
Dim LastRow As Long
Dim FoundRowToDelete As Boolean
Dim OriginalCalculationMode As Integer
Dim RowsToDelete As Range
Dim SearchItems() As String
SearchItems = Split(myTextString)
On Error GoTo ResetCalcs
OriginalCalculationMode = Application.Calculation
Application.Calculation = xlCalculationManual
With Worksheets(SheetName)
LastRow = .Cells(.Rows.Count, SearchColumn).End(xlUp).Row
Application.StatusBar = "**** Working on the '" & SheetName & "' Sheet: Number of Rows to be scanned(" & LastRow & "). Deletion keyword " & myTextString & " ***" 'Extra line added
For X = LastRow To DataStartRow Step -1
FoundRowToDelete = False
For Z = 0 To UBound(SearchItems)
If InStr(.Cells(X, SearchColumn).Value, SearchItems(Z)) Then
FoundRowToDelete = True
Exit For
End If
Next
If FoundRowToDelete Then
If RowsToDelete Is Nothing Then
Set RowsToDelete = .Cells(X, SearchColumn)
Else
Set RowsToDelete = Union(RowsToDelete, .Cells(X, SearchColumn))
End If
If RowsToDelete.Areas.Count > 100 Then
RowsToDelete.EntireRow.Delete
Set RowsToDelete = Nothing
End If
End If
Next
End With
If Not RowsToDelete Is Nothing Then
RowsToDelete.EntireRow.Delete
End If
ResetCalcs:
Application.Calculation = OriginalCalculationMode
End Sub

VBA to keep certain columns and remove all others

As topic says I have a vba code which does the job: remove all columns of sheet "incidents" and keep only the columns with names "Status", "Name" and "Age"
But for some reason, after a few thousand rows, it does not work properly and removes the data/cells of that columns that is keeping , and also works quite slow.
There is any other way to do this? More efective? At least to not remove any cell of that columns which must remain in the sheet.
Thanks in advance (code below).
Sub Cleanup_report2()
Dim currentColumn As Integer
Dim columnHeading As String
For currentColumn = Worksheets("Incidents").UsedRange.Columns.CounT To 1 Step -1
columnHeading = Worksheets("Incidents).UsedRange.Cells(1, currentColumn).Value
'CHECK WHETHER TO KEEP THE COLUMN
Select Case columnHeading
Case "Status", "Name", "Age"
'Do nothing
Case Else
If InStr(1, _
Worksheets("Incidents").UsedRange.Cells(1, currentColumn).Value, _
"DLP", vbBinaryCompare) = 0 Then
Worksheets("Incidents").Columns(currentColumn).Delete
End If
End Select
Next
End Sub
It should be quicker to only do one delete operation:
Sub Cleanup_report2()
Dim currentColumn As Integer
Dim columnHeading As String
Dim rDelete As Excel.Range
With Worksheets("Incidents_data")
For currentColumn = .UsedRange.Columns.Count To 1 Step -1
columnHeading = .UsedRange.Cells(1, currentColumn).Value
'CHECK WHETHER TO KEEP THE COLUMN
Select Case columnHeading
Case "Status", "Name", "Age"
'Do nothing
Case Else
If InStr(1, columnHeading, "DLP", vbBinaryCompare) = 0 Then
If rDelete Is Nothing Then
Set rDelete = .UsedRange.Cells(1, currentColumn)
Else
Set rDelete = Union(rDelete, .UsedRange.Cells(1, currentColumn))
End If
End If
End Select
Next
End With
If Not rDelete Is Nothing Then rDelete.EntireColumn.Delete
End Sub
Here is my anser... hope this helps...
Sub removeColumns()
Dim rng As Range 'store the range you want to delete
Dim c 'total count of columns
Dim i 'an index
Dim j 'another index
Dim headName As String 'The text on the header
Dim Status As String 'This vars is just to get the code cleaner
Dim Name As String
Dim Age As String
Dim sht As Worksheet
Status = "Status"
Name = "Name"
Age = "Age"
Set sht = Sheets("Incidents")
sht.Activate 'all the work in the sheet "Incidents"
c = Range("A1").End(xlToRight).Column
'From A1 to the left at the end, and then store the number
'of the column, that is, the last column
j = 0 'initialize the var
For i = 1 To c 'all the numbers (heres is the columns) from 1 to c
headName = Cells(1, i).Value
If (headName <> Status) And (headName <> Name) And (headName <> Age) Then
'if the header of the column is differente of any of the options
j = j + 1 ' ini the counter
If j = 1 Then 'if is the first then
Set rng = Columns(i)
Else
Set rng = Union(rng, Columns(i))
End If
End If
Next i
rng.Delete 'then brutally erased from leaf
End Sub

EXCEL - Look up a value in a list and return multiple corresponding values

I am trying to create a Tree Traversal in Excel for a schedule I have. I am at the point where I have 2 lists each 1006 cells long. The first is predecessors, the second is successors. I am trying to use a set of functions to display multiple results. For instance if I enter 3, I want all of the successors of task 3 to get listed. So far the code I have come up with is:
=IF(ISERROR(INDEX($A$1:$B$1006,SMALL(IF($A$1:$A$1006=$E$3,ROW($A$1:$A$1006)),ROW(1:1)),2)),"NO",INDEX($A$1:$B$1006,SMALL(IF($A$1:$A$1006=$E$3,ROW($A$1:$A$1006)),ROW(1:1)),2))
However when I input the predecessor, it does not display the correct successor.
Thank you in advance for whoever can help me
You cannot join values with formulas (or at least, i can't see an easy way to do it).
You can either call a procedure (faster but more intrusive):
Option Explicit
Sub Proc_ListPre()
Dim rData As Range, lLastrow As Long, i As Integer
Dim aValues() As Variant
Dim sFilter As String, sRes As String
'Ask for the value to filter to the user
sFilter = InputBox("Which predecessor do you want to analyse?", "Please type the predecessor you want")
If Len(sFilter) = 0 Then Exit Sub
'Define the range
'either use UsedRange (if only columns A and B are used)
'Set rData = ActiveSheet.UsedRange
'or use End(xlUp) if not
lLastrow = ActiveSheet.Range("a65536").End(xlUp).Row
Set rData = ActiveSheet.Range("A1:B" & lLastrow)
'Filter the predecessor with the criteria given in arg
rData.AutoFilter Field:=1, Criteria1:=sFilter
'Find the last row of the filtered data
lLastrow = ActiveSheet.Range("a65536").End(xlUp).Row
aValues = ActiveSheet.Range("A2:B" & lLastrow).Value
'Join the 2nd column of the array
'Join(WorksheetFunction.Index(aValues, 0, 2), ";") 'note that this doesn't work because index returns a 2D array
'Workaround to join the 2nd column
For i = 1 To UBound(aValues, 1)
If Len(CStr(aValues(i, 2))) > 0 Then
sRes = sRes & aValues(i, 2) & ";"
End If
Next
sRes = Left(sRes, Len(sRes) - 1)
MsgBox sRes
ActiveSheet.AutoFilterMode = False
End Sub
or use a formula that you will call in your worksheet as =ListPre(mypredecessor)
Function ListPre(ByVal sFilter As String)
Dim rData As Range, lLastrow As Long, i As Integer
Dim aValues() As Variant
Dim sRes As String
'Define the range
'either use UsedRange (if only columns A and B are used)
'Set rData = ActiveSheet.UsedRange
'or use End(xlUp) if not
lLastrow = ActiveSheet.Range("a65536").End(xlUp).Row
Set rData = ActiveSheet.Range("A1:B" & lLastrow)
aValues = ActiveSheet.Range("A2:B" & lLastrow).Value
'Join the 2nd column of the array
'Join(WorksheetFunction.Index(aValues, 0, 2), ";") 'note that this doesn't work because it returns a 2D array
'Workaround to join the 2nd column
For i = 1 To UBound(aValues, 1)
If Len(CStr(aValues(i, 2))) > 0 And CStr(aValues(i, 1)) = sFilter Then
sRes = sRes & aValues(i, 2) & ";"
End If
Next
sRes = Left(sRes, Len(sRes) - 1)
ListPre = sRes
End Function

Resources