I have a spreadsheet with employees and data listed. The drop-down in A1 lets someone select the employee and then it hides the rows for all other employees. I want to add the names of supervisors in the drop-down of A1 and have it select only the employees under that supervisor and hide the rest. The number of employees under each supervisor ranges from 3 to 6.
This is what I have to hide the rows when selecting a single employee :
The data is on worksheet1 and the list of employees and supervisors is on sheet2
Private Sub Worksheet_Change(ByVal Target As Range)
Dim v As Variant, i, j As Long
If Intersect(Target, Range("A1")) Is Nothing Then Exit Sub
Application.ScreenUpdating = False
Application.EnableEvents = False
ActiveWindow.FreezePanes = False
UsedRange.Rows.Hidden = False
If IsEmpty(Cells(1, 1)) Then Exit Sub
v = Cells(1, 1).Value
For i = 2 To 40 ' Show/Hide the Analysts rows - Add/Substract to the second number when adding/removing analysts
If Not Cells(i, 1) = v Then Rows(i).Hidden = True
If v = "Select Analysts/Supervisors" Then Rows(i).Hidden = False
Next i
Cells(2, 1).Select
ActiveWindow.FreezePanes = True
Cells(2, 1).Select
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
Anyone got any ideas on how to do this? I'm using Excel 2010
I hope i did right understand your question and excel workbook stucture.
You have to create range on the data sheet (in this example its named range: 'rngSupervisors')
In this range you put your supervisors names (column headers)
under each supervisor you write the employees. Each row, 1 employee.
The supervisors itself must be in your employee list to select it, for drop down.
so the data worksheet has for eaxample:
B1: supervisor1
C1: supervisor2
D1: supervisor3
than the employees
B2: emplyee1_UnderSupervisor1
B2: emplyee2_UnderSupervisor1
B2: emplyee3_UnderSupervisor1
C2: emplyee1_UnderSupervisor2
C2: emplyee2_UnderSupervisor2
.
.
then you have to name this range "B1:D1" as "rngSupervisors"
In this example the data worksheet is the second one. I would advise you to use the named worksheet, or better direct reference it (give name of worksheet in VBA editor, than you can reference it directly).
Here i created the a function that tests if Argument1-string is the advisor of Argument2-string. Than you can use this function to test it, if it is false, and the name dont match, than you can hide the row.
I tried to make minimal modifications on your code (but it could be good improved.. or the method you make it all, could be probably realised with excel filters)
Private Sub Worksheet_Change(ByVal Target As Range)
Dim v As Variant, i, j As Long
If Intersect(Target, Range("A1")) Is Nothing Then Exit Sub
Application.ScreenUpdating = False
Application.EnableEvents = False
ActiveWindow.FreezePanes = False
UsedRange.Rows.Hidden = False
If IsEmpty(Cells(1, 1)) Then Exit Sub
v = Cells(1, 1).Value
For i = 2 To 40 ' Show/Hide the Analysts rows - Add/Substract to the second number when adding/removing analysts
'if the name is not under selected supervisor, and its not selected name, hide the row
If Not isSupervisor(CStr(v), CStr(Cells(i, 1).Value)) And Not Cells(i, 1) = v Then
Rows(i).Hidden = True
End If
Next i
Cells(2, 1).Select
ActiveWindow.FreezePanes = True
Cells(2, 1).Select
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
'returns true if sEmplName is under sSupName supervisor
Private Function isSupervisor(sSupName As String, sEmplName As String) As Boolean
Dim wksDataSheet As Worksheet 'worksheet where supervisor and employes data saved
Dim rngSupCell As Range 'range with supervisor names/headers
Dim lSupColumn As Long 'column of selected supervisor
Dim lSupLastRow As Long 'last row with employes names of column of supervisor
Dim lCurrentRow As Long 'counter for current row by iteration
Set wksDataSheet = ThisWorkbook.Sheets(2) 'or some named sheet for example ThisWorkbook.Sheets("DataSheet")
With wksDataSheet
'for each cell in supervisor names range
For Each rngSupCell In .Range("rngSupervisors") 'could be .Range("A3:A6") for example
'if supervisor name found in the range
If StrComp(rngSupCell.Value, sSupName, vbTextCompare) = 0 Then
'get column
lSupColumn = rngSupCell.Column
'get last row
lSupLastRow = .Cells(.Rows.Count, "A").End(xlUp).row
'for each employee name, starts from next row
For lCurrentRow = rngSupCell.row + 1 To lSupLastRow
'if equals, return true, exit.
If StrComp(.Cells(lCurrentRow, lSupColumn).Value, sEmplName, vbTextCompare) = 0 Then
isSupervisor = True
Exit Function
End If
Next lCurrentRow
End If
Next rngSupCell
End With
'nothing found, return false
isSupervisor = False
End Function
Related
I have an excel document which has a lot of info and statistics and i am trying to figure out how to solve the following issue:
If a cell on column E, in the interval E5:E70, contains the "N/A" text (without quotes), i want to automatically input the "N/A" text on several specific cells in the same row
Added an image for reference.
Any help would be much appreciated, Thanks !
Latest edit:
I have attached a copy of the excel, maybe it will be a lot more helpful, for me it just won't work...it's so frustrating...
excel file
Paste the code below into the code sheet of the worksheet on which you want to have the action.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim C As Long
If Not Application.Intersect(Target, Range("E5:E70")) Is Nothing Then
SetApplication False
With Target
If StrComp(Trim(.Value), "N/A", vbTextCompare) = 0 Then
' recognises "n/a" as "N/A" and corrects entry
For C = Columns("E").Column To Columns("AL").Column
Cells(.Row, C).Value = "N/A"
Next C
End If
End With
SetApplication True
End If
End Sub
Private Sub SetApplication(ByVal AppMode As Boolean)
With Application
.EnableEvents = AppMode
.ScreenUpdating = AppMode
End With
End Sub
I assumed (based on the picture) that you want paste N/A's in this way: F - paste, G - don't paste, H - paste - and repeat this three further: paste, don't paste, paste, paste, don't paste, paste, etc.
So this code works accordingly to this rule. You just need to specify very last column instead of Column.Count - 2 - this bit says just that program should fill until the last column in a sheet.
Sub FillNAs()
Application.EnableEvents = False
Application.ScreenUpdating = False
Dim i, j As Long
For i = 5 To 70
If UCase(Cells(i, 5).Value) = "N/A" Then
j = 6
Do While j < Columns.Count - 2
Cells(i, j).Value = "N/A"
Cells(i, j + 2).Value = "N/A"
j = j + 3
Loop
End If
Next i
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
Try
Sub Demo()
Dim ws As Worksheet
Dim cel As Range
Set ws = ThisWorkbook.Sheets("Sheet1") 'change Sheet1 to your data sheet
For Each cel In ws.Range("E5:E70")
If CVErr(cel.Value) = CVErr(xlErrNA) Then
ws.Range("F" & cel.Row & ":I" & cel.Row) = CVErr(xlErrNA) 'mention desired range instead of (F:I)
End If
Next cel
End Sub
I have a workbook that contains about 150 worksheets. The first worksheet is a table/list of information that is about 150 rows long and 16 columns wide and is named "log". In col. 'j' of the "log" worksheet are cell values that are either contain new or closed. In col. 'm' are hyperlinks to other worksheets in the workbook. The goal of the code is to go through each row of the "log" sheet and identify if it contains closed in col. j. If yes, follow the corresponding hyperlink, in the same row, and delete the sheet. The issue is that I have situations where the hyperlink is shared by multiple rows. For instance, row 5 hyperlinks to sheet 2 and row 15 hyperlinks to sheet 2. My issue is that when the code comes to row 15 and goes to follow the hyperlink there is nothing to follow, thus, the "log" is the active sheet and the "log" ends up getting deleted and my code then bombs out. Is there a way to write code that says that if the active sheet is the log sheet to not delete it or skip the code that is currently in place to delete a sheet and instead continue the loop?
Here is the code...
Sub Deletelinks()
'Macro will check to see if status is closed and if so it will
'delete the supporting worksheet by following the hyperlink in
'same row
Dim count As Integer
Dim lrow As Long
Dim Rng As Range
Set Rng = Range("J2")
lrow = Worksheets("log").Range("J" & Rows.count).End(xlUp).row - 1
Application.ScreenUpdating = False
Application.DisplayAlerts = False
For count = 1 To lrow
Sheets("log").Activate
Rng.Offset(count - 1, 0).Activate
Select Case ActiveCell.Value = "Closed"
Case True
If ActiveCell.Offset(0, 3).Value = "Click" Then
ActiveCell.Offset(0, 3).Hyperlinks(1).Follow
If ActiveSheet.Name <> "log" Then
With ActiveSheet
ActiveWindow.SelectedSheets.delete
End With
End If
End If
Case False
End Select
Next count
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
A simpler approach would be to iterate over the hyperlinks in the column and use the hyperlink's properties to reference the adjacent cell to see if it equals Closed; then if it does, delete the hyperlinks target worksheet and clear the hyperlink.
Sub DeleteLinks()
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Dim link As Hyperlink
For Each link In Worksheets("log").Columns("M").Hyperlinks
If link.Range.Offset(0, -3) = "Closed" Then
On Error Resume Next
Range(link.SubAddress).Parent.Delete
On Error GoTo 0
link.Range.ClearContents
End If
Next
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
I have a list called "District List" on one tab and a Template that is driven by putting the name of a district into Cell C3. Each District has a wildly varying number of branches (between 1 & 500+ branches depending on the District) so the report template has a lot of blank space in some cases. I came up with this to loop through the District List, copy the Template tab, rename it the District Name, insert the name of the district into Cell C3, and then I have another loop to hide the blank rows.
It works, but it takes forever, like 5 minutes per tab, then after about four tabs, I get an object error at the first like of Sub CreateTabsFromList.
Is there a problem with the code, or is this just a really inefficient way to do this? If so can anyone help with a better method?
Sub HideRows()
Dim r As Range, c As Range
Set r = Range("a1:a1000") 'Sets range well beyond the last possible row with data
Application.ScreenUpdating = False
For Each c In r
If Len(c.Text) = 0 Then
c.EntireRow.Hidden = True 'Hide the row if the cell in A is blank
Else
c.EntireRow.Hidden = False
End If
Next c
Application.ScreenUpdating = True
End Sub
Sub CreateSheetsFromAList()
Dim MyCell As Range, MyRange As Range
Set MyRange = Sheets("District List").Range("A1")
Set MyRange = Range(MyRange, MyRange.End(xlDown))
For Each MyCell In MyRange
Sheets("Template").Copy After:=Sheets(Sheets.Count) 'creates a new worksheet
Range("C3").Value = MyCell.Value 'Pastes value in C3
Sheets(Sheets.Count).Name = MyCell.Value 'renames worksheet
HideRows 'Hides rows where cell in column A is ""
Next MyCell
End Sub
Deleting/Hiding rows, 1 by 1 is the slowest method. Always club them in one range and delete/hide them in one go, also looping through cells is slower than looping array.
Sub HideRows()
Dim lCtr As Long
Dim rngDel As Range
Dim r As Range
Dim arr
Set r = Range("a1:a1000") 'Sets range well beyond the last possible row with data
Application.ScreenUpdating = False
arr = r
For lCtr = LBound(arr) To UBound(arr)
If arr(lCtr, 1) = "" Then
If rngDel Is Nothing Then
Set rngDel = Cells(lCtr, 1) 'harcoded 1 as you are using column A
Else
Set rngDel = Union(rngDel, Cells(lCtr, 1))
End If
End If
Next
If Not rngDel Is Nothing Then
rngDel.EntireRow.Hidden=True
End If
Application.ScreenUpdating = True
End Sub
takes fraction of a second for 1000 rows.
I'm fairly new to VBA. I'm currently trying to find a faster way to copy and paste information by using Macros. I'm not sure how to code this.
I have two columns I want to use with a For Each loop.
I wanted to loop through each row of these two columns and use an If function. If the first row has a value in Column B (Column B cell <> "" Or Column B cell <> 0) then, select that row (i.e. Range("A1:B1")).
After the loop, I will copy whatever is selected and paste it to a specific row.
However, I want to keep adding to that selection as it loops through each row and only if it satisfies the If condition, so I'm able to copy it all once at the end. How do I go about combining this?
A B
1 Abc 1
2 Def 2
3 Geh 3
This is how you can expand current selection:
Sub macro1()
Set selectedCells = Cells(1, 2)
Set selectedCells = Application.Union(selectedCells, Cells(2, 3))
selectedCells.Select
End Sub
I'm sure you can manage the rest of your code by yourself, it's really easy. You already mentioned everything you need: For Each cell In Range("B1:B5") and If statement
Please try the below code
Sub test()
Application.ScreenUpdating = False
Dim rng As Range, one As Variant
Dim i As Integer
'Change the sheet and range name as yours
'Finding lastrow of destination column
i = Sheets("Sheet2").Cells(Rows.Count, "A").End(xlUp).Row + 1
' getting input from user
Set rng = Application.InputBox("Please select a range of cells!", "Please select a range", Selection.Address, , , , , 8)
For Each one In rng
If one.Value <> "" Or one.Value <> 0 Then
Range(one.Offset(0, -1), one).Copy
'Change the sheet and range name as yours
Sheets("Sheet2").Activate
Range("A" & i).Select
ActiveSheet.Paste
i = i + 1
End If
Next one
Application.ScreenUpdating = True
End Sub
The above macro will prompt you for the input range to be validate and copy to sheet2 in column A.
The below code will validate and copy paste the current selected range to sheet2 column A
Sub test()
Application.ScreenUpdating = False
Dim rng As Range, one As Variant
Dim i As Integer
'Chnage the sheet and range name as yours
'Finding lastrow of destination column
i = Sheets("Sheet2").Cells(Rows.Count, "A").End(xlUp).Row + 1
' getting input from user
Set rng = Selection
For Each one In rng
If one.Value <> "" Or one.Value <> 0 Then
Range(one.Offset(0, -1), one).Copy
'Chnage the sheet and range name as yours
Sheets("Sheet2").Activate
Range("A" & i).Select
ActiveSheet.Paste
i = i + 1
End If
Next one
Application.ScreenUpdating = True
End Sub
I think you're probably going about this the wrong way. Do you already know to where you would like to copy all the data in the end? It sounds like it, as you refer to copying it "to a specific row". If so, you'd be better off using your macro to copy the data from Columns A:B on the fly.
So, for example:
Sub CopyData()
Const SOURCE_COLUMN1 As Long = 1 ' A
Const SOURCE_COLUMN2 As Long = 2 ' B
Const TARGET_COLUMN1 As Long = 5 ' E
Const TARGET_COLUMN2 As Long = 6 ' F
Dim lngSourceRow As Long
Dim lngTargetRow As Long
With ThisWorkbook.Sheets("Sheet1")
lngSourceRow = 1
lngTargetRow = 0 ' Change this to the row above the one you want to copy to;
Do While .Cells(lngSourceRow, SOURCE_COLUMN1) <> ""
If .Cells(lngSourceRow, SOURCE_COLUMN2) <> "" Then
lngTargetRow = lngTargetRow + 1
.Cells(lngTargetRow, TARGET_COLUMN1) = .Cells(lngSourceRow, SOURCE_COLUMN1)
.Cells(lngTargetRow, TARGET_COLUMN2) = .Cells(lngSourceRow, SOURCE_COLUMN2)
End If
lngSourceRow = lngSourceRow + 1
Loop
End With
End Sub
I have some code that currently looks through the sheet for any cell that I have filled with a light grey, and then adds the value within that cell to a Names list. The goal being that somewhere else in the workbook I can reference this list as a drop down.
Here's my current code:
Sub Add_Food_To_List()
i = 1
Application.ScreenUpdating = False
Range("a1:a60").Select
x = "{"
y = ""
first = True
For Each Cell In Selection
If ActiveCell.Interior.ColorIndex = "2" Then
i = i + 1
If first = False Then
x = x & ", " & ActiveCell.Value
y = y & ", " & ActiveCell.Address
End If
If first Then
x = x & ActiveCell.Value
y = y & ActiveCell.Address
first = False
End If
ActiveWorkbook.Names("Foods").RefersTo = y
ActiveWorkbook.Names("Foods").Value = x
End If
ActiveCell.Offset(1, 0).Select
Next Cell
Range("a1").Select
Application.ScreenUpdating = True
End Sub
For some reason these two lines within the For Each Cell In Selection:
ActiveWorkbook.Names("Foods").RefersTo = y
ActiveWorkbook.Names("Foods").Value = x
overwrite each other. Whichever goes last ends up as the value that both RefersTo AND Value are set to in the name.
Bonus: This is my first VBA script. How can I get this script to run on the entire workbook, not just the active sheet? Also, how do I make it run automatically on save, or on workbook update?
Perhaps this will serve you better:
Create a Worksheet in your Workbook with the name Reference.
Type Foods in cell A1 and put at least one random food in cell A2.
Create a defined name of Foods with the following formula: =offset(A2,0,0,counta(A:A)-1,1) This is a Dynamic Named Ranges that will expand or contract as rows are added or deleted (just be sure there are no blank rows in between data).
Place the below code in the ThisWorkbook module in the VBE. The below code will run right before the Workbook saves. It will loop through each sheet and add the values of any cells highlighted grey in Range(A1:A60) to the rowset in column A of the Reference Worksheet directly underneath the existing rowset.
Code for Module:
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Application.ScreenUpdating = False
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
If ws.Name <> "Reference" Then
With ws
Dim rCell As Range
For Each rCell In .Range("a1:a60")
If rCell.Interior.ColorIndex = "2" Then
Dim wsRef As Worksheet
Set wsRef = Sheets("Reference")
If wsRef.Range("Foods").Find(rCell.Value, lookat:=xlWhole) Is Nothing Then
wsRef.Range("A" & wsRef.Rows.Count).End(xlUp).Offset(1).Value = rCell.Value2
End If
End If
Next
End With
End If
Application.ScreenUpdating = True
End Sub