Excel Выберите дело? - PullRequest
       4

Excel Выберите дело?

6 голосов
/ 17 января 2011

Я хочу создать формулу «падежей» для Excel, чтобы имитировать поведение «Выбор случая» (с несколькими аргументами, а также необязательно). Если A1 и A2 являются ячейками Excel, это цель:

A1 Case:     A2 Formula:                                                                   A2 Result
5            cases({A1>5,"greather than 5"}, {A1<5, "less than 5"},{else,"equal to 5"})    equal to 5   
Hi           cases({A1="","there is nothing"},{else,A1})                                   Hi
1024         cases({5<A1<=10,10},{11<=A1<100,100},{A1>100,1000})                           1000
12           cases({A1=1 to 9, "digit"}, {A1=11|22|33|44|55|66|77|88|99, "11 multiple"})   (empty) 
60           cases({A1=1 to 49|51 to 99,"not 50"})                                         not 50

Если это возможно, он должен принять формулы Excel или код VBA, чтобы выполнить операцию над ячейкой, прежде чем делать случай, например,

cases({len(A1)<7, "too short"},{else,"good length"})

Если это возможно, он должен принять или более клеток для оценки, например,

если A2 = A3 = A4 = A5 = 1 и A1 = 2, A6 = «один», A7 = «два»

cases(A1!=A2|A3|A4|A5, A6}, {else,A7}) will produce "two"

Кстати, | означает или,! = означает другое

Любая помощь?


Я благодарен.

Я мог бы написать следующее:

Public Function arr(ParamArray args())  'Your function, thanks
    arr = args
End Function

Public Function cases(arg, arg2)  'I don't know how to do it better
    With Application.WorksheetFunction
        cases = .Choose(.Match(True, arg, 0), arg2)
    End With
End Function

Я вызываю функцию таким образом

=cases(arr(A1>5, A1<5, A1=5),arr( "gt 5", "lt 5", "eq 5"))

И я не могу получить цель, она просто работает для первого условия, A1> 5.

Я исправил это с помощью for, но я думаю, что это не так элегантно, как ваше предложение:

Function selectCases(cases, actions)
    For i = 1 To UBound(cases)
        If cases(i) = True Then
            selectCases = actions(i)
            Exit Function
        End If
    Next
End Function

Когда я вызываю функцию:

=selectCases(arr(A1>5, A1<5, A1=5),arr( "gt 5", "lt 5", "eq 5"))

Работает.

Спасибо за все.


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

Function cases(ParamArray casesList())
    'Check all arguments in list by pairs (case, action),
    'case is 2n element
    'action is 2n+1 element
    'if 2n element is not a test or case, then it's like the "otherwise action"
    For i = 0 To UBound(casesList) Step 2
        'if case checks
        If casesList(i) = True Then
            'then take action
            cases = casesList(i + 1)
            Exit Function
        ElseIf casesList(i) <> False Then
            'when the element is not a case (a boolean value),
            'then take the element.
            'It works like else sentence
            cases = casesList(i)
            Exit Function
        End If
    Next
End Function

Когда А1 = 5 и я звоню:

=cases(A1>5, "gt 5",A1<5, "lt 5","eq 5")

Соответствует "eq 5"

Спасибо, это было захватывающе и по-настоящему познавательно!

1 Ответ

19 голосов
/ 18 января 2011

ОК, нет никакого способа сделать именно то, что вы хотите.Вы не можете использовать в формуле ничего, кроме синтаксиса Excel, поэтому такие вещи, как 'A1 = 1 до 9', просто невозможны.

Вы могли бы написать довольно сложную подпрограмму VBA, которая потребовалаСтроки или что-то в этом роде и проанализировали их, но на самом деле это сводится к разработке и реализации полного небольшого языка.И ваш «код» не будет хорошо работать с Excel.Например, если вы вызвали что-то вроде

