I have some coding which loops through a list of policy numbers, and then searches through an entire workbook for a matching policy number, bolding any matches found in the original list. The coding works fine, however what I would like to do if possible, is to 'print' the address/cell location of the matching value. For example, if policy number 1 was located on sheet 3, cell a1, then this would be displayed on a new blank worksheet. I'm sure this can be done using dictionaries and the print.debug function, But I want to avoid using a dictionary if possible. Hope this makes sense!
Sub HighlightMatches()
Application.ScreenUpdating = True
Dim var As Variant, iSheet As Integer, iRow As Long, iRowL As Long,bln As Boolean
iRowL = Sheets(3).Cells(Rows.Count, "P").End(xlUp).row
For iRow = 1 To iRowL
If Not IsEmpty(Cells(iRow, 16)) Then
For iSheet = ActiveSheet.Index + 1 To Worksheets.Count
bln = False
var = Application.match(Cells(iRow, 16).Value, Worksheets(iSheet).Columns(16), 0)
If Not IsError(var) Then
bln = True
Exit For
End If
Next iSheet
End If
' The below would 'print' all matches and an offset value to a new Worksheet.
If bln = False Then
Cells(iRow, 16).Font.Bold = False
Else
Cells(iRow, 16).Font.Bold = True
End If
Next iRow
Application.ScreenUpdating = True
End Sub
This would do it:
Option Explicit
Sub HighlightMatches()
Dim OriginalWS As Worksheet, NewListWorksheet As Worksheet, ws As Worksheet
Dim Cel As Range, Rng As Range, rSearchCell As Range
Set OriginalWS = ActiveSheet
Set Rng = Columns("P:P").SpecialCells(xlCellTypeConstants, 23)
For Each Cel In Rng
For Each ws In ActiveWorkbook.Worksheets
If ws.Name <> OriginalWS.Name And ws.Name <> "Search List" Then
On Error Resume Next
Set rSearchCell = ws.Columns("P:P").Find(What:=Cel.Value, After:=ActiveCell, LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False).Activate
On Error GoTo 0
If Not rSearchCell Is Nothing Then
Cel.Font.Bold = True
Sheets.Add(After:=Sheets(Sheets.Count)).Name = "Search List"
On Error Resume Next
Set NewListWorksheet = ActiveSheet
On Error GoTo 0
If Not NewListWorksheet Is Nothing Then
NewListWorksheet.Range("A" & NewListWorksheet.Range("A" & NewListWorksheet.Rows.Count).End(xlUp).Row + 1).Value = Cel.Value
Else
Set NewListWorksheet = Sheets.Add.Name = "Search List"
NewListWorksheet.Range("A1").Value = "Search results"
NewListWorksheet.Range("A1").Font.Bold = True
NewListWorksheet.Range("A2").Value = Cel.Value
End If
Else
Cel.Font.Bold = False
End If
End If
Set rSearchCell = Nothing 'Clear Cel
Next
Next
End Sub
Related
I'm am trying to automatically filter a column and copy/paste all the unique values to a new sheet each. Here's the code that I have been working with, however I'm facing this error when running the code:
Run-time error '1004': The extract range has a missing or invalid field name.
Sub Filter()
Application.ScreenUpdating = False
Dim x As Range
Dim rng As Range
Dim last As Long
Dim sht As String
'specify sheet name in which the data is stored
sht = "Filter This"
'change filter column in the following code
last = Sheets(sht).Cells(Rows.Count, "C").End(xlUp).Row
Set rng = Sheets(sht).Range("A1:H" & last)
Sheets(sht).Range("C1:C" & last).AdvancedFilter _
Action:=xlFilterCopy, _
CopyToRange:=Range("AA1"), _
Unique:=True
For Each x In Range([AA2], Cells(Rows.Count, "AA").End(xlUp))
With rng
.AutoFilter
.AutoFilter Field:=3, Criteria1:=x.Value
.SpecialCells(xlCellTypeVisible).Copy
Sheets.Add(After:=Sheets(Sheets.Count)).Name = x.Value
ActiveSheet.Paste
End With
Next x
'Turn off filter
Sheets(sht).AutoFilterMode = False
With Application
.ScreenUpdating = True
.CutCopyMode = False
End With
End Sub
You can use collections to filter out the unique items, instead of using the advanced filter.
Sub UsingCollection()
Dim cUnique As Collection, ws As Worksheet, fRng As Range
Dim Rng As Range
Dim Cell As Range
Dim sh As Worksheet
Dim vNum As Variant
Set sh = ThisWorkbook.Sheets("Filter This")
Set Rng = sh.Range("C2:C" & sh.Cells(sh.Rows.Count, "C").End(xlUp).Row)
Set cUnique = New Collection
On Error Resume Next
For Each Cell In Rng.Cells
cUnique.Add Cell.Value, CStr(Cell.Value)
Next Cell
On Error GoTo 0
For Each vNum In cUnique
With sh
Set fRng = .Range("C1:H" & .Cells(.Rows.Count, "C").End(xlUp).Row)
End With
Set ws = Sheets.Add(After:=Sheets(Sheets.Count))
With ws
.Name = vNum
With fRng
.AutoFilter Field:=3, Criteria1:=vNum
fRng.Copy ws.Range("A1")
End With
.AutoFilterMode = False
End With
Next vNum
End Sub
The title was hard to keep true to what I need in full so please read all the text.
I am trying to use a button to create a sheet that will show users all the comments on all the worksheets in an easy to understand format that basically acts as the highlights of the workbook.
The issue is the code currently shows ALL comments but I only wish for the person to see currently visible comments. What do I mean? Not all users can see all sheets or all columns and rows. Certain data is hidden because it does not pertain to them. I only want them to see data that is currently visible on any sheet they have as visible.
Example (NOT A REAL SITUATION); an excel document has 3 sheets (Sheet1, Sheet2, Sheet3). John logs in (using a Select Case VBA that hides data he does not need) and can see Sheet1 and Sheet2 but cannot see specific rows in each sheet such as Row 2 and column F in Sheet1 and Row 5 and Column K in Sheet2. He does not need to see the comments for the rows, columns, and sheets he cannot see.
How can I alter the code below to only display comments for cells he can see?
Note: I did not create this code, merely adopted it as it almost suits my needs.
Sub ShowCommentsAllSheets()
Application.ScreenUpdating = False
Dim commrange As Range
Dim mycell As Range
Dim ws As Worksheet
Dim newwks As Worksheet
Dim i As Long
Set newwks = Worksheets.Add
newwks.Range("A1:E1").Value = _
Array("Sheet", "Address", "Name", "Value", "Comment")
For Each ws In ActiveWorkbook.Worksheets
On Error Resume Next
Set commrange = ws.Cells.SpecialCells(xlCellTypeComments)
On Error GoTo 0
If commrange Is Nothing Then
Else
i = newwks.Cells(Rows.Count, 1).End(xlUp).Row
For Each mycell In commrange
With newwks
i = i + 1
On Error Resume Next
.Cells(i, 1).Value = ws.Name
.Cells(i, 2).Value = mycell.Address
.Cells(i, 3).Value = mycell.Name.Name
.Cells(i, 4).Value = mycell.Value
.Cells(i, 5).Value = mycell.Comment.Text
End With
Next mycell
End If
Set commrange = Nothing
Next ws
newwks.Cells.WrapText = False
newwks.Columns("E:E").Replace What:=Chr(10), _
Replacement:=" ", LookAt:=xlPart, _
SearchOrder:=xlByRows, MatchCase:=False, _
SearchFormat:=False, ReplaceFormat:=False
Application.ScreenUpdating = True
End Sub
I believe I need to add this code to fix the problem:
Comments = 1
For Each MyComments In ActiveSheet.Comments
If MyComments.Visible = True Then
Comments = 0
End If
Next
If Comments = 1 Then
Application.DisplayCommentIndicator = xlCommentAndIndicator
Else
Application.DisplayCommentIndicator = xlCommentIndicatorOnly
End If
However, I am struggling to fit this into the code. How should I proceed?
Code amended to cover visible sheets and then cells which are not hidden.
Sub ShowCommentsAllSheets()
Application.ScreenUpdating = False
Dim commrange As Range
Dim mycell As Range
Dim ws As Worksheet
Dim newwks As Worksheet
Dim i As Long
Set newwks = Worksheets.Add
newwks.Range("A1:E1").Value = _
Array("Sheet", "Address", "Name", "Value", "Comment")
For Each ws In ActiveWorkbook.Worksheets
If ws.Visible = xlSheetVisible Then
On Error Resume Next
Set commrange = ws.Cells.SpecialCells(xlCellTypeComments)
On Error GoTo 0
If Not commrange Is Nothing Then
i = newwks.Cells(Rows.Count, 1).End(xlUp).Row
For Each mycell In commrange
If Not (mycell.EntireRow.Hidden Or mycell.EntireColumn.Hidden) Then
With newwks
i = i + 1
On Error Resume Next
.Cells(i, 1).Value = ws.Name
.Cells(i, 2).Value = mycell.Address
.Cells(i, 3).Value = mycell.Name.Name
.Cells(i, 4).Value = mycell.Value
.Cells(i, 5).Value = mycell.Comment.Text
End With
End If
Next mycell
End If
Set commrange = Nothing
End If
Next ws
newwks.Cells.WrapText = False
newwks.Columns("E:E").Replace What:=Chr(10), _
Replacement:=" ", LookAt:=xlPart, _
SearchOrder:=xlByRows, MatchCase:=False, _
SearchFormat:=False, ReplaceFormat:=False
Application.ScreenUpdating = True
End Sub
I am new to writing macros and trying to write one for work. Below is a piece of code I have been fighting with. I want it to look at sheet "NG304" and find key words listed in column B. If the key words are there, move them to the second spreadsheet "Payroll Detail". Issues i'm having - the code is not going through the whole list and it doesn't seem to be pasting in the next available row on the payroll detail spreadsheet (it will simply paste on top of my header).
Code:
Dim Findme As String, Findwhat As String, c As Range
With ActiveWorkbook.Worksheets("NG304")
For Each c In .Range(.Cells(2, "B"), .Cells(Rows.Count, "B").End(xlUp))
Findwhat = vbNullString
Findme = StrConv(c.Value2, vbProperCase)
Select Case True
Case Findme Like "VCIP"
Findwhat = "VCIP"
Case Findme Like "Company Labor"
Findwhat = UCase(Findme)
Case Else
'do nothing
End Select
If CBool(Len(Findwhat)) Then
With .Parent.Worksheets("NG304")
c.EntireRow.Cut Destination:=Worksheets("Payroll Detail").Range("A" & lastrow + 1)
lastrow = lastrow + 1
End With
End If
Next c
End With
This will filter each value defined in K_WORDS (at the top), and move the rows to the other sheet:
Option Explicit
Public Sub moveKeywordRows()
Const K_WORDS As String = "VCIP,Company Labor" '<------- Defined keywords
Dim wsFrom As Worksheet, wsDest As Worksheet, kw As Variant, i As Long, lr As Long
Set wsFrom = ThisWorkbook.Worksheets("NG304")
Set wsDest = ThisWorkbook.Worksheets("Payroll Detail")
kw = Split(K_WORDS, ",")
Application.ScreenUpdating = False
For i = 0 To UBound(kw)
lr = wsDest.Cells(wsDest.Rows.Count, "B").End(xlUp).Row
With wsFrom.UsedRange
.AutoFilter Field:=2, Criteria1:="=" & kw(i)
.Copy
wsDest.Cells(lr, "A").PasteSpecial xlPasteAll
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
Application.CutCopyMode = False
wsDest.Activate: wsDest.Cells(1, "A").Select
Next
wsDest.UsedRange.EntireColumn.AutoFit
With wsFrom
.Activate 'wsFrom.UsedRange.AutoFilter '.ShowAllData
End With
Application.ScreenUpdating = True
End Sub
This is your posted code, with some adjustments - it seems to be working:
Public Sub moveKeywordRows1()
Dim FindMe As String, FindWhat As String, c As Range, lr As Long, wsDest As Worksheet
Set wsDest = ThisWorkbook.Worksheets("Payroll Detail")
With ThisWorkbook.Worksheets("NG304")
Application.ScreenUpdating = False
For Each c In .Range(.Cells(2, "B"), .Cells(.Rows.Count, "B").End(xlUp))
FindMe = StrConv(c.Value2, vbProperCase)
FindWhat = vbNullString
Select Case UCase(FindMe)
Case "VCIP": FindWhat = "VCIP"
Case UCase("Company Labor"): FindWhat = "Company Labor"
End Select
If Len(FindWhat) > 0 Then
c.EntireRow.Cut Destination:=wsDest.Range("A" & lr + 1)
lr = lr + 1
End If
Next
Application.ScreenUpdating = True
End With
End Sub
I'm trying to delete columns if a certain cell in that column is "0" - I'm looping through all worksheets. I've tried different variations and it all ends up with different results - either nothing is deleted or everything is deleted.
Whole code:
Sub Finalize()
Dim finalform As Worksheet
Dim deletename As String
Dim finalworkbook As Workbook
Dim ws As Worksheet
Dim copyrange As Range
Dim columnloop As Range
Dim finalarray As Variant
Application.DisplayAlerts = False
Application.ScreenUpdating = False
Set finalform = Workbooks(ActiveWorkbook.Name).ActiveSheet
finalarray = Array(finalform.Range("D3").Value, finalform.Range("D4").Value, finalform.Range("D5").Value _
, finalform.Range("D6").Value, finalform.Range("D7").Value, finalform.Range("D8").Value, finalform.Range("D9").Value _
, finalform.Range("D10").Value, finalform.Range("D11").Value, finalform.Range("D12").Value, finalform.Range("D13").Value _
, finalform.Range("D14").Value, finalform.Range("D15").Value)
On Error Resume Next
For a = 3 To 20
If Range("B" & a).Value <> "" Then
Workbooks.Open finalform.Range("B" & a).Value
Set finalworkbook = Workbooks(ActiveWorkbook.Name)
'Find, replace, remove
For Each ws In finalworkbook.Worksheets
'Copy paste values
If Not IsInArray(ws.Name, finalarray) Then
Set copyrange = ws.Cells
copyrange.Copy
copyrange.PasteSpecial xlPasteValues
End If
Application.CutCopyMode = False
Next ws
For Each ws In finalworkbook.Worksheets
'Delete sheets
For b = 3 To 15
deletename = finalform.Range("D" & b).Value
If deletename <> "" Then
finalworkbook.Worksheets(deletename).Delete
End If
Next b
With ws
myChar = Chr(10)
'Enter space
.UsedRange.Replace What:="~~", Replacement:=myChar, LookAt:=xlPart, _
SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, _
ReplaceFormat:=False
For c = .UsedRange.Columns.Count To 1 Step -1
cName = Split(Cells(, c).Address, "$")(1)
nom = .Range(cName & "8").Value
If nom = 0 Then
.Columns(c).EntireColumn.Delete
End If
Next c
End With
Next ws
finalworkbook.Save
finalworkbook.Close
End If
Next a
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
The issue here is how VBA interprets the =0 operation. Here is a quick list. You most likely want to use IsEmpty() as ZygD noted.
IsEmpty(cell.Value) 'Returns true if the cell is empty, or false if it is filled with 0.
cell.Value = 0 'Returns true if the cell is empty and if it is filled with 0, false otherwise.
IsNumeric(cell.Value) 'Returns true if the cell is empty, or if it is filled with any number, false otherwise.
If you're looping through all worksheets, you can even use vbNullStringinstead of 0 or "", as this runs faster.
I have an advanced filter macro to run in excel that filters certain columns for unique data. I have a bunch of workbooks as well, and have certain headers that are identical across these workbooks, but headers in each workbook may differ in columns.
So header 'Stackoverflow' may be Column F in one file, and Column E in another. I just want to alter my code to something generic so it gets filter this column with a particular header no matter which workbook (Instead of filtering e:e, f:f, etc). any input is appreciated.
EDIT: this is my full macro, the part where I filter is a bit further down.
Here is my code:
Sub stkoverflow()
Dim ws As Worksheet
Dim wks As Excel.Worksheet
Dim wksSummary As Excel.Worksheet
Dim y As Range
Dim intRow As Long, i As Integer
Dim r As Range, lr As Long, myrg As Range
For Each ws In ActiveWorkbook.Worksheets
ws.Activate
lr = Cells(Rows.Count, "c").End(3).Row
Set myrg = Range("f2:f" & lr)
myrg.ClearContents
myrg.Formula = "=IFERROR(LEFT(e2,FIND(""_"",e2,1)-1),LEFT(e2,2))"
myrg.Value = myrg.Value
Range("f1").Value = "Test"
Next ws
On Error Resume Next
Set wksSummary = Excel.ActiveWorkbook.Worksheets("Unique data")
On Error GoTo 0
If wksSummary Is Nothing Then
Set wksSummary = Excel.ActiveWorkbook.Worksheets.Add
wksSummary.Name = "Unique data"
End If
For Each wks In Excel.ActiveWorkbook.Worksheets
With wksSummary
If wks.Name <> .Name Then
' THIS SECTION OF CODE IS POINTLESS. 'r' WILL ALWAYS BE DECLARED IRRESPECTIVE OF THE 'IF' STATEMENT
' If Application.WorksheetFunction.CountA(wks.Range("f:f")) Then
' Dim r As Range
' End If
With wksSummary
If wks.Name <> .Name Then
If Application.WorksheetFunction.CountA(wks.Range("a:a")) Then
Set r = .Cells(.Cells(.Rows.Count, 4).End(xlUp).Row + 1, 4)
Set y = .Cells(.Cells(.Rows.Count, 5).End(xlUp).Row + 0, 5)
If WorksheetFunction.CountA(wks.Range("f:f")) > 1 Then
If WorksheetFunction.CountA(wks.Range("a:a")) > 1 Then
wks.Range("f:f").AdvancedFilter xlFilterCopy, , r, True
wks.Range("a:a").AdvancedFilter xlFilterCopy, , y, True
Else
r = "N/A"
y = "N/A"
End If
End If
r.Delete xlShiftUp
End If
' I HAVE INSERTED BLOCK ENDINGS FROM HERE, AND CHANGED THE INDENTING OF THE SUBSEQUENT CODE TO FIT
' The next 4 lines are all inserted
End If
End With
End If
End With
' I have removed 4 x 'tab' indents from all of the code below
Next wks
Range("A1").Value = "File Name "
Range("B1").Value = "Sheet Name "
Range("D1").Value = "Scenario Name"
intRow = 2
For i = 1 To Sheets.Count
If Sheets(i).Name <> ActiveSheet.Name Then
Cells(intRow, 2) = Sheets(i).Name
Cells(intRow, 1) = ActiveWorkbook.Name
intRow = intRow + 1
End If
Next i
End Sub
Here is one way to get the column number of a header
Option Explicit
Public Function hdrCol(ByRef ws As Worksheet, _
ByVal hdrName As String, _
Optional hdrRow As Long = 1, _
Optional matchLtrCase As Boolean = True) As Long
Dim found As Range, foundCol As Long
If Not ws Is Nothing Then
hdrRow = Abs(hdrRow)
hdrName = Trim(hdrName)
If hdrRow > 0 And Len(hdrName) > 0 Then
Set found = ws.UsedRange.Rows.Find(What:=hdrName, _
LookIn:=xlFormulas, _
LookAt:=xlWhole, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlNext, _
matchCase:=matchLtrCase)
If Not found Is Nothing Then foundCol = found.Column
End If
End If
hdrCol = foundCol
End Function
To test it:
Public Sub testHeader()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Worksheets
MsgBox hdrCol(ws, "Stackoverflow")
Next
End Sub
.
Edit:
Changes I'd make to your code (not tested)
Option Explicit
Public Sub stkoverflow()
Dim wb As Workbook, ws As Worksheet, wsSummary As Worksheet, lr As Long
Dim y As Range, r As Range, thisRow As Long, colA As Range, colF As Range
Set wb = ActiveWorkbook
For Each ws In wb.Worksheets
lr = ws.Cells(ws.Rows.Count, "C").End(3).Row
With ws.Range("F2:F" & lr)
.ClearContents
.Formula = "=IFERROR(LEFT(E2,FIND(""_"",E2,1)-1),LEFT(E2,2))"
.Value = .Value
End With
ws.Range("F1").Value = "Test"
If ws.Name = "Unique data" Then Set wsSummary = ws
Next ws
If wsSummary Is Nothing Then
Set wsSummary = wb.Worksheets.Add
wsSummary.Name = "Unique data"
End If
For Each ws In wb.Worksheets
With wsSummary
If ws.Name <> .Name Then
'...
'Determine dynamic columns based on header
Set colA = ws.Columns(hdrCol(ws, "YOUR_HEADER_NAME_FOR_COL_A", 1, True))
Set colF = ws.Columns(hdrCol(ws, "YOUR_HEADER_NAME_FOR_COL_F", 1, True))
If ws.Name <> .Name Then
If Application.WorksheetFunction.CountA(colA) Then
Set r = .Cells(.Cells(.Rows.Count, 4).End(xlUp).Row + 1, 4)
Set y = .Cells(.Cells(.Rows.Count, 5).End(xlUp).Row + 0, 5)
If WorksheetFunction.CountA(colF) > 1 Then
If WorksheetFunction.CountA(colA) > 1 Then
colF.AdvancedFilter xlFilterCopy, , r, True
colA.AdvancedFilter xlFilterCopy, , y, True
Else
r = "N/A"
y = "N/A"
End If
End If
r.Delete xlShiftUp
End If
'...
End If
End If
End With
'...
Next ws
With ActiveSheet 'not sure about the ActiveSheet...
.Range("A1").Value = "File Name "
.Range("B1").Value = "Sheet Name "
.Range("D1").Value = "Scenario Name"
End With
thisRow = 2
For Each ws In wb.Worksheets
If ws.Name <> ActiveSheet.Name Then
ActiveSheet.Cells(thisRow, 2) = ws.Name
ActiveSheet.Cells(thisRow, 1) = wb.Name
thisRow = thisRow + 1
End If
Next
End Sub
'---------------------------------------------------------------------------------------