VBA: adding up irregular ranges - vba

I need some help to create a macro which adds all the values on the column E between the rows with the "avg" word. the result should be displayed on the cells where the "Sum here" label is displayed. Both texts "avg" and "sum here" is just for illustrate the example, "avg" could be replaced by any other word and "sum here" should actually be the aggregation of the values above it.
The real challenge is that the number of ranges on column E is variable, so i would like to find a macro which is able to deal with "n" number of ranges on column E.
Finally, the values on column D are only the example of the expected value on the "sum here" cells.
This is what I have tried to far:
Sub Macro1()
'
' Macro1 Macro
'
Dim sumhere As Range
Dim startingpoint As Range
Dim endingpoint As Range
'
Range("C17").Select
Selection.End(xlDown).Select
If ActiveCell = "avg" Then
ActiveCell.Offset(rowoffset:=0, columnoffset:=2).Select
Set sumhere = ActiveCell
Set startingpoint = ActiveCell.Offset(rowoffset:=-1, columnoffset:=0)
Selection.End(xlUp).Select
If (ActiveCell.Value) = "Sum here" Then
Set endingpoint = ActiveCell.Offset(rowoffset:=1, columnoffset:=0)
sumhere.Formula = "=sum(range(startingpoint:endingpoint)"
Else
Selection.End(xlUp).Select
If (ActiveCell.Value) = "Sum here" Then
Set endingpoint = ActiveCell.Offset(rowoffset:=1, columnoffset:=0)
sumhere.Formula = "=Sum(Range(startingpoint.adress:endingpoint.adress))"
Else: End If
End If
End If
End Sub
Additionally, as you can see, I do not know, how to define a range using variables. My original idea was to combine this code with some kind of "do while" or/and "for i= 1 to x" and "next i". But I can't see how to combine it.

Using formula only, and providing that column A only has avg (or any text) on each subtotal row.
I've given two versions of the formula - the volatile version (updates everytime you change anything on the spreadsheet), and the non-volatile version (only updates if it needs to).
The formula should be entered on row 6 - change the $E6 to which ever row you need.
(volatile)
=SUM(OFFSET($E6,IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)-ROW()+1,,ROW()-1-IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)))
(non volatile):
=SUM(INDEX($E:$E,IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)+1):INDEX($E:$E,ROW()-1))
or if you don't mind using a helper column:
In cell B6:
=IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)
In E6: (volatile)
=SUM(OFFSET($E6,$B6-ROW()+1,,ROW()-1-$B6))
or (non volatile):
=SUM(INDEX($E:$E,$B6):INDEX($E:$E,ROW()-1))
Edit:
Thought I'd add a UDF to calculate it to if you're after VBA.
Use the function =AddSubTotal() in the rows you want the sub total to be shown in, or use =AddSubTotal("pop",6) to sum everything in column F (col 6) using "pop" rather than "avg".
Public Function AddSubTotal(Optional Delim As String = "avg", Optional ColNumber = 5) As Double
Dim rCaller As Range
Dim rPrevious As Range
Dim rSumRange As Range
Set rCaller = Application.Caller
With rCaller.Parent
Set rPrevious = .Range(.Cells(1, 1), .Cells(rCaller.Row - 1, 1)).Find(Delim, , , , , xlPrevious)
If Not rPrevious Is Nothing Then
Set rSumRange = rPrevious.Offset(1, ColNumber - 1).Resize(rCaller.Row - rPrevious.Row - 1)
Else
Set rSumRange = .Range(.Cells(1, ColNumber), .Cells(rCaller.Row - 1, ColNumber))
End If
End With
AddSubTotal = WorksheetFunction.Sum(rSumRange)
End Function

