I've searched and searched the internet and all of the forums and I've been piecing together code and still can't figure this out. I've tried For loops and For Each loops and still can't get it right. In my sheet, I have all of my dates in Column D. I want to hide rows by month. I want to be able to click a macro button and only show dates in January, or February, or etc.
This is what I currently have:
Sub January()
'
'
'
Dim cell As Range
For Each cell In Range("Date")
If cell.Value = "" Then
cell.EntireRow.Hidden = False
End If
If cell.Value < "1/1/2018" Or cell.Value > "1/31/2018" Then
cell.EntireRow.Hidden = True
End If
Next cell
End Sub
When I run this, it just hides anything that isn't an empty cell. I've cycled between defining cell as a Range and as a Variant and it's the same either way.
ETA:
It is working now and it took help from everybody. I really appreciate it! Here's what I ended with..
Sub January()
'
'
'
Dim cell As Range
For Each cell In Range("Date")
If cell.Value = "" Then
cell.EntireRow.Hidden = False
ElseIf cell.Value < CDate("1/1") Or cell.Value > CDate("1/31") Then
cell.EntireRow.Hidden = True
End If
Next cell
End Sub
I removed the years from the code so that I don't have to change any coding for future years.
Your current setup would qualify all dates as either < or > the respective date comparison.
If you are trying to hide rows for January in this code, then you need to use AND instead of OR
And be sure you use >= & <= to include those first and last dates.
If cell >= "1/1/2018" AND cell <= "1/31/2018" Then
If you are trying to hide rows not January then your < and > are transposed:
If cell < "1/1/2018" OR cell > "1/31/2018" Then
Alternative approach: If you've got Excel 2013 or later, simply add a Table Slicer and filter on a MONTH column generated with =DATE(YEAR([#Date]),MONTH([#Date]),1) as shown below:
Or otherwise use a PivotTable and a Slicer:
To see how easy it is to set up a PivotTable, see VBA to copy data if multiple criteria are met
Ultimately, I believe this is the code you're looking for:
Sub January()
Dim cell As Range
Application.ScreenUpdating = False
For Each cell In Range("Date")
'If date falls on or after January 1, AND on or before January 31, don't hide the row
If cell.Value >= CDate("1/1/2018") And cell.Value <= CDate("1/31/2018") Then
cell.EntireRow.Hidden = False
Else
'If the cell doesn't contain anything or isn't in January, hide the row
cell.EntireRow.Hidden = True
End If
Next cell
Application.ScreenUpdating = True
End Sub
You need to use And logic, not Or logic. Or logic always returns TRUE unless both expressions are false or there is a null involved. Because of this, the code stopped looking at your logical statement once it evaluated to true since every date you had - I'm assuming - fell after January 1, 2018. This in turn caused the rows to hide unexpectedly.
Additionally, I would convert the strings you have into dates using CDate. It helps Excel understand what is going on a bit better and makes your code easier to understand to outsiders. Another good practice to work on is adding comments to code. I think we've all learned the hard way by leaving comments out of code at some point or another.
One last thing: if you're planning to have buttons for each month, consider doing one procedure for all of them and having variables populate the date ranges, potentially using input boxes to get the values from the user. It'll save you a lot of headaches if you ever decide to change things up in the future.
Untested, written on mobile. I am just providing an alternative approach which tries to use MONTH and YEAR. Some may find this approach easier to understand.
Option Explicit
Sub January()
Dim cell As Range
For Each cell In Range("Date")
If cell.Value = "" Then
cell.EntireRow.Hidden = False
Else
cell.EntireRow.Hidden = (Month(cell.Value) = 1) and (year(cell.Value) = 2018)
End if
Next cell
End sub
I will actually go with Slicers and Table.
But if you call VBA your neat solution then I'd say abandon the loop.
Have nothing against it but if Excel already have the functionality, then use it.
It is like a discount or a promotion that we need to take advantage of.
So instead of loop, why not just filter?
Dim lr As Long, r As Range
With Sheet1 '/* sheet where data reside */
.AutoFilterMode = False '/* reset any filtering already applied */
lr = .Range("D" & .Rows.Count).End(xlUp).Row '/* get the target cells */
Set r = .Range("D1:D" & lr) '/* explicitly set target object */
'/* filter without showing the dropdown, see the last argument set to false */
r.AutoFilter 1, ">=2/1/2018", xlAnd, "<=2/28/2018", False
End With
Above is for February of this year, you can tweak it to be dynamic.
You can create separate sub procedure for each month of you can just have a generic one.
Related
I'm fairly new to vba and have a rather simple problem. Can someone please help:
Instead of selecting the specific cell, I want my vba macro to go to the bottom of the column of interest, skip, and two cells below it do the following:
Range("W72").Select
Selection.NumberFormat = "General"
ActiveCell.FormulaR1C1 = "Null_value"
Range("X72").Select
Selection.NumberFormat = "General"
ActiveCell.FormulaR1C1 = "=R[-2]C[1]-SUM(R[-2]C[-8]:R[-2]C[-6])"
As you can see the code above refers to the specific cells W72 and X72. Currently, the last entry in these columns are in W70 and X70 but next month my dataset will get bigger so W72 and X72 aren't the right locations to do the actions above. How do I correct for this such that my vba code is automatically going to the bottom of W(n):X(n), skips one row and in W(n+2), X(n+2) performs the code above.
Also, my formula above (ActiveCell.FormulaR1C1) also is referring to specific cells, in my case Row 70 several columns to the left, but as you probably tell, this too has the same issue since the row referencing changes each month. I need to get my vba to have the formula pick up the last row of those columns, the columns are P,Q,R.
Thanks for any help you can provide.
Update: Part of my same working project, I would greatly appreciate if anyone can help with this too. Thank you:
Hi All,
I currently have an input box for a variable that changes everymonth:
r_mo = Application.InputBox(prompt:="Enter the reporting month as YYYYMM (Eg:201604). Errors in this entry will result in errors in the results.")
This prompts an input box which one has to manually enter into... However, I want to automate this process and eliminate the need for an input box. Isn't there a now function in vba that will automatically generate today's date.
From a now, or system function all I want to do is extract the year in four digits and the month in two digits.
So for example, if we're in decemeber 2016
Sub asasdas ()
"Now function"
r_mo = YYYYMM ' automatically updated from "now function"
End Sub
I appreciate any help you can give me and thank you so much all.
You can get the last populated row of a given column (W in my example) in VBA with the following code:
Dim ws As Worksheet : Set ws = ThisWorkbook.Worksheets("MySheetNameHere")
lastRow = ws.Cells(ws.Rows.Count, "W").End(xlUp).Row
Naturally, if you then add 2 to lastRow you have the cell you are looking for.
I'd do it like
Sub asdf()
Range("w1048576").End(xlUp).Offset(2, 0).Select 'gets the last row
With Selection
.NumberFormat = "General"
.FormulaR1C1 = "Null_value"
End With
ActiveCell.Offset(, 1).Select
With Selection
.NumberFormat = "General"
.FormulaR1C1 = "=R[-2]C[1]-SUM(R[-2]C[-8]:R[-2]C[-6])"
End With
End Sub
If you want a more detailed answer you're going to have to make a new question but for your second question try this.
Sub Now()
Dim myDate As String
myDate = Date
myDate = Format(myDate, "yyyymm")
Debug.Print myDate '201606 output for June 10th 2016
End Sub
So I'm trying to code for an Excel VBA billing archive/tracker form for my work, and I am very new to excel vba. Any help would be much appreciated. Essentially, I want my userform to input billing date on the bottom table, then take the sum of the parts selected (A1-A19) and input the sum to the nearest, empty cell to the left of the input billing date. If it's the first billing cycle, then I just want to call the dollar amount in the Grand Total Engagement and put that value to the right of the input billing date. Attached are my snippits of the Excel sheet, and userform. Thanks so much community!
Excel Sheet
BillingArchiveUserform
'Creates "LastRow" variable
If Range("D47").Value = "" Then
LastRow = 47
Else
Main.Range("D46").Select
LastRow = Selection.End(xlDown).Row
End If
i = 47
Do While i <= LastRow
If BillingDate.Value = Range("D" & i).Value Then
If CmBBill.Value = "Yes" Then
'Range("D" & i).Offset( 'resume from here and figure it out, but I want to choose the nearest empty cell to the right of date
'Input project specific information (i.e. the sum of the parts that need to be billed)
Else
i = i + 1
End If
Loop
Cells(LastRow + 1, 4).Value = BillingDate.Value 'Inputs Billing Date
'2. Choose column of ProjectToBill based on value in textbox
Main.Range("E5").Select
LastCol = Selection.End(xlToRight).Column
1) You do not need a loop for this
2) You do not have any logic that checks if the userform checkboxes are set to true or false and then what to do if they are true/false.
3) Offset Property will be need to put information in lowe table in next available row. Offset can be sued to adjust left or right based obn your criteria, which will most likely be IF's
4) if first billing cycle you can direct the input to the first location in the lower area.
5) seeing in how your just dumping these into a spreadsheet, having to declare variables from textboxes in userforms is wont be needed. I do mention it however, because if you ever need to manipulate that information in VBA, it may need to be done so. And it looks like that simple math needs ot be done, so if you run into issues that may be the problem.
This should get you started in the right place
Private Sub commandbutton_click()
If CheckBox1.Value = True Then Sheet1.Range("E4").Value = "Wherever you hid the predetermined value"
If Sheet1.Range("d47").Value = vbNullString Then
Sheet1.Range("d47").Value = BillingDateTextBox.Value
Else
Sheet1.Cells(Sheet1.Rows.Count, 1).End(xlUp).Offset(1, 0).Value = BillingDateTextBox.Value
Sheet1.Cells(Sheet1.Rows.Count, 1).End(xlUp).Offset(0, 1).Value = "Wherever you hid the predetermined value"
End If
End Sub
I need to have the user select a range of cells with their mouse and then run a macro to add +1 to that selected range. The range will often be different every time, so it cannot be defined.
Here's what I have so far, it just works on a single active cell, I cannot get it to work on a range selection, because I do not know what to define or function to utilize.
My code Follows:
Sub Add()
ActiveCell.Value = ActiveCell.Value + 1
End Sub
The below code uses the built-in Paste Special Add feature to add 1 to every cell in the selected range.
Sub AddOneToSelection()
Dim rBlank As Range
If TypeName(Selection) = "Range" Then
'Put a '1' in an unused cell and copy it
Set rBlank = ActiveSheet.Cells.SpecialCells(xlLastCell).Offset(1, 0)
rBlank.Value = 1
rBlank.Copy
'Paste Special Add over the selection
Selection.PasteSpecial Paste:=xlPasteValues, _
Operation:=xlAdd
'Get rid of the copied cell
rBlank.ClearContents
End If
End Sub
The benefit of this over looping is that Paste Special Add treats formulas and values differently and you don't have to code that part yourself.
The downside is you increase your UsedRange by one row, if that matters to you.
This should work for you:
Dim r, c As Range
Set r = Selection
For Each c In r
c.Value = "+1"
Next
This assumes that your cell formats will display "+1" instead of "1". You can set the format of your cells to "Text".
I have large table with values in range H2:PIG2202. I need to compare the first rows H2:PIG2 values with all other rows values. And if there is a match in the result table it pastes just those values which matched.
Now I'm using this formula in the result table to display needed values:
=IF(sheet!H$2=sheet!H3;IF(AND(sheet!H3;ISBLANK(sheet!H3))=FALSE;sheet!H3;"");"")
The VBA code is:
Sub find()
Application.ScreenUpdating = False
Range("H2:PIG2202").FormulaR1C1 = _
"=IF('sheet'!R2C='sheet'!R[1]C,IF(AND('sheet'!R[1]C,ISBLANK('sheet'!R[1]C))=FALSE,'sheet'!R[1]C,""""),"""")"
Application.ScreenUpdating = True
End Sub
The problem is that Excel shows an error when I run this macros that there are not enough system resources.
Also I would like that in the result table would be just values, not formulas.
Is that possible to do? I have no idea how to achieve this :(
Thank you in advance!
Quick solution for your which is not sophisticated and I'm rather not proud of it (but it was quickest to prepare). Keep in mind that you are going to run it 24m times which could take several minutes, maybe an hour. Some comments inside the code.
Sub Solution()
Application.ScreenUpdating = False
'-----previous one
'Range("H2:PIG2202").FormulaR1C1 = _
"=IF('sheet'!R2C='sheet'!R[1]C,IF(AND('sheet'!R[1]C,ISBLANK('sheet'!R[1]C))=FALSE,'sheet'!R[1]C,""""),"""")"
'----- new one- inserting values- first idea, simple code
Dim Cell As Range
'run for one row first to check if it is ok! and check time needed per row
'next change range to one you expect
'next- take a cup of coffee and relax...
For Each Cell In Range("h2:PIG2")
Cell.FormulaR1C1 = _
"=IF('sheet'!R2C='sheet'!R[1]C,IF(AND('sheet'!R[1]C,ISBLANK('sheet'!R[1]C))=FALSE,'sheet'!R[1]C,""""),"""")"
Cell.Value = Cell.Value
'to trace progress in Excel status bar
Application.StatusBar = Cell.Address
Next Cell
Application.ScreenUpdating = True
End Sub
I am not sure if what I want to achieve is possible. So here it is:
I got workbook with 2 sheets:
First sheet cointains raw data with employees and trainings they did or did not take (they could not come to the the training). Sheets cointains few columns like: name, special ID (different for every employee), 2 blank columns, presence (yes/no), and few more...
Second sheet will create report based on range and presence criteria.
Technically it's something like that:
Report sheet has list of employees, they will be filtered using autofilter. Those filtered employees will be checked if they were on 14 categories of trainings. Categories differs with range (ranges are known; with time ranges will be added or adjusted to newly added trainings).
My problem:
Is it possible to create vba code that will check if employee was on certain trainings (countif in certain range with condition: not present = do not count) and paste the value to certain cells? If it is could you give some advice on how to do that? I am not asking for ready code.
Trying to make it work but I stuck at this. Error at line "if cells.find...".
Sub Check()
MyRange = Range("A1", Selection.End(xlDown))
For Each MyCell In MyRange
With Range("pp2dni2007")
If Cells.Find(MyCell.Value) Is Nothing Then
Else
If ActiveCell.Value = ActiveCell.Offset(0, 3).Value Then
MyCell.Offset(0, 6).Value = 1
Else
MyCell.Offset(0, 6).Value = 0
End If
End If
End With
Next
End Sub
2nd edit, earlier code did infinite loop. Now I think if-statements reference to wrong range but not sure how to deal with it.
Sub Check()
Dim MyRange As Range, MyCell As Variant
Range("A1").Select
Set MyRange = Range(Selection, Selection.End(xlDown)).Rows.SpecialCells(xlCellTypeVisible)
For Each MyCell In MyRange.Cells
With Range("pp2dni2007")
If .Cells.Find(MyCell.Value) Is Nothing Then
Else
If .Cells.Find(MyCell.Value).Value = .Cells.Find(MyCell.Value).Offset(0, 3).Value Then
MyCell.Offset(0, 6).Value = 1
Else
MyCell.Offset(0, 6).Value = 0
End If
End If
End With
Next
End Sub
Sample workbook: https://dl.dropboxusercontent.com/u/7421442/sample%20workbook%20(1).xls
Declare all your variables, e.g.,
Dim MyRange as Range, MyCell as Range.
Without declaration, MyCell is variant data type, and that is why you're getting an Object Required error. Then do:
For Each MyCell In MyRange.Cells
Inside the With block, you may want to use (note the . in front of Cells):
If .Cells.Find(MyCell.Value) Is Nothing Then
Further, you will probably need to revise what you're doing with ActiveCell, since this is never changing which cell is active, I'm not sure it would give you the expected results. It is always preferable to avoid relying on Active and Select methods (except for to use them as user-input). INstead, work with the range objects directly.