Checking if data exists and adding it if not - vba

I have two rows containing dates which I am trying to compare and see if they are the same. If not, I want to add the extra data (i.e. row 1 can change and so I want those changes added to row 2). Ive tried looking around and also writing my own loop but I`m getting an error.
UPDATE following the comment, i am still getting an error; "Unable to get the CountIf propety of the worksheetfunction class"
I am wondering if there are any alternatives to check if the data is present somewhere in the second row add add it if not.
I am new to vba and programming in general and any help would be appreciated.
Dim Dates As Range
Set Dates = Range("C23:O23")
Dim hisdate As Range
Set hisdate = Range("C35:O35")
For Each cell In Dates 'this is gonna first add new dates
If WorksheetFunction.CountIf(hisdate, cell) > 0 Then 'do nothing
Else
Set hisdate = Union(hisdate, cell)
End If
Next

As mentioned in the comments, WorksheetFunction.CountIf doesn't work with multi-area ranges. You could write your own countIf-function that loops over all areas (works even if the range is not a multi-area range)
Dim cell As Range
For Each cell In Dates 'this is gonna first add new dates
If MyCountIf(hisdate, cell) <= 0 Then
Set hisdate = Union(hisdate, cell)
End If
Next
Debug.Print hisdate.Address
Function MyCountIf(fullRange As Range, val As Variant)
MyCountIf = 0
Dim r As Range
For Each r In fullRange.Areas
MyCountIf = MyCountIf + WorksheetFunction.CountIf(r, val)
Next
End Function

Related

Trying to loop non-contiguous rows in a range

I am trying to write something that will loop through a range of non-contiguous rows in a table, and change column data in each row ... for example, ClearContents. The range will be dynamic, and the rows I want to loop through will all be "Selected."
I tried the following, but it stopped after the first row. I am pretty sure the problem is that the next row is non-contiguous to the first row:
For Each b In a.Rows
mainTasks.DataBodyRange(Range("mainTasks[Status]").Column).ClearContents
Next b
Then I had the bright idea to write something that works only the "selected" column cells. I tried using If ... .Value = Selected and that didn't work.
Am I trying to do something that Excel 2016 VBA can't do? That is, loop through non-contiguous rows in a range? I've been researching and tried several other things that don't work. Can you tell me if I am going down the wrong rabbit hole?
You don't have any variables in your loop.
It will just the clear the one column repeatedly in the loop. You need to somehow reference b in the loop to have a different outcome in each loop.
I tried to simplfy your code in the comments for testing purpose, but the following code should help,
Dim x As Integer
Dim myrange As Range
For x = 1 To 30
If (mainTasks.Cells(x, 2) = "completed" Or mainTasks.Cells(x, 2) = "Dismissed") And mainTasks.Cells(x, 3) <> "" Then
If myrange Is Nothing Then
Set myrange = mainTasks.Cells(x, 2)
Else
Set myrange = Union(myrange, mainTasks.Cells(x, 2))
End If
End If
Next x
myrange.ClearContents
I tried it out and it works Ok given the way I set it up. Hope it helps!
You will also have to add the code to copy the data, I am clearing the contents in this one, given that was your original question
This works too (i think)
Dim selectedRange As Range
Set selectedRange = Application.Selection
For Each Row In selectedRange.Rows
Debug.Print Row.Address
Next Row

Excel Tables VBA: Trying to select rows based on column value

I am a beginner. I have been trying to teach myself VBA and researching this question for two weeks, including reviewing all the relevant answers on this forum. I give up!
I am trying to loop through the rows of a table to select a table row based on the content of one of the row's cells (naming a particular column). I want to use the name of the table column. But something is wrong with my "If ... Then" statement. I get errors with every attempt. Right now I get a compile error "Expected Then or Go To," with the period before Value highlighted. But I have a feeling that if I fixed that error there would be another one right behind it. What am I getting wrong, besides trying to learn this on my own? ;>)
Thanks in advance!
Sub CommandButton1_Click()
Dim tbl As ListObject
Dim x As Long
Set tbl = ActiveSheet.ListObjects("Table1")
For x = 1 To tbl.Range.Rows.Count
If (Range("Table1[Status]")).Value = "Completed"
'I can't seem to find the right statement to put between If and .Value!
Rows(x).Select
End If
Next x
End Sub
Change If (Range("Table1[Status]")).Value = "Completed" to If (Range("Table1[Status]").Value) = "Completed" Then. Your .Value just needed to be put in the brackets. I would also highly suggest looking into the Rubberduck add-in for VBA. It has an Auto Indenter so your code always looks in order.
Sub CommandButton1_Click()
Dim tbl As ListObject
Dim x As Long
Dim myRange As Range
Set tbl = ActiveSheet.ListObjects("Table1")
For x = 1 To tbl.Range.Rows.Count
If tbl.DataBodyRange(x, Range("Table1[Status]").Column) = "Completed" Then
If myRange Is Nothing Then
Set myRange = tbl.ListRows(x).Range
Else
Set myRange = Union(myRange, tbl.ListRows(x).Range)
End If
End If
Next x
myRange.Select
End Sub

How do I Count Cells in Columns of a non-rectangular range

So I have a range that will be selected by a user. It will be two columns, usually not next to each other. I need to count the number of cells in each column of the selected range to determine if the number of cells in each column is equal. (If it's not I will need to adjust the range.)
For example, a User may select B5:B10 and D6:D9. So I need the code to return 6 and 4 respectively.
I've tried:
Set rng = Selection
rng.Columns(1).Count
this returns 1, which obviously isn't the number I need.
Thanks in advance!
You can use the Areas method of the Range object to get the areas of the range. Areas are groups of contiguous ranges within a non-contiguous range.
Set rng = Selection
For i = 1 to rng.Areas.Count
debug.print rng.Areas(i).Cells.Count
Next
There is caveat here that you may need to test for, and that is if the user selects, for example, A1:B10, with one mouse drag. Since this is a contiguous range, it will only have one Area and you will not get two distinct numbers. If you need to test for this, you can do something like the below.
Set rng = Selection
'non-contiguous ranges will always return one column, if there are mutiple columns both cell counts are equal by default
If rng.Columns.Count = 1 Then
For i = 1 to rng.Areas.Count
debug.print rng.Areas(i).Cells.Count
Next
End If
Dammit #Scott - just beat me to it.
Although I'm using the Rows property - if the user selects A1:B19 it will return 19 in a single element of the array, if they select A1:A19 and then B1:B19 it will return 19 in two elements of the array.
Using Cells it will return 38 in a single element, or 19 in two elements.
Sub Test()
Dim rRange As Range
Dim lRows() As Long
Dim x As Long
Set rRange = Selection
With rRange
ReDim lRows(1 To .Areas.Count)
For x = 1 To .Areas.Count
lRows(x) = .Areas(x).Rows.Count
Next x
End With
End Sub

Finding cellindex depending on cell content in excel

Im in a bit of a hard spot at the moment, i've been searching the web for some weeks now whitout any luck. I hope some of you might have the answer however.
I want to search through a row for a cell with a specific numeric value. The row contains weeknumbers. When found i would like to be able to handle the cell like an object, or at least i need to know the coordiantes of the cell for further processing.
Furthermore, can that process be reversed. Meaning, can i get the value from say the next cell in the same row if i have the coordinates of the prior cell?
Example would be:
Search through row for the week number for this week.
Hopefully get the coordinates for the cell that contains the right number.
When i have that column, i want to find the content of the cells monday to friday for that week for every coworker
I doesnt matter if this i done by VBA or formulas.
Thanks a lot in advance.
Best regards Teambit
No need for VBA for this... you should be able to achieve it with formulae easily enough.
So, cell L3 has the formula:=ADDRESS(3,MATCH($B$1,$A$3:$N$3,0)). This will perform a MATCH on the week entered into cell $B$1 and find it in the range of weeks $A$3:$N$3. The ADDRESS function will return the cell address for where the week number is found, so $G$3 in my example.
Then the lower grid/table will display the working week for the current week. The OFFSET function is moving the cell address down 3 rows, then right 1, 2, 3, 4, or 6 rows for each day of the week. OFFSET will return a 0 for a blank, so the T function is ensuring we get a text equivalent, or blank instead of 0. I'm using INDIRECT to pass the OFFSET function the starting cell address, so it is using the value of our starting week cell ($G$3), instead of that cell ($L$1).
I've not put anything in to make this dynamic per worker, so it's always using row 3, but you can get a cell reference using ADDRESS from a VLOOKUP for a name and then plug that into the other formulae.
Check if this help u..
Sub SubOne()
Dim sh As Worksheet
Dim rw As Range
Dim iFoundIt As Range
Dim RowCount As Integer
' var to iterate trough rows
RowCount = 0
Set sh = ActiveSheet
For Each rw In sh.Rows
' iterate over all cells in first row
If sh.Cells(rw.Row, 1).Value = "ThisIsMySpecialValue" Then
' value is found, we save the cell for future options
Set iFoundIt = sh.Cells(rw.Row, 1)
Exit For
End If
RowCount = RowCount + 1
Next rw
Debug.Print (iFoundIt.Row)
Debug.Print (iFoundIt.Column)
End Sub
I know if i understand your question well, but if you have coordinates of cell and you wana e.g. next cell on left you can use something like this
Sub findWeek()
Dim targetSheet As Worksheet
Set targetSheet = Sheets("Sheet1")
Dim l As Long
Dim row As Long
row = 2
Dim myVal As String
myVal = "some name of week you looking for"
Dim resultCell As Range
With targetSheet
Do While .Cells(row, l).Value <> myVal
l = l + 1
Loop
resultCell = .Cells(row, l)
End With
End Sub
So i updated answer, try something like this. Just change sheet name, maybe row and even name which are you looking for. And in value resultCell you will have object of cell which you are looking for. And if you look at cell/range api you can get its coordinates etc...
So you can make it a function which will return range, or whatever you want... But always in value resultCell will be cell which you are looking for.
Maybe you will need to take care if week is not found ;) because this will stuck in loop, but its not that hard

Finding average of selection and then assigning it to a cell

I am attempting to create some dynamic code that, at this point, will select a bunch of cells, move the selection over two columns, then find the average of that selection and send that value to a cell. This is what I have so far, I am getting stuck at averaging the selection I've made:
Sub StatMakersub(Rng1 As Range)
Dim MyRange As Range
Dim Cell As Object
Dim InQuestion As Range
Dim SelAvg As Object
'Check every cell in the range for matching criteria.
For Each Cell In Rng1
If Cell.Value = 2000 Then
If MyRange Is Nothing Then
Set MyRange = Range(Cell.Address)
Else
Set MyRange = Union(MyRange, Range(Cell.Address))
End If
End If
Next
'Select the new range of only matching criteria
MyRange.Select
Selection.Offset(0, 2).Select
Set InQuestion = Selection
Range("P2").Formula = "=Average(Selection)"
Range("Q2").Formula = "=STDDEVA(Selection)"
End Sub
I can't find much on the web about how to average range variables.
You can calculate the average of a selection in this way:
Application.WorksheetFunction.Average("Here you put your range")
The result is a value and not an object, so you should use a variable. Taking names from your case you should use it like this:
SelAvgResult = Application.WorksheetFunction.Average(InQuestion)
I put another name for the variable, but you may still use SelAvg if you like. Just remind to define it as a variable (you may choose your desired format depending on the data size) instead of object if you do not need it anymore.
You may use then this variable for setting the value of your desired cell.
I have a last note: your code seems to replicate the already existing formula AVERAGEIF. If your criteria column is for instance column A and value you should use for calculating the average are in column C, You could directly set the value of the cell where you want the average like this:
=AVERAGEIF(A:A, "2000", C:C)
In this case you would avoid VBA.
Have you tried using the Sum worksheet function for calculating the sum of the range?
Xsum = WorksheetFunction.Sum(arrayX)
and dividing the Xsum value with the length of the array?
One thing I should metion is that you do not need to select the range to work with it. You can use it directly and doing so will also improve how fast your code runs.
To insert your worksheet functions, use the Range.Address function to generate a cell reference to put into the formulas.
https://msdn.microsoft.com/en-us/library/office/ff837625.aspx