The following VBA routine assumes that
your data is in Columns C:E
Nothing else relevant (nothing numeric) in that range
Your "key word" where you want to show the sum is avg
avg (the key word) is hard-coded in the macro
You could easily modify this routine to also perform an average of those values, and put those results, for example, in Column D
Any of the above are easily modified
Option Explicit
Sub TotalSubRanges()
Dim vSrc As Variant, rSrc As Range
Dim dAdd As Double
Dim I As Long
Const sKey As String = "avg"
Set rSrc = Range(Cells(1, "C"), Cells(Rows.Count, "C").End(xlUp)).Resize(columnsize:=3)
vSrc = rSrc
'Do the "work" in a VBA array, as this will
' execute much faster than working directly
' on the worksheet
For I = 1 To UBound(vSrc, 1)
If vSrc(I, 1) = sKey Then
vSrc(I, 3) = dAdd
dAdd = 0
Else
If IsNumeric(vSrc(I, 3)) Then dAdd = dAdd + vSrc(I, 3)
End If
Next I
'write the results back to the worksheet
' and conditionally format the "sum" cells
With rSrc
.EntireColumn.Clear
.Value = vSrc
.Columns(3).AutoFit
.EntireColumn.ColumnWidth = .Columns(3).ColumnWidth
.FormatConditions.Delete
.FormatConditions.Add _
Type:=xlExpression, _
Formula1:="=" & .Item(1, 1).Address(False, True) & "=""" & sKey & """"
With .FormatConditions(1)
.Interior.ColorIndex = 6
End With
End With
End Sub

Surely you just need something like:
Sub sums()
Dim i As Integer, j As Integer, k As Integer
j = Range("C1048576").End(xlUp).Row
k = 1
For i = 1 To j
If Range("C" & i).Value <> "" Then
Range("E" & i).Value = "=Sum(E" & k & ":E" & i - 1 & ")"
k = i + 1
End If
Next i
End Sub

Change:
Dim startingpoint As Range
Dim endingpoint As Range
To:
Dim startingpoint As Variant
Dim endingpoint As Variant
As the startingpoint and endingpoint is used in a formula, you cant define them as a Range.

Related

VBA - check if a string is is 1 of those in a column of a different sheet, in an if statement

Hello i want to simpify the formula from
If InStr(1, Sheets("Le 2250").Cells(i, 1).Value, "250-") Or _
If InStr(1, Sheets("Le 2250").Cells(i, 1).Value, "135-") Or _
If InStr(1, Sheets("Le 2250").Cells(i, 1).Value, "700-")
to have the "250-" be 1 of the values in a column of a specific sheet, rather than having to put many "Or if ()" functions with the numerous strings i have to lpok for
Any help appreciated.
Here is an alternative that uses the Evaluate method...
If Evaluate("OR(ISNUMBER(MATCH({""*250-*"",""*135-*"",""*700-*""},{""" & Sheets("Le 2250").Cells(i, 1).Value & """},0)))") Then
Note, however, the number of characters used with the Evaluate method cannot exceed 255, otherwise an error will be returned.
Basically, build an array of your test values, and loop that array until you find something.
Something like this
Sub Demo()
Dim ws As Worksheet
Dim rTestStings As Range, TestStings As Variant
Dim TestValue As Variant
Dim idx As Long
Dim Found As Boolean
'Get Test Strings from Sheet. Adjust to suit your data
With rTestStings = Worksheets("specific sheet")
Set rTestStings = .Range(.Cells(1, 1), .Cells(.Rows.Count, 1).End(xlUp))
End With
TestStings = rTestStings.Value2
Set ws = Sheets("Le 2250")
'I'm guessing you are doing something like this
For i = SomeValue To SomeOtherValue
TestValue = ws.Cells(i, 1).Value
Found = False
For idx = LBound(TestStings, 1) To UBound(TestStings, 1)
If Not IsEmpty(TestStings(idx, 1)) Then 'incase there are gaps in your test data
If InStr(TestValue, TestStings(idx, 1)) Then
Found = True
Exit For
End If
End If
Next
If Found Then
MsgBox "Found " & TestStings(idx, 1) & " in cell " & ws.Cells(i, 1).Address
' do something ...
End If
Next i
End Sub

How To Have VBA Insert Formula Result as a Value

I got help last week getting my syntax and ranges correct, and thought I could just do a vlookup to finish it but apparently I was mistaken. It just seems like when I try to research how to accomplish this, I find various examples but I don't have the background to translate it to my code.
The macro runs and does almost everything its supposed to do. But in addition to inserting the arrays, there are 3 other cells that need values when there are blank cells in my ‘sourcerng’.
This is the logic for the cells that need values (the values are already in my worksheet, I just need to get them to these blank cells). I tried to do an IIF statement for these but I still have no idea what I'm doing. Would that be the best way? Should it just be another IF THEN statement?
rngBE - IF Column Z = 0 Then copy value from corresponding row in column O. Otherwise copy value from column Z
rngBG - IF Column AA = "Unknown" Then copy value from corresponding row in column I. Otherwise copy value from column AA.
rngBK - IF Column AB = "Unknown" Then copy value from corresponding row in column N. Otherwise copy value from column AB.
Sub AutomateAllTheThings6()
Dim arr3() As String
Dim arr11() As String
'Dim resBE As String
Dim rng3 As Range
Dim rng11 As Range
Dim rngBE As Range
Dim rngBG As Range
Dim rngBK As Range
Dim sourcerng As Range
'Dim firstRow As Long
Dim lastRow As Long
'Dim i As Long
Call OptimizeCode_Begin
'firstRow = 2
lastRow = ActiveSheet.Range("D1").End(xlDown).Row
Set rng3 = ActiveSheet.Range("BH2:BJ" & lastRow)
Set rng11 = ActiveSheet.Range("BL2:BV" & lastRow)
Set rngBE = ActiveSheet.Range("BE2:BE" & lastRow)
Set rngBG = ActiveSheet.Range("BG2:BG" & lastRow)
Set rngBK = ActiveSheet.Range("BK2:BK" & lastRow)
Set sourcerng = ActiveSheet.Range("BE2:BE" & lastRow)
arr3() = Split("UNKNOWN,UNKNOWN,UNKNOWN", ",")
arr11() = Split("UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,00/00/0000, _
00/00/0000,00/00/0000,00/00/0000,NEEDS REVIEW", ",")
For Each cell In sourcerng
If IsEmpty(cell) Then
Intersect(rng3, ActiveSheet.Rows(cell.Row)).Value = arr3
Intersect(rng11, ActiveSheet.Rows(cell.Row)).Value = arr11
'***PLS HELP***
Intersect(rngBE, ActiveSheet.Rows(cell.Row)).Value = "WEEEEE"
Intersect(rngBG, ActiveSheet.Rows(cell.Row)).Value = "WOOOOO"
Intersect(rngBK, ActiveSheet.Rows(cell.Row)).Value = "WAAAAA"
End If
Next
Range("BR2:BU2").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.NumberFormat = "mm/dd/yyyy"
Columns("BF:BF").Select
Selection.Delete Shift:=xlToLeft
Call OptimizeCode_End
End Sub
'*********TESTING***********
'resBE = IIf(Cells(13,Z).Value = 0, Cells(13,BE).Value = Cells(13,Z), Cells(13,BE).Value = Cells(13,O))
'***************************************
'For i = firstRow To lastRow
' valZ = Range("Z" & i)
' valOh = Range("O" & i)
'
' If valZ = 0 Then
' rngBE.Value = valOh
' Else rngBE.Value = valZ
' End If
There are several ways to do your task. If you're more of an "Excel" person than VBA you might consider this approach: You can inject the syntax of any "regular" formula in R1C1 Format.
So the formula mentioned above =if($Z2=0,$O2,$Z2) is .FORMULA format for any value in row 2.
But in .FORMULAR1C1 it can be inserted in ANY cell as: =IF(RC26=0,RC15,RC26) (basically no rows up or down, but always columns O (15) and Z(26).
So, your modified code would have something like this:
Intersect(rngBE, ActiveSheet.Rows(cell.Row)).FormulaR1C1 = "=IF(RC26=0,RC15,RC26)"
Intersect(rngBE, ActiveSheet.Rows(cell.Row)).Value = _
Intersect(rngBE, ActiveSheet.Rows(cell.Row)).Value
Again, this is NOT the most efficient way to accomplish your task, but if you're dealing with thousandsof rows, versus tens to hundreds of thousands, I wouldn't worry about it and it gives you a new tool to use.

Iterating through row range group data and take an action

I am getting to know Excel VBA. I have a working program that uses an action button on one sheet opens a source workbook and data worksheet, selects data and puts that into a second workbook and destination sheet. I then sort the data as needed and it looks like this
Destination sheet, sorted and annotated duplicates
I am now trying to select the data based on col 2 "B" where the items are duplicated and/or not duplicated then perform an action (send an email to the manager about the staff under their control). I can get an email to work but its selecting the data that I'm having trouble with.
the output data would be col 1 & col 3 to 5 e.g.
Dear Manager1,
you staff member/s listed below have achieved xyz
Person1 22/06/2017 11/08/2017 22/08/2017
Person11 22/06/2017 11/08/2017 22/08/2017
Person15 22/06/2017 11/08/2017 22/08/2017
congratulations....
So what I hope somebody can help me with is a clue how I get to look at the data in col 2
add the Row data required to an array or something then to check the next Row add it to the same something until it is different to the next Row Pause do the action then do the next iteration. Resulting in:
Manager1 .....Person 1,11,15action
Manager10 ..... Person 10action
Manager2 ..... Person 12,16,2,25,28action
Manager3 ..... Person 13,17,26,29,3action
until last line is reached.
I am so confused with arrays / lookups and loops I have lost the plot somewhere along the way.
I have a variable lastTmp which tells me the last line of data in the set, this will vary each month.
The Range is:
Set rng1 = Range("B5:B" & Cells(Rows.Count, "B").End(xlUp).row)
The last piece of my working code is:
Dim lp As Integer
lp = 1
For Each cell In rng1
If 1 < Application.CountIf(rng1, cell.Value) Then
With cell
.Offset(0, 4) = "duplicate : "
.Offset(0, 5) = lp
End With
Else
With cell
.Offset(0, 4) = "NOT duplicate : "
.Offset(0, 5) = 0
End With
End If
Next cell
You will be better placed to confront confusion if you do your indenting more logically. Related For / Next, If / Else / End If and With / End With should always be on the same indent level for easier reading. I rearranged your original code like this:-
For Each Cell In Rng1
If 1 < Application.CountIf(Rng1, Cell.Value) Then
With Cell
.Offset(0, 4) = "duplicate : "
.Offset(0, 5) = lp
End With
Else
With Cell
.Offset(0, 4) = "NOT duplicate : "
.Offset(0, 5) = 0
End With
End If
Next Cell
It now becomes apparent that the With Cell / End With need not be duplicated. I have further presumed that your variable lp actually was intended to hold the count. That made me arrive at the following compression of your code.
Dim Rng1 As Range
Dim Cell As Range
Dim lp As Integer
' the sheet isn't specified: uses the ActiveSheet
Set Rng1 = Range("B5:B" & Cells(Rows.Count, "B").End(xlUp).Row)
For Each Cell In Rng1
With Cell
lp = Application.CountIf(Rng1, .Value)
.Offset(0, 4) = IIf(lp, "", "NOT ") & "duplicate : "
.Offset(0, 5) = lp
End With
Next Cell
Consider using a Dictionary or Collection, whenever, checking for duplicates.
Here I use a Dictionary of Dictionaries to compile lists of Persons by Manager.
Sub ListManagerList1()
Dim cell As Range
Dim manager As String, person As String
Dim key As Variant
Dim dictManagers As Object
Set dictManagers = CreateObject("Scripting.Dictionary")
For Each cell In Range("B5:B" & Cells(Rows.Count, "B").End(xlUp).Row)
manager = cell.Value
person = cell.Offset(0, -1).Value
If Not dictManagers.Exists(manager) Then
dictManagers.Add manager, CreateObject("Scripting.Dictionary")
End If
If Not dictManagers(manager).Exists(person) Then
dictManagers(manager).Add person, vbNullString
End If
Next
For Each key In dictManagers
Debug.Print key & " -> "; Join(dictManagers(key).Keys(), ",")
Next
End Sub
I recommend you wanting Excel VBA Introduction Part 39 - Dictionaries
Assuming your data is as in the image
Then following code will give you result as in the image below.
Sub Demo()
Dim srcSht As Worksheet, destSht As Worksheet
Dim lastRow As Long, i As Long
Dim arr1(), arr2()
Dim dict As Object
Set dict = CreateObject("scripting.dictionary")
Set srcSht = ThisWorkbook.Sheets("Sheet2") 'change Sheet2 to your data sheet
Set destSht = ThisWorkbook.Sheets("Sheet1") 'change Sheet1 to your output sheet
arr1 = Application.Index(srcSht.Cells, [row(1:7000)], Array(2, 1)) 'See note below
arr2 = arr1
For i = 1 To UBound(arr1, 1)
If Not dict.exists(LCase$(arr1(i, 1))) Then
dict.Add LCase$(arr1(i, 1)), i
Else
arr2(i, 1) = vbNullString
arr2(dict.Item(LCase$(arr1(i, 1))), 2) = arr2(dict.Item(LCase$(arr1(i, 1))), 2) & "," & arr1(i, 2)
End If
Next
destSht.Range("A1").Resize(UBound(arr1, 1), UBound(arr1, 2)) = arr2 'display result
destSht.Columns("a").SpecialCells(xlBlanks).EntireRow.Delete
End Sub
Note : For details on assigning range to array see this.

Converting a long nested if and or formula into vba case

I have a nestled If And Or formula that I am trying to convert into probably a Case formula using VBA (or any other suggestion would be great), but I am a beginner and not sure how. The reason is that this formula currently is in every cell AG12:ACG500 and takes up so much memory that the spreadsheet is extremely slow.
Basically, I am trying to match the date in column Z12:Z500 (Outage Month Start) to the date in Row AG6:ACG6 (DATES), then looking in column C12:C500 (ACTV_NAME)... which provides the output of either R, S, L, MR, MS, ML, ?R, ?S or ?L in the cell where the two dates (column Z and row 6) intersect .. this should coincide with the where the Gantt Chart bar starts .. I do not need any help with the Gantt chart bars/color coding .. I just need help basically labeling them with the fore-mentioned.
=IF(OR($Z12="",$AA12=""),"",IF(AND(AG$6=$Z12,$Z12<>"",$C12="Relay",$L12="No"),"R",IF(AND(AG$6=$Z12,$Z12<>"",$C12="Substation",$L12="No"),"S",IF(AND(AG$6=$Z12,$Z12<>"",$C12="Line",$L12="No"),"L",IF(AND(AG$6=$Z12,$Z12<>"",$C12="Relay",$L12="YES"),"MR",IF(AND(AG$6=$Z12,$Z12<>"",$C12="Substation",$L12="YES"),"MS",IF(AND($AF$6=$Z12,$Z12<>"",$C12="Line",$L12="YES"),"ML",IF(AND(AG$6=$Z12,$Z12<>"",$C12="Relay",$L12="Maybe"),"?R",IF(AND(AG$6=$Z12,$Z12<>"",$C12="Substation",$L12="Maybe"),"?S",IF(AND($AF$6=$Z12,$Z12<>"",$C12="Line",$L12="Maybe"),"?L",""))))))))))
Your formula can be simplified to:
=IF(OR($Z12="",$AA12=""),"",IF(AND(AG$6=$Z12,$Z12<>""),IF($L12 = "YES","M" & LEFT($C12,1),IF($L12 = "Maybe","?" & LEFT($C12,1),LEFT($C12,1))),""))
Converting single formula into a routine that loops
This is Scott Craner's simplified version of your formula, converted into a routine that will loop through all the cells in AG12:ACG500 and check each cell for their intersecting criteria.
I am not able to test this code because I have no data set to base it off of. That being said, I'm not sure that it will perform in the way you desire. Let me know if it works for you.
Sub compareDates()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets(1)
Dim dataRange As Range: Set dataRange = ws.Range("AG12:ACG500")
Dim oMS As Range, aN As Range, idk As Range, d As Range
Dim yNM As Range, myCell As Range, myRow As Long, myCol As Long
For Each myCell In dataRange
myRow = myCell.Row
myCol = myCell.Column
Set oMS = ws.Range("Z" & myRow) 'Outage Month Start
Set aN = ws.Range("C" & myRow) 'ACTV_NAME
Set idk = ws.Range("AA" & myRow) 'not sure what AA is for
Set d = ws.Cells(6, myCol) 'DATES
Set yNM = ws.Range("L" & myRow) 'yes no maybe
If oMS.Value = "" Or idk.Value = "" Then
myCell.Value = ""
ElseIf d.Value = oMS.Value And oMS.Value <> "" Then
If UCase(yNM.Value) = UCase("Yes") Then
myCell.Value = "M" & Left(aN.Value, 1)
ElseIf UCase(yNM.Value) = UCase("Maybe") Then
myCell.Value = "?" & Left(aN.Value, 1)
Else: myCell.Value = Left(aN.Value, 1)
End If
Else: myCell.Value = ""
End If
Next myCell
End Sub

Compare 4 columns in one excel sheet using vba

I need your help please, I have 4 columns in an excel sheet and I need to compare them 2 by 2 i will explain to you :
In column A i have users(user1,user2,user3 ...)
In column B i have functionalities ( fonc1, fonc2, fonc3.....)
In column C i have users(user1,user2,user3 ...)
In column D i have functionalities ( fonc1, fonc2, fonc3.....)
The columns C and D are a new version of columns A and B in the columns C and D the users may change order or change functionalities .
When i execute my code i put the result in other new columns:
column F where i have the users
column G where i put the Deleted_functionalities
column H where i put the New_functionalities
The first problem is that the code doesn't get the users it get only the new and deleted functionalities. The second problem is that when the column A is more than column C where the users are stocked the code doesn't work. Can you please help me to find a solution? Thank you in advance .
Here is my code and the file I am working on :
Private Sub CommandButton2_Click()
Dim rngCell As Range
For Each rngCell In Range("B2:B2000")
If WorksheetFunction.CountIf(Range("D2:D2000"), rngCell) = 0 Then
Range("G" & Rows.Count).End(xlUp).Offset(1) = rngCell
End If
Next
For Each rngCell In Range("D2:D2000")
If WorksheetFunction.CountIf(Range("B2:B2000"), rngCell) = 0 Then
Range("H" & Rows.Count).End(xlUp).Offset(1) = rngCell
End If
Next
End Sub
and this is the excel file
http://www.cjoint.com/c/FCxnwjp22rv
try this
Private Sub CommandButton2_Click()
Dim ws As Worksheet
Dim cell As Range, funcCell As Range
Dim oldUserRng As Range, newUserRng As Range, reportRng As Range
Dim iReport As Long
Dim oldFunc As String, newFunc As String
Set ws = ThisWorkbook.Worksheets("users") '<== adapt it to your needs
With ws
Set oldUserRng = .Columns(1).Resize(.Rows.Count - 1).Offset(1).SpecialCells(xlCellTypeConstants, xlTextValues)
Set newUserRng = .Columns(3).Resize(.Rows.Count - 1).Offset(1).SpecialCells(xlCellTypeConstants, xlTextValues)
Set reportRng = .Range("F1:I1") '<== added one report column to account for unchanged functions
End With
reportRng.Value = Array("user", "deleted", "new", "same")
iReport = 1
For Each cell In oldUserRng
With cell
oldFunc = .Offset(, 1).Value
Set funcCell = FindAndOffset(newUserRng, .Value, 1)
If funcCell Is Nothing Then
reportRng.Offset(iReport) = Array(.Value, "", "", oldFunc)
Else
newFunc = funcCell.Value
If newFunc = oldFunc Then
reportRng.Offset(iReport) = Array(.Value, "", "", newFunc)
Else
reportRng.Offset(iReport) = Array(.Value, oldFunc, newFunc, "")
End If
End If
iReport = iReport + 1
End With
Next cell
For Each cell In newUserRng
With cell
Set funcCell = FindAndOffset(oldUserRng, .Value, 1)
If funcCell Is Nothing Then
reportRng.Offset(iReport) = Array(.Value, "", .Offset(, 1).Value, "")
iReport = iReport + 1
End If
End With
Next cell
End Sub
Not so sure it does what you need.
you'd better provide screenshots of "before" and "after" scenarios.
BTW, is it safe to assume that both old and new user columns cannot hold duplicates (i.e.: two or more "userX" in column A and/or column C?)
But it does speed up thing considerably since it iterates only through non empty cells.
I hope I get what you want to achieve. Does the following solve your problem?
Private Sub CommandButton2_Click()
Dim rngCell As Range
For Each rngCell In Range("A2:A20000")
If WorksheetFunction.CountIf(Range("C2:C20000"), rngCell) > 0 Then
Range("F" & Rows.Count).End(xlUp).Offset(1) = rngCell
Range("F" & Rows.Count).End(xlUp).Offset(0, 1) = rngCell.Offset(0, 1).Value
Range("F" & Rows.Count).End(xlUp).Offset(0, 2) = Application.WorksheetFunction.VLookup(rngCell.Value, Range("C2:D20000"), 2, 0)
ElseIf (rngCell <> "") Then
Range("F" & Rows.Count).End(xlUp).Offset(1) = rngCell
Range("F" & Rows.Count).End(xlUp).Offset(0, 1) = rngCell.Offset(0, 1).Value
End If
Next
For Each rngCell In Range("C2:C20000")
If (WorksheetFunction.CountIf(Range("A2:A20000"), rngCell) = 0 And rngCell <> "") Then
Range("F" & Rows.Count).End(xlUp).Offset(1) = rngCell
Range("F" & Rows.Count).End(xlUp).Offset(0, 2) = rngCell.Offset(0, 1).Value
End If
Next
End Sub
A user is only included in column F when he appears both in columns A and C.In case you want to include every user that is either in column A or C the code has to be altered.