Selecting multiple slicer items based on a cell value - vba

I need a VBA code that is able to select multiple slicer items based on a cell value. This is for making weekly reports so the target cell would be C17 which is in format "YYYYWW" (i.e 3rd week of 2018 would be 201803) and is entered by user. This is what I call endDate. Excel itself then calculates the starting week, which is by default 30 weeks before endDate. As the value of cell C17 changes, the macro should automatically trigger.
So, for an example, "201803" is entered into C17 and excel then calculates the value "201725" and inserts it into G17. Entering a new value into C17 triggers the macro, which start off by reading the value of cell C17 (endDate) and G17 (startDate). Then the macro will deselect all slicer items (there is only one slicer used) and select those which are either equal to endDate or startDate, or in between the two values. Because there was 52 weeks in 2017, "201753" should be overwritten into "201801". I know this is not the ideal solution as it needs to be updated every year but it's the best I came up with.
I have learned little programming in C but this is one of my first codes in VBA so any pointers would be appreciated. This is what I got so far:
Private Sub Worksheet_Change(ByVal Target As Range)
' 18.01.2018 Oscar E.
Dim startDate As Long
Dim endDate As Long
Dim n As Long
Dim i As Long
If Target.Address = "$C$3" Then
endDate = Range("C17").Value
startDate = Range("G17").Value
With ActiveWorkbook.SlicerCaches("Slicer_YYYYWW")
.ClearManualFilter
For n = startDate To endDate
If n = 201753 Then
n = 201801
End If
.SlicerItems(n).Selected = True
Next
End With
End If
End Sub
Right now, I am getting a
Run time error '1004': Application-defined or object defined error
on .SlicerItems(n).Selected = True near the end. So the problem is, I guess, overwriting slicer items selection.

SlicerItems must to be Stirng. But when .ClearManualFilter all item selected. All items must not be selected.
Dim startDate As Long
Dim endDate As Long
Dim n As Long
Dim i As Long
Dim strN As String
Dim sl As SlicerItem
If Target.Address = "$C$3" Then
endDate = Range("C17").Value
startDate = Range("G17").Value
With ActiveWorkbook.SlicerCaches("Slicer_YYYYWW")
.ClearManualFilter
For n = startDate To endDate
If n = 201753 Then
strN = CStr(201801)
End If
Next
For Each sl In .SlicerItems
If sl.Name = strN Then
sl.Selected = True
Else
sl.Selected = False
End If
Next
End With
End If

Related

String Value is not passing correctly

