Excel Select Case?
5 jechaviz [2011-01-17 21:15:00]
Я хочу создать формулу "case" для 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"))
Он работает.
Спасибо всем.
После небольшой работы, наконец, я получу случай выбора 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 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")
Он соответствует "eq 5"
Спасибо, это было захватывающее и действительно воспитательное!
vba excel worksheet-function
1 ответ
18 Решение jtolle [2011-01-18 04:30:00]
O.K., нет никакого способа сделать то, что вы хотите. Вы не можете использовать ничего, кроме синтаксиса 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" находит позицию первого TRUE, а "CHOOSE" возвращает соответствующий "случай".
Вы можете эмулировать предложение "else", обернув все это в "IFERROR":
=IFERROR(CHOOSE(MATCH(TRUE, arr(A1>5, A1<5), 0), "gt 5", "lt 5"), "eq 5")
Если это слишком много для вас, вы всегда можете написать еще один VBA UDF, который приведет к MATCH, CHOOSE и т.д. внутри и вызовет его следующим образом:
=cases(arr(A1>5, A1<5, A1=5), "gt 5", "lt 5", "eq 5")
Это недалеко от вашего предложенного синтаксиса и намного, гораздо проще.
EDIT:
Я вижу, что вы уже придумали (хорошее) решение, которое ближе к тому, что вы действительно хотите, но я подумал, что добавлю это в любом случае, поскольку мое выражение выше о приведении 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 наших "случаев" работал, как Excel "CHOOSE", мы хотели бы возвращать ссылки при необходимости.
В основном, в новом UDF "случаях" мы сами делаем "выбор", индексируя в массив параметров значений case. Я удалил обработчик ошибок там, поэтому основные вещи, такие как несоответствие между результатами условия случая и значениями case, приведут к возвращаемому значению #VALUE!. Вероятно, вы добавили бы больше проверок в настоящую функцию, например, чтобы убедиться, что результаты условия были логическими и т.д.
Я рад, что вы достигли еще лучшего решения для себя! Это было интересно.
БОЛЬШЕ О 'assign':
В ответ на ваш комментарий, здесь больше о том, почему это является частью моего ответа. VBA использует другой синтаксис для назначения объекта переменной, чем для присвоения простого значения. Посмотрите на справки VBA или посмотрите этот вопрос stackoverflow, а другим понравятся: Что действительно делает ключевое слово Set в VBA?
Это имеет значение, потому что, когда вы вызываете функцию VBA из формулы Excel, параметрами могут быть объекты типа Range, в дополнение к номерам, строкам, логическим значениям, ошибкам и массивам. (См. Может ли Excel VBA UDF, вызванный из листа, когда-либо передаваться экземпляр любого класса объектной модели Excel VBA, отличного от "Range" ?)
Ссылки на диапазон - это то, что вы описываете, используя синтаксис Excel, такой как A1: Q42. Когда вы передаете один в Excel UDF в качестве параметра, он отображается как объект Range. Если вы хотите вернуть объект Range из UDF, вам нужно сделать это явно с помощью ключевого слова VBA "Установить". Если вы не используете "Установить", Excel вместо этого примет значение, содержащееся в пределах диапазона, и вернет его. В большинстве случаев это не имеет значения, но иногда вам нужен фактический диапазон, например, когда у вас есть именованная формула, которая должна оцениваться в диапазоне, потому что она используется в качестве источника для списка проверки.