Filtering Arrays by values in a specific Column The following function, as evidenced by its name KeepOrRemoveArrayRowsWhereComparisonIsTrue is probably trying to do too much. 

How can I better structure/refactor/split it to be cleaner whilst retaining the original intent: "Here's an array, filter it based on the values in this column"?



Public Function KeepOrRemoveArrayRowsWhereComparisonIsTrue(ByRef sourceArray As Variant, ByVal colIndex As Long, ByVal operator As ComparisonOperator, ByVal comparisonValue As Variant, ByVal hasHeaders As Boolean, ByVal keepOrRemoveOnTrue As KeepOrRemove, Optional ByRef arrayOfRemovedRows As Variant) As Variant

        Dim LB1 As Long, UB1 As Long
        AssignArrayBounds sourceArray, LB1, UB1

        Dim rowsToBeRemoved As Variant, removeCounter As Long
        rowsToBeRemoved = Array()
        ReDim rowsToBeRemoved(1 To 1)

        Dim ix As Long, startRow As Long
        If hasHeaders Then startRow = LB1 + 1 Else startRow = LB1

        Dim sourceValue As Variant

        Select Case keepOrRemoveOnTrue

            Case KeepOrRemove.keep
                removeCounter = 0
                For ix = startRow To UB1
                    sourceValue = sourceArray(ix, colIndex)
                    If IsNull(sourceValue) Then sourceValue = 0
                    If IsNull(comparisonValue) Then comparisonValue = 0
                        If Not ComparisonIsTrue(sourceValue, operator, comparisonValue) Then
                            removeCounter = removeCounter + 1
                            ReDim Preserve rowsToBeRemoved(1 To removeCounter)
                            rowsToBeRemoved(removeCounter) = ix
                        End If
                Next ix

            Case KeepOrRemove.Remove
                removeCounter = 0
                For ix = startRow To UB1
                    sourceValue = sourceArray(ix, colIndex)
                    If IsNull(sourceValue) Then sourceValue = 0
                    If IsNull(comparisonValue) Then comparisonValue = 0
                        If ComparisonIsTrue(sourceValue, operator, comparisonValue) Then
                            removeCounter = removeCounter + 1
                            ReDim Preserve rowsToBeRemoved(1 To removeCounter)
                            rowsToBeRemoved(removeCounter) = ix
                        End If
                Next ix

        End Select

        sourceArray = Remove2DArrayRows(sourceArray, rowsToBeRemoved, arrayOfRemovedRows)

        KeepOrRemoveArrayRowsWhereComparisonIsTrue = sourceArray

End Function
As mentioned in a comment (and in chat) already, your method does too many things at once

Do one thing and do that well. Any method that takes more than three arguments stinks to the heavens. At two arguments you should already be thinking whether you actually need them all.

Let's just look at the name for a second: KeepOrRemoveArrayRowsWhereComparisonIsTrue ... 


Why is there an Or inside?
Why are you talking about the Comparison?
Why are you talking about ArrayRows?


The latter two are easily fixed. Remove that unneeded fluff:

KeepOrRemoveWhere( ... )


wow.. that's... significantly shorter. It also opens the door for the next change. Split the method at it's responsibilities.

Flags are dangerous, because they're magical and they allow you to pack state-machines into arguments where they don't belong.
Instead of passing in a boolean telling you whether you remove or keep, and which becomes a lie as soon as you change the variable name, you should separate these two methods into one responsible for removing, and one for keeping.

This is often known as Remove and Retain (though Keep works just as well.

Now we have  the following signatures:

Public Function KeepWhere(ByRef sourceArray As Variant, ByVal colIndex As Long, ByVal operator As ComparisonOperator, ByVal comparisonValue As Variant, ByVal hasHeaders As Boolean, Optional ByRef arrayOfRemovedRows As Variant) As Variant


and 

Public Function RemoveWhere(ByRef sourceArray As Variant, ByVal colIndex As Long, ByVal operator As ComparisonOperator, ByVal comparisonValue As Variant, ByVal hasHeaders As Boolean, Optional ByRef arrayOfRemovedRows As Variant) As Variant


now we include the changes to your ComparisonOperator construction from here, to replace operator and comparisonValue by a single predicate.

The last thing that bugs me is hasHeaders. This is something the calling code should've taken care of by skipping the first row. 

I daresay that this is not something this function should take care of. It's yet another responsibility, and one that shouldn't be handed down the callchain, but processed early.

We end up with:

 Public Function KeepWhere(ByRef sourceArray As Variant, ByVal colIndex As Long, ByVal predicate As Predicate, Optional ByRef arrayOfRemovedRows As Variant) As Variant


Additionally (and lastly) I'm more used to returning the removed elements from such a function (since you still have a reference to the sourceArray anyways). As such the last parameter falls away and we're left with following implementation

Public Function KeepWhere (ByRef sourceArray As Variant, ByVal colIndex As Long, ByVal predicate As Predicate) As Variant
    Dim LB1 As Long, UB1 As Long
    AssignArrayBounds sourceArray, LB1, UB1

    Dim rowsToBeRemoved As Variant, removeCounter As Long
    rowsToBeRemoved = Array()
    ReDim rowsToBeRemoved(1 To 1)

    Dim sourceValue As Variant

    removeCounter = 0
    For ix = startRow To UB1
        sourceValue = sourceArray(ix, colIndex)
        If IsNull(sourceValue) Then sourceValue = 0
        If Not predicate.Test(sourceValue) Then
            removeCounter = removeCounter + 1
            ReDim Preserve rowsToBeRemoved(1 To removeCounter)
            rowsToBeRemoved(removeCounter) = ix
        End If
    Next ix

    sourceArray = Remove2DArrayRows(sourceArray, rowsToBeRemoved, arrayOfRemovedRows)

    KeepOrRemoveArrayRowsWhereComparisonIsTrue = arrayOfRemovedRows
End Function


and similarly for RemoveWhere...
It may be interesting to talk about a way to "Mark" things for rows. then you could actually make this even cleaner by using some flow like the following stub code:

Public Function KeepWhere (sourceArray, colIndex, predicate)
    rowsToBeRemoved = MarkRows (sourceArray, colIndex, predicate.Invert)
    KeepWhere = Remove2DArrayRows (sourceArray, rowsToBeRemoved)
End Function