I have a word table. I wrote a macro to get values from the table. When it runs I get a runtime error 13. When I debug and watch the value of parsing string it looks like this "2019-04-03 There is only one quote in the string. I think that is the case I couldn't convert that string into a date format. Can you help me to fix this?
The code
Sub Macro2()
Dim NumRows As Integer
Dim startDate As String
Dim days As String
Dim endDate As String
If Not Selection.Information(wdWithInTable) Then
Exit Sub
End If
NumRows = Selection.Tables(1).Rows.Count
'Loop to select each row in the current table
For J = 2 To NumRows
'Loop to select each cell in the current row
startDate = Selection.Tables(1).Rows(J).Cells(5).Range.Text
days = Selection.Tables(1).Rows(J).Cells(6).Range.Text
FormatDate = CDate(ends)
endDate = DateAdd("d", days, FormatDate)
Selection.Tables(1).Rows(J).Cells(7).Range.Text = endDate
Next J
End Sub
The table
Here's the minimal change I found that works for me when tested in Word 2013.
General points:
I added Option Explicit so that the computer would help me find errors. In this case, the variables J and FormatDate were used but not Dimed, and ends was used but never initialized (I changed it to startDate).
The Range.Text in a table cell includes whitespace and the end-of-table marker (ยค). That is why CDate was giving an error.
For the dates, I used Left() to take only the left ten characters, since you seem to always be using yyyy-mm-dd-format dates.
For the counts of days, since those can be of any length, I used Range.Words(1).Text to keep only the first Word (as MS Word defines it), which is the number.
I also added the CLng() call in the parameter to DateAdd, since DateAdd wants a number* rather than a string.
For production use, I would also recommend using Selection only in one place, and doing Dim workTable as Table: Set workTable = Selection.Tables(1). That will simplify your code.
Code
<=== marks changed lines
Option Explicit ' <==
Sub Macro2()
Dim NumRows As Integer
Dim startDate As String
Dim days As String
Dim endDate As String
If Not Selection.Information(wdWithInTable) Then
Exit Sub
End If
NumRows = Selection.Tables(1).Rows.Count
'Loop to select each row in the current table
Dim J As Long ' <==
For J = 2 To NumRows
'Loop to select each cell in the current row
startDate = Selection.Tables(1).Rows(J).Cells(5).Range.Text
startDate = Left(startDate, 10) ' <== Remove the space and table mark
days = Selection.Tables(1).Rows(J).Cells(6).Range.Words(1).Text ' <===
Dim FormatDate As Date ' <==
FormatDate = CDate(startDate) ' <== not `ends`
endDate = DateAdd("d", CLng(days), FormatDate) ' <=== clng
Selection.Tables(1).Rows(J).Cells(7).Range.Text = endDate
Next J
End Sub
* DateAdd actually takes a Double, but VBA can promote Long to Double. I chose CLng since it looks like you are only using integer day spans. If not, use CDbl instead.
Try:
Sub Demo()
Dim r As Long
With Selection
If Not .Information(wdWithInTable) Then Exit Sub
With .Tables(1)
For r = 2 To .Rows.Count
.Cell(r, 7).Range.Text = _
Format(DateAdd("d", Split(.Cell(r, 6).Range.Text, vbCr)(0), CDate(Split(.Cell(r, 5).Range.Text, vbCr)(0))), "YYYY-MM-DD")
Next r
End With
End With
End Sub

Excel VBA with matches and index

I've written some VBA code with two matches and index formula. I need to pick the unique value from a sheet and compare it with the other sheet. However it is not working. I get some errors. (unable to get the match property of the worksheetfunction class vba - this is the error)
Here is my code :
Sub Post_Attendance()
Sheets("DB").Activate
'On Error Resume Next
Dim myvalue As String
Dim mydate As String
Dim mypost As String
(the date value entered in a cell)
Dim Dt As String
Dt = Range("C7").Value
(the unique id entered in a cell)
Dim empid As String
empid = Range("C8").Value
(activating another worksheet , from a cell value)
Dim strWsName As String
strWsName = Sheets("DB").Range("A7")
Sheets(Left(strWsName, 3)).Select
(match function to find the row and columns number for indexing)
mydate = WorksheetFunction.Match(Dt, Range("B1:Q1"), 0)
myvalue = WorksheetFunction.Match(empid, Range("A5:A500"), 0)
mypost = WorksheetFunction.Index(Range("B2:Q6"), myvalue, mydate)
End Sub
First off, WorksheetFunction.Match never returns a string; it either returns a number (a long integer) or an error. It is not the value from the match, it is the row or column number where the match was found.
Next, you cannot catch an #N/A error from no match with WorksheetFunction.Match but you can catch it with Application.Match into a variant.
Real dates are numbers, not strings. The raw underlying value is another long integer; e.g. a positive whole number with no decimal portion. If you had time or a datetime then you would have a decimal portion.
Resolve and reference your parent worksheet properly; do not rely upon Select or Activate.
The number returned from MATCH is the position within the range of cells searched. You are looking for a row number from row 5 to row 500 then using that to find a row within row 2 to 6; any match above row 9 (match returning 6 or above) in the original is going to be out-of-range.
If the empid values are numbers then deal with numbers; you cannot find a match to a true number from text-that-looks-like-a-number; e.g. 99 <> "99". I'm going to assume that empid should be alphanumeric and not a true number but given the errors with the previous variable assignments, it is up to you to determine the correct assignment.
Here is my best guess at an error controlled sub procedure (given that you have shown no sample data).
Option Explicit
Sub Post_Attendance()
'On Error Resume Next
Dim myvalueRow As Variant, mydateCol As Variant, dt As Long, empid As String, mypost As Variant
dt = Worksheets("DB").Range("C7").Value2
empid = Worksheets("DB").Range("C8").Value2
With Worksheets(Left(Worksheets("DB").Range("A7").Value2, 3))
'locate the column for the date
mydateCol = Application.Match(dt, .Range("B1:Q1"), 0)
If IsError(mydateCol) Then _
mydateCol = Application.Match(CStr(Worksheets("DB").Range("C7").Value2), .Range("B1:Q1"), 0)
If IsError(mydateCol) Then
Debug.Print "dt not found in row 1"
Exit Sub
End If
'locate the row for the value
myvalueRow = Application.Match(empid, .Columns("A"), 0)
If IsError(myvalueRow) Then
Debug.Print "empid not found in column A"
Exit Sub
End If
mypost = Application.Index(.Range("B:Q"), myvalueRow, mydateCol)
End With
End Sub

