I have a worksheet, and the worksheet has multiple columns,multiple rows. How can I create a script such that, if a cell in any of those columns does not have a phrase, for example,'cat', the whole row gets deleted?
There are many solutions online, but they usually ask you to define a range, such as which column you want to search in and until how many rows. I can't have these restrictions as my different worksheets have different columns and different number of rows, but the same concept, where I delete a row if a certain phrase isn't existent in that row.
I am assuming that the word cat must appear somewhere in the row. If the row is completely "feline-free" then that row will be deleted. Here is a typical approach:
Sub KeepOnlyCatRows()
Dim i As Long, N As Long, s As String, c As String
N = Cells(Rows.Count, "A").End(xlUp).Row
c = Chr(1)
For i = N To 1 Step -1
s = c & Application.WorksheetFunction.TextJoin(c, True, Rows(i)) & c
If InStr(s, c & "cat" & c) = 0 Then
Rows(i).Delete
End If
Next i
End Sub
NOTES:
If the word cat must appear in every cell in the row, then ignore this answer.
I am assuming that the "cat-cell" contains only the word cat and no other text.
EDIT#1:
In order to allow an arbitrary word rather than cat, try the following:
Sub KeepOnlySpecialRows()
Dim i As Long, N As Long, s As String, c As String
Dim SpecialWord As String
N = Cells(Rows.Count, "A").End(xlUp).Row
c = Chr(1)
SpecialWord = Application.InputBox(Prompt:="Enter the special word:", Type:=2)
For i = N To 1 Step -1
s = c & Application.WorksheetFunction.TextJoin(c, True, Rows(i)) & c
If InStr(s, c & SpecialWord & c) = 0 Then
Rows(i).Delete
End If
Next i
End Sub
Related
This question already has answers here:
If Cell Contains This or That Paste onto Another Sheet
(2 answers)
Closed 3 years ago.
I'm a beginner when it comes to VBA and Macros; therefore, I'm not sure what the exact verbiage is but I believe I'm looking for help with looping.
My macro currently partially matches cells in each row in "SheetJS" containing either "Mercedes-Benz" or "BMW" and pastes the values to Column D in "Sheet1". However, it only copies the first iteration/cell that partially matches the text.
I want the macro to copy and past all matches. For example the 1st iteration should be copied/pasted to "Sheet1" Column D, 2nd in Column H, 3rd in L, and so on. Each iteration should have 3 cells in between.
I don't even know how to move forward with this.
Any tips would be greatly appreciated.
Thanks!
Sub Extract_Data_or()
For Each cell In Sheets("SheetJS").Range("A1:ZZ200")
matchrow = cell.Row
If (cell.Value Like "*Mercedez-Benz*") Or (cell.Value Like "*BMW*") Then
Sheets("Sheet1").Range("D" & matchrow).Value = cell.Value
End If
Next
End Sub
Edit 01.09.20
I want the macro to get all iterations/partial matches in each row and copy them. The current macro only copies the first match. I don't want to copy the entire row just the individual cells.
For example the first match in "SheetJS" should be copied to Column D in "Sheet1". The second match, (if any) should be copied to Column H, 3rd in column L, 4th in column P, etc. Every match should be placed 4 cells from each other.
SheetJS
All matches are highlighted in yellow. The values in each cell should copied over to "Sheet1"
Sheet1
The first match in each row is in Column D, the 2nd( if any) is in Columb H, etc.
You asked yesterday something similar. I asked for clarifications and I supplied a solution without receiving any sign from you...
Anyhow, maybe this time you will look at the next code and maybe test it. It works very fast, avoiding cells iteration. It works only in memory:
Private Sub Extract_Data_Bis()
Dim rngArr As Variant, dArr As Variant
Dim sh As Worksheet, i As Long, j As Long, k As Long
Dim lngOcc As Long, lngChanges As Long, boolFound As Boolean
Dim lngSameRow As Long, lngMised As Long
Set sh = Sheets("Sheet1")
rngArr = Sheets("SheetJS").Range("A1:ZZ200").Value
dArr = sh.Range("D1:F200").Value
For i = 1 To UBound(rngArr, 1)
boolFound = False: k = 0: lngSameRow = 0
For j = 1 To UBound(rngArr, 2)
If InStr(UCase(rngArr(i, j)), UCase("Mercedez-Benz")) > 0 Or _
InStr(UCase(rngArr(i, j)), "BMW") > 0 Then
If Not boolFound Then
lngSameRow = i
k = 1
Else
If lngSameRow = i Then
k = k + 1
End If
End If
lngOcc = lngOcc + 1: boolFound = True
If k <= 3 Then
dArr(i, k) = rngArr(i, j)
lngChanges = lngChanges + 1
Else
lngMised = lngMised + 1
End If
End If
Next j
Next i
sh.Range("D1:F200").Value = dArr
MsgBox lngOcc & " occurrences, versus " & lngChanges & " changes done. " & lngMised & " missed..."
End Sub
In case there are more then 3 occurrences on a row, at the end it makes a balance between occurrences, changes done and missed ones...
I have been stuck on this for weeks and have tried many formula combinations but can't get this to work. I don't know VBA so don't know where to start there.
I have List 1 and List 2 below. I need List 3 to be created from the data in Lists 1 and 2. List 3 can, preferably, be created in a new sheet.
I need to lookup the criteria from Column A, in List2 (Column D) then return all matches in a new list that shows: List 1; the criteria (Column A), data in Column B; and all matches from List 2 (Column E)
See Below. List 3 is the outcome
I broke this into two parts and I tried using a formula that copied the row the amount of times that there was a match. Then I was going to copy paste or find some vba or formula to combine the table but I came to a dead end when I realized the they tables weren't sorted in the same order. I ended up with these two lists to combine
Tried this VBA
Getting this error
Try This.
Run the macro "Test"
The first parameter should be the range of your first list (Just the numbers)
The second parameter should be the range of your second list (Just the numbers)
OutputSheet should be the sheet you want to output the list on
You can also optionally set the output row and output column (It will start at A1 if you don't specify)
Sub CreateList(List1 As Range, List2 As Range, OutputSheet As Worksheet, Optional ORow As Long = 1, Optional OCol As Long = 1)
Dim c, d
For Each c In List1
For Each d In List2
If c = d Then
OutputSheet.Cells(ORow, OCol).Value = c.Value
OutputSheet.Cells(ORow, OCol + 1).Value = c.Offset(0, 1).Value
OutputSheet.Cells(ORow, OCol + 2).Value = d.Offset(0, 1).Value
ORow = ORow + 1
End If
Next d
Next c
End Sub
Sub Test()
With Sheets("Sheet1")
CreateList .Range("A2:A7"), .Range("D2:D6"), Sheets("Sheet2")
End With
End Sub
The code loops through each number in the first list, and then each number in the second list.
If the numbers are the same, it outputs the number, the item, and the price.
First it will check If 10 = 10 Then - output the number, output the text next to the number on the first list, and output the amount next to the number on the second list.
Then it increases the row by 1.
That's pretty much all there is to it - just make sure you specify the ranges properly and change the sheet references as needed.
If you have never used VBA before, you can open the window by pressing ALT+F11
Right click to the left side and select Insert -> Module
Paste the code into the right side.
Update the ranges on the following line so they match where your lists are:
CreateList .Range("A2:A7"), .Range("D2:D6"), Sheets("Sheet2")
You can then close the window and press ALT+F8 to open the Run Macro dialog.
Select Test and click Run
Input:
Results:
What about this?
The code below assumes that on Sheet1, data starts from Row2 where Row1 is the header row.
Sub CreateList()
Dim x, y, z()
Dim i As Long, j As Long, k As Long, n As Long, dlr As Long
Dim wsData As Worksheet, wsOutput As Worksheet
Application.ScreenUpdating = False
Set wsData = Sheets("Sheet1")
On Error Resume Next
Set wsOutput = Sheets("List")
wsOutput.Cells.Clear
On Error GoTo 0
If wsOutput Is Nothing Then
Sheets.Add(after:=wsData).Name = "List"
Set wsOutput = ActiveSheet
End If
x = wsData.Range("A1").CurrentRegion.Value
y = wsData.Range("D1").CurrentRegion.Value
For i = 2 To UBound(x, 1)
If Application.CountIf(wsData.Columns("D"), x(i, 1)) > 0 Then
n = Application.CountIf(wsData.Columns("D"), x(i, 1))
ReDim z(1 To n)
k = 1
For j = 2 To UBound(y, 1)
If y(j, 1) = x(i, 1) Then
z(k) = y(j, 2)
k = k + 1
End If
Next j
dlr = wsOutput.Range("C" & Rows.Count).End(3)(2).Row
wsOutput.Range("A" & dlr).Value = x(i, 1)
wsOutput.Range("B" & dlr).Value = x(i, 2)
wsOutput.Range("C" & dlr).End(3)(2).Resize(UBound(z, 1), 1) = Application.Transpose(z)
End If
Erase z
Next i
dlr = wsOutput.Range("C" & Rows.Count).End(3)(2).Row
If dlr > 1 Then
wsOutput.Range("A2:C" & dlr).CurrentRegion.SpecialCells(xlCellTypeBlanks).FormulaR1C1 = "=R[-1]C"
wsOutput.Rows(1).Delete
End If
Application.ScreenUpdating = True
End Sub
I have a workbook with cells that have linebreaks (entered via ALT + ENTER).
I have to separate them into individual rows. All the cells are in column A.
Each line in the cell has a bullet point (eg. "* ") up front, which could serve as a beacon to break the line at this point.
You can use split with Chr(10) or VbLf
Dim cell_value As Variant
Dim counter As Integer
'Row counter
counter = 1
'Looping trough A column define max value
For i = 1 To 10
'Take cell at the time
cell_value = ThisWorkbook.ActiveSheet.Cells(i, 1).Value
'Split cell contents
Dim WrdArray() As String
WrdArray() = Split(cell_value, vbLf)
'Place values to the B column
For Each Item In WrdArray
ThisWorkbook.ActiveSheet.Cells(counter, 2).Value = Item
counter = counter + 1
Next Item
Next i
No you have array to place each row to different cell
There is no need of code for this, lets make it simple.
Follow the bellow steps.
Select the data-set you want to split -> Go to Data Tab -> Select "Text to columns" -> from this pop-up select "Delimited" -> Select which delimiter is separating your texts -> Select the destination cell -> Click "OK"
Try This.
Regards,
Ashwin
Edit from Markus: For the newline as delimiter use "Ctr-J"
If you select the cell and run the macro you would get what you want on the next column like this:
Option Explicit
Public Sub selection_into_rows()
Dim k As Variant
Dim l_counter As Long
k = Split(Selection, Chr(10))
For l_counter = LBound(k) To UBound(k)
Cells(l_counter + 1, Selection.Column + 1) = k(l_counter)
Next l_counter
End Sub
This will work on one row only after selecting it (but should get you started):
Option Explicit
Public Sub SelectionIntoRows()
Dim k() As String
Dim l As Long
Dim i As Long
k() = Split(Range("A1"), " ")
i = 1
For l = 0 To UBound(k)
Cells(i, 1) = k(l)
i = i + 1
Next l
End Sub
Sub extract()
'Query extract data in cell B divided by ALT+Enter, Comma space
'Mandatory to create in front Sheet1, Sheet2, and Sheet3
'ATTENTION! if field B is empty return no data!! Manually add column A (with empty column B)if needed!!
'manually remove empty cell in results (Sheet2)
'before START Query remove duplicate from input data!!
'Doesn't work with full stop
'When finished Msg Done will be display
Dim c As Long, r As Range, I As Long, d As Long, Temp() As String
d = 0
For Each r In Range("B2:B" & Range("B" & Rows.Count).End(xlUp).Row) ' Change this to suit your range..
c = 2
Temp = Split((r.Value), Chr(10))
For i = LBound(Temp) To UBound(Temp)
Sheets("Sheet2").Cells(r.Row, c - 1).Offset(d, 0).Value = Cells(r.Row, r.Column - 1).Value
Sheets("Sheet2").Cells(r.Row, c).Offset(d, 0).Value = Temp(i)
Cells(r.Row, c).Offset(d, 0).Select
ActiveCell.Value = Trim(ActiveCell.Value)
d = d + 1
Next
d = d - 1
Next
Sheets("Sheet2").Select
Columns("A:B").Select
ActiveSheet.Range("$A$1:$B$62856").RemoveDuplicates Columns:=Array(1, 2), _
Header:=xlYes
Range("A1").Select
I had a half dozen of these blobs from an poor Acrobat PDF to XLSX conversion, peppered throughout 500 rows.
I copied the text blob (like OP) into Notepad, removed "" from the start and end line, and pasted to a new spreadsheet. That tells me how many lines to insert. Then pasted into that hole.
"This was all of
my input text"
Then I could use TextToCol.
I am writing a macro in excel for work and I am having trouble. In this scenario there are two sheets, "BU" and "TOPS Information". When the macro is used it is supposed to search every line of "BU" for the value found in "TOPS Information", then go to the next line of "TOPS Information and repeat the process. If it finds a correct match it is supposed to copy a cell and paste it into "TOPS Information".
Here is the code:
Sub QIM()
Dim j As Integer
Dim k As Integer
Dim i As Integer
Dim l As Integer
Dim m As Integer
Dim searchArray(1 To 3) As String
j = 0
k = 1
'WARNING: Temporary Sheet Names
lastRowTOPS = Worksheets("TOPS Information").Cells(Rows.Count, "A").End(xlUp).Row
lastRowBU = Worksheets("BU").Cells(Rows.Count, "A").End(xlUp).Row
'Cycle through BU rows
For j = lastRowTOPS To 1 Step -1
'Cycle through searchArray for each BU row
For k = lastRowBU To 1 Step -1
'//////////////////////////////////////
x = Sheets("BU").Range("B" & k).Value
y = Range("C" & j).Value
If StrComp(x, y) = 1 Then
Sheets("BU").Range("C" & k).Copy
Range("H" & j).PasteSpecial
End If
'//////////////////////////////////////
Next k
Next j
End Sub
This Macro obviously only works if "TOPS Information" is selected at the time. Any and all help would be most appreciated. THANKS!
You sorta answered it yourself. Range refers to the current sheet, but when you're bouncing around then you have to qualify it.
Prefix your ranges with the appropriate sheet like so,
Sub QIM()
Dim j As Integer
Dim k As Integer
Dim i As Integer
Dim l As Integer
Dim m As Integer
Dim searchArray(1 To 3) As String
j = 0
k = 1
'WARNING: Temporary Sheet Names
lastRowTOPS = Worksheets("TOPS Information").Cells(Rows.Count, "A").End(xlUp).Row
lastRowBU = Worksheets("BU").Cells(Rows.Count, "A").End(xlUp).Row
'Cycle through BU rows
For j = lastRowTOPS To 1 Step -1
'Cycle through searchArray for each BU row
For k = lastRowBU To 1 Step -1
'//////////////////////////////////////
x = Sheets("BU").Range("B" & k).Value
y = Sheets("TOPS Information").Range("C" & j).Value
If StrComp(x, y) = 1 Then
Sheets("BU").Range("C" & k).Copy
Sheets("TOPS Information").Range("H" & j).PasteSpecial
End If
'//////////////////////////////////////
Next k
Next j
End Sub
Assuming only want to copy the top most found data in BU to TOPS, you can use below.
Sub QIM()
Dim oWS_TOPS As Worksheet, oWS_BU As Worksheet ' Worksheet objects
Dim oRng_TOPS As Range, oRng_BU As Range ' Range objects
Dim R_TOPS As Long, R_BU As Long
Set oWS_TOPS = ThisWorkbook.Worksheets("TOPS Information") ' <-- Replace this "TOPS Information" to match future changes
Set oWS_BU = ThisWorkbook.Worksheets("BU") ' <-- Replace this "BU" to match future changes
R_TOPS = oWS_TOPS.Cells(Rows.Count, "A").End(xlUp).Row
R_BU = oWS_BU.Cells(Rows.Count, "A").End(xlUp).Row
' Search column B of BU for each cell in column C of TOPS
For Each oRng_TOPS In oWS_TOPS.Columns("C").Cells ' <-- Replace this "C" to match future changes
' Exit if row is more than last A column data
If oRng_TOPS.Row > R_TOPS Then Exit For
For Each oRng_BU In oWS_BU.Columns("B").Cells ' <-- Replace this "B" to match future changes
' Exit if row is more than last A column data
If oRng_BU.Row > R_BU Then Exit For
' Check if Ranges match (## See Update ##)
If InStr(1, oRng_TOPS.Value, oRng_BU.Value, vbTextCompare) > 0 Then
' Copy column C of found row in BU to column H of TOPS, then exit
oWS_BU.Cells(oRng_BU.Row, "C").Copy oWS_TOPS.Cells(oRng_TOPS.Row, "H") ' <-- Replace these "C" and "H" to match future changes
Exit For
End If
Next
Next
Set oWS_TOPS = Nothing
Set oWS_BU = Nothing
End Sub
There are many ways to achieve your goal, and this is one of it.
UPDATE Note on comparing cell values (String):
StrComp(S1,S2[,mode]) only return 3 values {-1, 0, 1} to indicate if S1 is less/equal/greater than S2. If you want an exact match (case sensitive and exact spacing), use If StrComp(S1,S2) = 0 Then.
InStr([i,]S1,S2[,mode]) only returns positive values - it returns the character location of first appearance of S2 in S1. If S2 is not found then it returns zero.
You can also use Trim(sText) to remove leading/ending spaces of sText.
Hope below screenshot says more.
Suppose I have a column of arbitrary length where each cell contains a string of text. Is there a way to determine what words appear most frequently in the column (not knowing in advance which words to check) and subsequently order these words along with their frequencies in a two column table? Would VBA be best for this task?
As an example, a cell might contain the string "This is a string, and the # of characters inthis string is>0." (errors intentional)
Select a portion of column A and run this small macro ( the table will be placed in cols. B & C :
Sub Ftable()
Dim BigString As String, I As Long, J As Long, K As Long
BigString = ""
' Add code to sum both "All" and "all"
' Add code to separate "." "!" etc. from the word preceeding them so that word
' is also counted in the total. For example: "all." should not be reported as 1 ' "all." but "all" be added to the total count of "all" words.
' Would you publish this new code?
For Each r In Selection
BigString = BigString & " " & r.Value
Next r
BigString = Trim(BigString)
ary = Split(BigString, " ")
Dim cl As Collection
Set cl = New Collection
For Each a In ary
On Error Resume Next
cl.Add a, CStr(a)
Next a
For I = 1 To cl.Count
v = cl(I)
Cells(I, "B").Value = v
J = 0
For Each a In ary
If a = v Then J = J + 1
Next a
Cells(I, "C") = J
Next I
End Sub
Given this:
I'll use a pivot table to get this:
Best part is, if I got more, it's easy to get Top 5, 10, etc. And it'll always result to unique indices. From there, there are all manners of editing and calculation you can do. :)
Using Google Sheets:
index((Transpose(ArrayFormula(QUERY(TRANSPOSE(SPLIT(JOIN(" ",$B$2)," ")&{"";""}),"select Col1, count(Col2) group by Col1 order by count(Col2) desc limit 20 label Col1 'Word', count(Col2) 'Frequency'",0)))),1,$A6+1)&":"&index((Transpose(ArrayFormula(QUERY(TRANSPOSE(SPLIT(JOIN(" ",$B$2)," ")&{"";""}),"select Col1, count(Col2) group by Col1 order by count(Col2) desc limit 20 label Col1 'Word', count(Col2) 'Frequency'",0)))),2,$A6+1)
In the above $B$2 contains the text string
$A6 = 1 will give you the most used word
$A6 = 2 will give you the second most used word
etc.
This is set to do 20 most frequent. If you want more, increase the limit value to whatever you want.
Here's a tiny fix plus an enhancement to the script kindly offered by "Gary's Student". The fix is that while building the collection is apparently not case-sensitive (and this is correct--we probably don't want new items added to the collection that differ only in case from existing items), the IF statement that does the counting IS case-sensitive as written, so it doesn't count correctly. Just change that line to...
If LCase(a) = LCase(v) Then J = J + 1
And here's my enhancement. To use it, you first select one or more columns but NOT their (first) header/label rows. Then run the script, and it gives results for each selected column in a new worksheet--along with that header/label row so you know what you're looking at.
I'm just a dabbler. I just hack stuff when I need to get a job done, so it's not elegant, I'm sure...
Sub FrequencyV2() 'Modified from: https://stackoverflow.com/questions/21858874/counting-the-frequencies-of-words-in-excel-strings
'It determines the frequency of words found in each selected column.
'Puts results in new worksheets.
'Before running, select one or more columns but not the header rows.
Dim rng As Range
Dim row As Range
Dim col As Range
Dim cell As Range
Dim ws As Worksheet
Dim wsNumber As Long 'Used to put a number in the names of the newly created worksheets
wsNumber = 1
Set rng = Selection
For Each col In rng.Columns
Dim BigString As String, I As Long, J As Long, K As Long
BigString = ""
For Each cell In col.Cells
BigString = BigString & " " & cell.Value
Next cell
BigString = Trim(BigString)
ary = Split(BigString, " ")
Dim cl As Collection
Set cl = New Collection
For Each a In ary
On Error Resume Next 'This works because an error occurs if item already exists in the collection.
'Note that it's not case sensitive. Differently capitalized items will be identified as already belonging to collection.
cl.Add a, CStr(a)
Next a
Set ws = Sheets.Add(After:=Sheets(Sheets.Count))
ws.Name = "F" & CStr(wsNumber)
wsNumber = wsNumber + 1
Worksheets(ws.Name).Cells(1, "A").Value = col.Cells(1, 1).Offset(-1, 0).Value 'Copies the table header text for current column to new worksheet.
For I = 1 To cl.Count
v = cl(I)
Worksheets(ws.Name).Cells(I + 1, "A").Value = v 'The +1 needed because header text takes up row 1.
J = 0
For Each a In ary
If LCase(a) = LCase(v) Then J = J + 1
Next a
Worksheets(ws.Name).Cells(I + 1, "B") = J 'The +1 needed because header text takes up row 1.
Next I
Next col
End Sub