6

ケースの選択動作をシミュレートするために、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 = "one"、A7 = "two"

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"))

できます。

全てに感謝。


少し仕事をした後、最終的に私は最初に欲しいものに近い、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

A1 = 5で、私が電話した場合:

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

これは次のように読み取ることができます。A1が5より大きい場合は、「gt 5」を選択しますが、A1が5より小さい場合は、「lt 5」を選択します。それ以外の場合は、「eq5」を選択します。実行後、「eq5」と一致します

ありがとう、それは刺激的で本当に教育的でした!

4

1 に答える 1

20

OK、あなたが望むことを正確に行う方法はまったくありません。数式内ではExcel構文以外は使用できないため、「A1=1から9」のようなものは不可能です。

文字列などを取得して解析する非常に複雑なVBAルーチンを作成することもできますが、それは実際には完全な小さな言語を設計および実装することになります。そして、あなたの「コード」はExcelではうまく機能しません。たとえば、あなたが次のようなものを呼んだ場合

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

(引用符はエスケープされていることに注意してください)、Excelは、A1参照が移動したとき、または数式がコピーされたときに、A1参照を更新しませんでした。それでは、「構文」オプション全体を破棄しましょう。

ただし、通常のExcel数式と1つの小さな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」は最初のTRUEの位置を見つけ、「CHOOSE」は対応する「ケース」を返します。

全体を「IFERROR」でラップすることにより、「else」句をエミュレートできます。

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

それが冗長すぎる場合は、MATCH、CHOOSEなどを内部に取り込む別のVBA UDFをいつでも記述して、次のように呼び出すことができます。

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

これは、提案された構文からそれほど遠くなく、はるかに単純です。

編集:

あなたはすでにあなたが本当に望んでいるものに近い(良い)解決策を考え出しているようですが、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

それは小さなヘルパールーチン'assign'を使用します:

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

'assign'ルーチンを使用すると、ユーザーが値または範囲参照のいずれかを使用してUDFを呼び出すことができるという事実に対処しやすくなります。'cases'UDFをExcelの'CHOOSE'のように機能させたいので、必要に応じて参照を返したいと思います。

基本的に、新しい'cases' UDF内では、case値のparam配列にインデックスを付けることにより、自分で「選択」部分を実行します。そこでエラーハンドラーを叩いたので、ケース条件の結果とケース値の不一致などの基本的なものは、戻り値#VALUE!になります。条件の結果がブール値であることを確認するなど、実際の関数にさらにチェックを追加する可能性があります。

しかし、あなたがあなた自身のためにさらに良い解決策に到達したことをうれしく思います!これはおもしろいです。

'割り当て'についての詳細:

あなたのコメントに応えて、それが私の答えの一部である理由についてもっと詳しく説明します。VBAは、オブジェクトを変数に割り当てるために、プレーンな値を割り当てる場合とは異なる構文を使用します。VBAヘルプを参照するか、このstackoverflowの質問やその他の質問を参照してください。キーワードSetはVBAで実際に何をしますか?

これが重要なのは、Excelの数式からVBA関数を呼び出す場合、パラメーターは、数値、文字列、ブール値、エラー、および配列に加えて、Range型のオブジェクトになる可能性があるためです。(ワークシートから呼び出されたExcel VBA UDFに、「範囲」以外のExcel VBAオブジェクトモデルクラスのインスタンスを渡すことはできますか?を参照してください。 )

範囲参照は、A1:Q42のようなExcel構文を使用して説明するものです。1つをパラメーターとしてExcelUDFに渡すと、Rangeオブジェクトとして表示されます。UDFからRangeオブジェクトを返す場合は、VBAの「Set」キーワードを使用して明示的に返す必要があります。'Set'を使用しない場合、Excelは代わりに範囲内に含まれる値を取得して返します。ほとんどの場合、これは重要ではありませんが、検証リストのソースとして使用されるため、範囲に評価する必要がある名前付き数式がある場合など、実際の範囲が必要な場合があります。

于 2011-01-18T01:30:42.870 に答える