function not appropriately defined within userform subroutine

I apologize if this is a dumb question but I've been unable to find an answer to my current issue. I am attempting to create a userform which searches spreadsheet data based on input in the combobox for username, a start date, and an end date.
There is one user column and all other columns correspond to dates. The data is a daily workload productivity number (e.g., 87 widgets). The goal is to be able to enter inputs and copy the data from the raw data spreadsheet to a results spreadsheet, showing information for one user during that date range.
I keep getting an error ("Compile error: Sub or Function not defined") regarding my attempt to use the function to define columns for the appropriate dates. I'd appreciate any input. Thanks.
'Function changes column number to column name
Public Function fnColumnToLetter_CellAdressReplace(ByVal ColumnNumber As Integer)
fnColumnToLetter_CellAdressReplace = Replace(Replace(Cells(1, ColumnNumber).Address, "1", ""), "$", "")
End Function
Private Sub SearchButton1_Click()
'Dim variables
Dim sheet As Worksheet
Dim name1 As String
Dim date1 As Date
Dim date2 As Date
Dim STARTcol As String
Dim ENDcol As String
Dim pharmcol1 As String
Dim pharmcol2 As String
Set sheet = ActiveWorkbook.Sheets("Data")
Set Results = ActiveWorkbook.Sheets("Results")
'Variables for entries input into userform
name1 = userform1.NameBox1.Value
date1 = userform1.DateBox1.Value
date2 = userform1.DateBox2.Value
'Define row based on pharmacist name
rowno = Application.WorksheetFunction.Match(name1, Range("A:A"), 0)
pharmrow = "a" + rowno
'Find first column from start date
STARTcol = Application.WorksheetFunction.Match(date1, Range("A1:AZZ1"), 0)
'Find last column from end date
ENDcol = Application.WorksheetFunction.Match(date2, Range("A1:AZZ1"), 0)
'Call function to replace column number to column name
pharmcol1 = fnColumnToLetter_CellAddressReplace(STARTcol) + rowno
pharmcol2 = fnColumnToLetter_CellAddressReplace(ENDcol) + rowno
'Copy table array to RESULTS worksheet
sheet.Range("pharmcol1:pharmcol2").Copy Destination = Results.Range("A1")
End Sub
Compile error: Sub or Function not defined
Whenever I see this error I usually look for grammatical errors between the function/sub and the call. In your case you have spelled Address differently between the two.
Public Function fnColumnToLetter_CellAdressReplace(ByVal ColumnNumber As Integer)
pharmcol1 = fnColumnToLetter_CellAddressReplace(STARTcol) + rowno
pharmcol2 = fnColumnToLetter_CellAddressReplace(ENDcol) + rowno
Hope this helps.

VBA Macro Compile Error