=cases("{A1="""",""there is nothing""},{else,A1}")

(обратите внимание на экранированные кавычки), Excel не обновит вашу ссылку A1, когда она будет перемещена или формула скопирована.Итак, давайте отбросим весь вариант «синтаксиса».

Однако, оказывается, вы можете получить большую часть поведения, которое, я думаю, вам действительно нужно с обычными формулами Excel плюс один крошечный VBA UDF.Сначала UDF:

Public Function arr(ParamArray args())
    arr = args
End Function

Это позволяет нам создать массив из набора аргументов.Поскольку аргументы могут быть выражениями, а не просто константами, мы можем вызвать их из формулы, подобной этой:

=arr(A1=42, A1=99)

и получить обратно массив логических значений.

С этим небольшим UDF,Теперь вы можете использовать обычные формулы для «выбора дел».Они бы выглядели так:

=CHOOSE(MATCH(TRUE, arr(A1>5, A1<5, A1=5), 0), "gt 5", "lt 5", "eq 5")

Что происходит, так это то, что 'arr' возвращает логический массив, 'MATCH' находит позицию первого ИСТИНА, а 'CHOOSE' возвращает соответствующий «регистр».

Вы можете эмулировать предложение "else", заключив все это в 'IFERROR':

=IFERROR(CHOOSE(MATCH(TRUE, arr(A1>5, A1<5), 0), "gt 5", "lt 5"), "eq 5")

Если это слишком многословно для вас, вы всегда можете написать другой UBA VBA, который быпоместите MATCH, CHOOSE и т. д. внутрь и назовите его так:

=cases(arr(A1>5, A1<5, A1=5), "gt 5", "lt 5", "eq 5")

Это не далеко от предложенного вами синтаксиса и намного, намного проще.

РЕДАКТИРОВАТЬ:

Я вижу, что вы уже придумали (хорошее) решение, которое ближе к тому, что вы действительно хотите, но я подумал, что все равно добавлю это, так как мое утверждение выше о том, чтобы сделать MATCH, CHOOSE и т. Д. ВнутриUDF сделал его проще, чем он есть на самом деле.

Итак, вот UDF для "случаев":

Public Function cases(caseCondResults, ParamArray caseValues())
    On Error GoTo EH

    Dim resOfMatch
    resOfMatch = Application.Match(True, caseCondResults, 0)

    If IsError(resOfMatch) Then
        cases = resOfMatch
    Else
        Call assign(cases, caseValues(LBound(caseValues) + resOfMatch - 1))
    End If

    Exit Function

EH:
    cases = CVErr(xlValue)
End Function

Он использует небольшую вспомогательную подпрограмму 'assign':

Public Sub assign(ByRef lhs, rhs)
    If IsObject(rhs) Then
        Set lhs = rhs
    Else
        lhs = rhs
    End If
End Sub

Процедура 'назначить' просто облегчает работу с тем, что пользователи могутвызывать UDF со значениями или ссылками на диапазон.Поскольку мы хотим, чтобы наша UDF «дела» работала так же, как «CHOOSE» в Excel, мы хотели бы при необходимости возвращать обратные ссылки.

По сути, в новой UDF «дела» мы выполняем часть «выбор»мы сами, индексируя в массив параметров значения регистра.Я включил обработчик ошибок, поэтому базовые вещи, такие как несоответствие между результатами условий случая и значениями случая, приведут к возвращаемому значению #VALUE !.Вы, вероятно, добавите больше проверок в реальную функцию, например, убедитесь, что результаты условия были логическими, и т. Д.

Я рад, что вы достигли даже лучшего решения для себя!Это было интересно.

БОЛЬШЕ О 'назначить':

В ответ на ваш комментарий, здесь больше о том, почему это является частью моего ответа.VBA использует другой синтаксис для назначения объекта переменной, чем для назначения простого значения.Посмотрите на справку VBA или посмотрите на этот вопрос stackoverflow и другие подобные ему: Что на самом деле делает ключевое слово Set в VBA?

Это важно, потому что, когда вы вызываете функцию VBA из Excelформула, параметры могут быть объектами типа Range, в дополнение к числам, строкам, логическим значениям, ошибкам и массивам.(См. Может ли пользовательская функция Excel VBA, вызванная из рабочего листа, когда-либо передаваться экземпляру какого-либо класса объектной модели Excel VBA, кроме 'Range'? * )

Ссылки на диапазоны - это то, что вы описываете, используя синтаксис Excel, такой как A1: Q42.Когда вы передаете его в Excel UDF в качестве параметра, он отображается как объект Range.Если вы хотите вернуть объект Range из UDF, вы должны сделать это явно с ключевым словом VBA 'Set'.Если вы не используете «Set», Excel вместо этого примет значение , содержащееся в Range, и вернет его.В большинстве случаев это не имеет значения, но иногда вам нужен фактический диапазон, например, когда у вас есть именованная формула, которая должна соответствовать диапазону, поскольку она используется в качестве источника для списка проверки.

...