Excel选择案例?

jec*_*viz 6 excel vba worksheet-function

我想为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
Run Code Online (Sandbox Code Playgroud)

如果可以,它必须接受excel公式或vba代码,才能在获取案例之前对单元格进行操作,ig

cases({len(A1)<7, "too short"},{else,"good length"})
Run Code Online (Sandbox Code Playgroud)

如果可以,它必须接受或更多的细胞来评估,ig

如果A2 = A3 = A4 = A5 = 1且A1 = 2,A6 ="1",A7 ="2"

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

顺便说一下,| 意思是,或者!=意味着不同

有帮助吗?


我很感激.

我能写的是这个:

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
Run Code Online (Sandbox Code Playgroud)

我以这种方式调用函数

=cases(arr(A1>5, A1<5, A1=5),arr( "gt 5", "lt 5", "eq 5"))
Run Code Online (Sandbox Code Playgroud)

我无法达到目标,它只适用于第一个条件,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
Run Code Online (Sandbox Code Playgroud)

当我调用该函数时:

=selectCases(arr(A1>5, A1<5, A1=5),arr( "gt 5", "lt 5", "eq 5"))
Run Code Online (Sandbox Code Playgroud)

有用.

谢谢大家.


下班后,最后我得到一个excel选择案例,最接近我想要的东西.

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
Run Code Online (Sandbox Code Playgroud)

当A1 = 5时我打电话:

=cases(A1>5, "gt 5",A1<5, "lt 5","eq 5")
Run Code Online (Sandbox Code Playgroud)

它与"eq 5"相匹配

谢谢,这是令人兴奋的,真正的教育!

jto*_*lle 20

好吧,完全没有办法做你想要的.您不能在公式中使用除Excel语法之外的任何内容,因此"A1 = 1到9"之类的东西是不可能的.

可以编写一个非常复杂的VBA例程,它接受字符串或其他东西并解析它们,但这实际上相当于设计和实现一个完整的小语言.并且您的"代码"不能很好地与Excel一起使用.例如,如果你打电话的话

=cases("{A1="""",""there is nothing""},{else,A1}")
Run Code Online (Sandbox Code Playgroud)

(注意转义的引号),Excel移动或复制公式时不会更新A1引用.所以让我们放弃整个"语法"选项.

然而,事实证明,你得到多少我想到你居然要与普通Excel公式加一个很小的VBA UDF的行为.首先是UDF:

Public Function arr(ParamArray args())
    arr = args
End Function
Run Code Online (Sandbox Code Playgroud)

这让我们可以从一组参数创建一个数组.由于参数可以是表达式而不仅仅是常量,我们可以从这样的公式中调用它:

=arr(A1=42, A1=99)
Run Code Online (Sandbox Code Playgroud)

并获取一个布尔值数组.

使用这个小型UDF,您现在可以使用常规公式来"选择案例".它们看起来像这样:

=CHOOSE(MATCH(TRUE, arr(A1>5, A1<5, A1=5), 0), "gt 5", "lt 5", "eq 5")
Run Code Online (Sandbox Code Playgroud)

发生了什么''arr'返回一个布尔数组,'MATCH'找到第一个TRUE的位置,'CHOOSE'返回相应的"case".

您可以通过将整个事物包装在'IFERROR'中来模拟"else"子句:

=IFERROR(CHOOSE(MATCH(TRUE, arr(A1>5, A1<5), 0), "gt 5", "lt 5"), "eq 5")
Run Code Online (Sandbox Code Playgroud)

如果这对你来说太冗长了,你总是可以编写另一个将MATCH,CHOOSE等带入其中的VBA UDF,并像这样调用它:

=cases(arr(A1>5, A1<5, A1=5), "gt 5", "lt 5", "eq 5")
Run Code Online (Sandbox Code Playgroud)

这与您提出的语法相差不远,而且更简单.

编辑:

我看到你已经提出了一个更接近你真正想要的(好的)解决方案,但我认为无论如何我都要加上这个,因为我上面关于在UDF中引入MATCH,CHOOSE等的声明使它成为了它看起来更容易.

所以,这是一个'案例'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
Run Code Online (Sandbox Code Playgroud)

它使用一个小帮助程序,'assign':

Public Sub assign(ByRef lhs, rhs)
    If IsObject(rhs) Then
        Set lhs = rhs
    Else
        lhs = rhs
    End If
End Sub
Run Code Online (Sandbox Code Playgroud)

'assign'例程可以更容易地处理用户可以使用值或范围引用调用UDF这一事实.由于我们希望我们的'case'UDF像Excel的'CHOOSE'一样工作,我们希望在必要时返回引用.

基本上,在新的'case'UDF中,我们通过索引到case值的param数组来自己做"选择"部分.我在那里打了一个错误处理程序,所以基本的东西,如案例条件结果和案例值之间的不匹配将导致返回值为#VALUE!.您可能会在实际函数中添加更多检查,例如确保条件结果是布尔值等.

不过,我很高兴你为自己找到了更好的解决方案!这很有意思.

关于'assign'的更多信息:

在回应您的评论时,这里更多的是为什么这是我答案的一部分.VBA使用不同的语法将对象分配给变量,而不是分配普通值.查看VBA帮助或查看此stackoverflow问题以及其他类似问题:关键字Set在VBA中实际执行了什么操作?

这很重要,因为当您从Excel公式调用VBA函数时,除了数字,字符串,布尔值,错误和数组之外,参数还可以是Range类型的对象.(请参阅从工作表调用的Excel VBA UDF是否可以传递除"Range"以外的任何Excel VBA对象模型类的实例?)

范围引用是您使用Excel语法(如A1:Q42)描述的内容.将一个Excel UDF作为参数传递给它时,它将显示为Range对象.如果要从UDF返回Range对象,则必须使用VBA"Set"关键字显式执行.如果您不使用'Set',Excel将取代Range中包含的并返回该值.大多数情况下这并不重要,但有时您需要实际范围,例如当您有一个必须评估范围的命名公式时,因为它被用作验证列表的源.