I'm attempting to write a simple VBA macro that will take the active cell's column and the user's input to add a range of cells on a single row together. The range is calculated by adding the integer the user inputs to the active column and that is the end column. The problem is that it gives me a "Compile Error: Invalid Qualifier" when I run it, and gets angry at the 'total' line.
Here is my code. I'm just starting in VBA, but it can't be that hard....right?
Sub Food()
Dim first As Variant
Dim last As Integer
Dim days As Integer
Dim month As Range
Dim total As Double
first = ActiveCell.Column
days = InputBox("Days in the month?")
last = first + days
Set month.Value = Range(Cells(first, 4), Cells(last, 4))
total.Value = WorksheetFunction.Sum(month)
Worksheets(1).Cells(1, 13).Value = total
End Sub
Looks like a syntax error. Try dropping the .value on month and total.
Sub Food()
Dim first As Variant
Dim last As Integer
Dim days As Integer
Dim month As Range
Dim total As Double
first = ActiveCell.Column
days = InputBox("Days in the month?")
last = first + days
Set month = Range(Cells(first, 4), Cells(last, 4))
total = WorksheetFunction.Sum(month)
Worksheets(1).Cells(1, 13).Value = total
End Sub
If you want to get the value of a range into a variable, you put the .value with the range instead of the variable. For example:
x = cells(1,2).value
The .value property returns the value in a range object. So it doesn't make sense to use it on a function and will cause excel to throw and error if you try. It also doesn't make sense to try to set the month variable as a value instead of a range since that would just make it an array. If you want month to be an array you would want to set it as a variant instead of a range.
For more information on the .value property, see this link:
https://msdn.microsoft.com/en-us/vba/excel-vba/articles/range-value-property-excel

Excel VBA Set Variable to Equal Values between Dates

In Excel using VBA, I need to set a variable to equal a list of all the dates between a start and end date (similar to equaling a range containing multiple values). The catch is only the start and end date are in a range, non of the values in between.
In SQL Server I've used the Sys.Columns table to generate a list of dates between two dates that are not actually stored on that table. Is there a way to do something similar here without having each date between the start and end date written somewhere? I googled for a couple hours and didn't find anything on how to do this.
What I'm attempting to do is have a variable I can do a For Each loop on. So for each date I will check if it exists in another worksheet, if it does nothing will happen, if it does not it will be added.
I've tried:
Dim DatesInSettings As Date
DatesInSettings = StartDate To EndDate
For Each Date In DatesInSettings
'Insert commands here
Next DatesInSetting
But that clearly isn't the answer. Help?
This searches Sheet2 for dates between the start date and end dates on Sheet1 - in cells A1 and B1:
Sub RunDates()
Dim StartDate As Date
Dim EndDate As Date
Dim i As Date
StartDate = Sheet1.Range("A1")
EndDate = Sheet1.Range("B1")
For i = StartDate To EndDate
If WorksheetFunction.CountIf(Sheet2.Range("A1:A5"), i) > 0 Then
Debug.Print i; "- date found"
Else
Debug.Print i; "- date not found"
End If
Next i
End Sub
The following subroutine calls a dictionary that will store all the dates between two given endpoints. Then it uses a simple existence comparison to check if the dates on your list is inside the dictionary's items. If it's not, it's going to print them out as not in the list.
Modify accordingly to suit your needs. ;)
CODE:
Sub GetListOfDates()
Dim StartDate As Date, EndDate As Date
Dim DictOfDates As Object, DateToCheck As Variant, ListOfDates As Variant
Dim Iter As Long
Set DictOfDates = CreateObject("Scripting.Dictionary")
StartDate = "12/31/2013"
EndDate = "01/15/2014"
For Iter = StartDate + 1 To EndDate - 1
With DictOfDates
If Not .Exists(Iter) Then
.Add Iter, Empty
End If
End With
Next Iter
'--Print them somewhere.
'Range("A1").Resize(DictOfDates.Count, 1).Value = Application.Transpose(DictOfDates.Keys)
ListOfDates = Range("B1:B15").Value
For Each DateToCheck In ListOfDates
If Not DictOfDates.Exists(DateToCheck) Then
Debug.Print Str(DateToCheck) + " is not in the list!" '--Or whatever action you want.
End If
Next DateToCheck
Set DictOfDates = Nothing
End Sub
Let us know if this helps. :)
I solved it with a vector.
I hope it helps
Sub Dates_Vector()
Public Dates() As Date
ReDim Dates(End_Dat - Start_Date)
For x = 0 To End_Dat - Start_Date
Dates(x) = Dat_Ini + x
Next x
For Each Date In Dates
'Insert commands here
Next Date
End Sub