Как выполнить цикл по условиям в формуле countifs VBA

#excel #vba #countif

#excel #vba #countif

Вопрос:

У меня есть рабочий лист Excel со столбцом, полным формул COUNTIFS (). Для каждого, значение которого равно нулю, я должен вручную применить фильтры к соответствующим столбцам, чтобы выяснить, на каком шаге формулы результат достиг нуля. Что я хочу сделать, так это написать макрос, чтобы немного автоматизировать это. Например:

  =COUNTIFS('Data'!A:A,"Yes",'Data'!B:B,"Yes",'Data'!C:C,"Yes")
  

Если количество становится равным нулю, как только выполняется первое условие, я хочу, чтобы оно MsgBox имело значение 1. Если при вычислении второго условия оно становится равным нулю, верните вместо него значение 2. Если значение не достигает нуля до добавления третьего условия, я хочу, чтобы оно вместо этого возвращало 3 и так далее.

Для простоты предположим, что это должно работать только для одной ячейки, вместо того, чтобы перебирать каждую ячейку в моем столбце.

РЕДАКТИРОВАТЬ: Вот код, который я написал до сих пор. Потребуется формула COUNTIFS() и выполнить первое условие как COUNTIF(), но я не смог придумать, как расширить это, чтобы также выполнять более поздние условия.

             'Find Indexes
            countifsStart = InStr(1, cell.Formula, "COUNTIFS(")
            sheetNameStart = InStr(countifsStart, cell.Formula, "(")   2
            sheetNameEnd = InStr(sheetNameStart, cell.Formula, "'")
            searchRangeStart = InStr(sheetNameEnd, cell.Formula, "!")   1
            searchRangeSemicolon = InStr(searchRangeStart, cell.Formula, ":")
            searchStringStart = InStr(searchRangeSemicolon, cell.Formula, ",")   2
            searchStringEnd = InStr(searchStringStart, cell.Formula, ",") - 1

            'Parse formula components
            sheetName = Mid(cell.Formula, sheetNameStart, sheetNameEnd - sheetNameStart)
            searchColumn = Mid(cell.Formula, searchRangeStart, 1)
            Set searchRange = Range(searchColumn amp; ":" amp; searchColumn)
            searchString = Mid(cell.Formula, searchStringStart, searchStringEnd - searchStringStart)

            'Run the countif
            countIf = Application.WorksheetFunction.countIf(Sheets(sheetName).Range(searchColumn amp; ":" amp; searchColumn), searchString)

            'Point out the culprit
            MsgBox "Sheet Name: " amp; sheetName amp; vbNewLine amp; _
                   "Search Range: " amp; searchColumn amp; ":" amp; searchColumn amp; vbNewLine amp; _
                   "Search String: " amp; searchString amp; vbNewLine amp; _
                   "CountIf: " amp; countIf
  

Комментарии:

1. Что вы написали таким образом в качестве начального макроса? Если вы спрашиваете, возможно ли это, то да, это возможно… но вам нужно показать, что вы сделали, чтобы мы могли предоставить объективные ответы, обычно связанные с конкретными ошибками, которые вы получаете, или вопросами о неправильных выходных данных.

Ответ №1:

Возможно, что-то подобное сработает для вас:

 Sub tgr()

    Dim rFormula As Range
    Dim hArguments As Object
    Dim sArguments As String
    Dim sMessage As String
    Dim sTemp As String
    Dim sChar As String
    Dim lFunctionStart As Long
    Dim lParensPairs As Long
    Dim lQuotePairs As Long
    Dim bArgumentEnd As Boolean
    Dim i As Long, j As Long

    Set hArguments = CreateObject("Scripting.Dictionary")

    For Each rFormula In Selection.Cells
        lFunctionStart = InStr(1, rFormula.Formula, "COUNTIFS(", vbTextCompare)
        If lFunctionStart > 0 Then
            lFunctionStart = lFunctionStart   9
            lParensPairs = 1
            lQuotePairs = 0
            j = 0
            bArgumentEnd = False
            For i = lFunctionStart To Len(rFormula.Formula)
                sChar = Mid(rFormula.Formula, i, 1)
                Select Case sChar
                    Case "'", """"
                        If lQuotePairs = 0 Then
                            lQuotePairs = lQuotePairs   1
                        Else
                            lQuotePairs = lQuotePairs - 1
                        End If
                        sTemp = sTemp amp; sChar

                    Case "("
                        If lQuotePairs = 0 Then
                            lParensPairs = lParensPairs   1
                        End If
                        sTemp = sTemp amp; sChar

                    Case ")"
                        If lQuotePairs = 0 Then
                            lParensPairs = lParensPairs - 1
                            If lParensPairs = 0 Then
                                j = j   1
                                hArguments(j) = sTemp
                                sTemp = vbNullString
                                Exit For
                            Else
                                sTemp = sTemp amp; sChar
                            End If
                        Else
                            sTemp = sTemp amp; sChar
                        End If

                    Case ","
                        If lQuotePairs = 0 And lParensPairs = 1 Then
                            bArgumentEnd = True
                            j = j   1
                            hArguments(j) = sTemp
                            sTemp = vbNullString
                        Else
                            sTemp = sTemp amp; sChar
                        End If

                    Case Else
                        sTemp = sTemp amp; sChar

                End Select
            Next i
            For i = 1 To hArguments.Count Step 2
                If Len(sArguments) = 0 Then
                    sArguments = hArguments(i) amp; "," amp; hArguments(i   1)
                Else
                    sArguments = sArguments amp; "," amp; hArguments(i) amp; "," amp; hArguments(i   1)
                End If
                If Evaluate("COUNTIFS(" amp; sArguments amp; ")") = 0 Then
                    MsgBox "Search Range: " amp; hArguments(i) amp; Chr(10) amp; _
                           "Search String: " amp; hArguments(i   1) amp; Chr(10) amp; _
                           "Countif condition position: " amp; Int(i / 2)   1
                    Exit For
                End If
            Next i
        End If
    Next rFormula

End Sub
  

Ответ №2:

Публикация просто как альтернативный метод для получения аргументов (который я нашел в другом ответе в другом месте Питера Торнтона)

 Private args()

Sub Tester()
    Debug.Print GetZeroStep(Range("M1"))
End Sub


Function GetZeroStep(c As Range)

    Dim f, arr, i, r, s, n, rng, v
    f = Replace(c.Formula, "=COUNTIFS(", "=MyUDFTmp(")

    Debug.Print f
    r = Application.Evaluate(f)


    For i = 0 To UBound(args) Step 2
        n = n   1
        Set rng = args(i)
        v = args(i   1)
        If Not IsNumeric(v) Then v = """" amp; v amp; """"
        s = s amp; IIf(s <> "", ",", "") amp; "'" amp; rng.Parent.Name amp; "'!" amp; _
                                         rng.Address() amp; "," amp; v
        Debug.Print "=COUNTIFS(" amp; s amp; ")"
        r = Application.Evaluate("=COUNTIFS(" amp; s amp; ")")
        If r = 0 Then
            GetZeroStep = n
            Exit Function
        End If
    Next i
    GetZeroStep = 0 '<< didn't return zero on any step...
End Function

'https://social.msdn.microsoft.com/Forums/Lync/en-US/8c52aee1-5168-4909-9c6a-9ea790c2baca/get-formula-arguments-in-vba?forum=exceldev
Public Function MyUDFTmp(ParamArray arr())
   args() = arr
End Function