#excel #vba
#excel #vba
Вопрос:
Я пытаюсь найти способ использовать VBA, чтобы показать мне числа в диапазоне 1-3 (только целые числа), которые отсутствуют в переменном диапазоне строк.
Столбец D может быть пустым или содержать однозначный номер (1, 2 или 3). В столбце E числа имеют убывающее значение. Наименьшее значение равно 1.
Диапазон зависит от значения в столбце E.
Примеры / объяснение:
- E2 = 4, поэтому он должен искать недостающие числа в D2-D5. В этих 4 строках есть все три числа.
- E6 = 2. Найдите недостающее число в D6-D7. Недостающее число равно 3.
- E8 = 1, и E8 пустое. Пропущенные числа 1, 2, 3
- (отсутствует в таблице) Значение в столбце E также может быть равно 1 и иметь номер 1-3 в соответствующем столбце D.
Я думаю, что это могло бы упростить, если бы я добавил пустую строку между каждым сгруппированным набором строк (между чередующимися цветами на изображении ниже). Но мне все еще, вероятно, нужна куча помощи для этого.
Я действительно надеюсь, что кто-нибудь здесь сможет помочь / понять, что я пытаюсь сделать. Это последний шаг моего кода, и я ошеломлен тем, как заставить это работать.
Комментарии:
1. Вы можете использовать
INDEX
для создания диапазона динамического размера, в котором указано количество строк, которые вы хотите проверить (например,INDEX(D2, 1, 0, E2, 1)
)2. Спасибо за ответ, @Chronocidal, но я не совсем понимаю, как заставить это работать. Индекс имеет только четыре аргумента? Я также добавил таблицу в исходное сообщение с «желаемым» результатом из кода. В столбце F показаны недостающие числа.
3. извините,
INDEX
иOFFSET
там перепутал —OFFSET(D2,1,0,E2,1)
, и объедините это сCOUNTIF
4. Я начинаю понимать, что, вероятно, я над головой. Начал изучать VBA полторы недели назад для этого проекта. До сих пор я в основном копировал / вставлял код, найденный в другом месте, а также использовал «макрос записи» при каждой возможности. Возможно, мне нужно сделать шаг назад и начать изучать основы. Просто так расстраивает, что для завершения моего кода не хватает только одной части.
5. Я пробовал подход грубой силы (проверьте,
1
если оно отсутствует, затем верните", 1", do the same for
2` и3
, затем объедините их в 1 строку и используйтеMID(<string>, 3, 99)
, чтобы удалить первую", "
), но я не смог из вашего объяснения получить ваш ожидаемый результат (у меня было2
для второй строки и1,2
для третьей строки, но у вас они оба пустые?), Я думаю, что разбиение его на пошаговый метод помогло бы вам это обдумать.
Ответ №1:
Метод грубой силы: (формула написана для ячейки F1
)
=IF(E1<IFERROR(OFFSET(E1,-1,0),-1),"",MID(IF(COUNTIF(OFFSET(D1,0,0,E1,1),1)>0,"",", 1") amp; IF(COUNTIF(OFFSET(D1,0,0,E1,1),2)>0,"",", 2") amp; IF(COUNTIF(OFFSET(D1,0,0,E1,1),3)>0,"",", 3"),3,99))
Разбиваем его:
=IF(E1<IFERROR(OFFSET(E1,-1,0),-1),"", <OUTPUT>)
Если значение в столбце E меньше значения над ним, ничего не показывать. В противном случае покажите наш<OUTPUT>
Следующие биты я покажу в обратном порядке, так как это будет иметь больше смысла:
OFFSET(D1, 0, 0, E1, 1)
Это<RANGE>
ячейка шириной в 1 ячейку иE1
высотой вD1
ячейку, начинающаяся с (0 строк и 0 столбцов)
Выясните, каких чисел не хватает в нашем <RANGE>
, и что <TEXT>
показать:
IF(COUNTIF(<RANGE>,1)>0,"",", 1") amp;
Если число1
не отображается в диапазоне, включите", 1"
в<TEXT>
IF(COUNTIF(<RANGE>,2)>0,"",", 2") amp;
Если число2
не отображается в диапазоне, включите", 2"
в<TEXT>
IF(COUNTIF(<RANGE>,3)>0,"",", 3")
Если число3
не отображается в диапазоне, включите", 3"
в<TEXT>
Это дает нам<TEXT>
из""
,", 1"
", 1, 2"
,", 1, 2, 3"
", 1, 3"
,", 2"
", 2, 3"
,,,, или", 3"
Наконец, приведите в порядок наши <OUTPUT>
:
MID(<TEXT>, 3, 99)
Удаляет первые 2 символа с начала<TEXT>
Комментарии:
1. Вау, я действительно поражен этим ответом! Большое вам спасибо! У меня не будет возможности опробовать это до понедельника, но это выглядит многообещающе! Вероятно, придется вернуться к этому несколько раз, чтобы полностью понять это. Я нажимаю стрелку вверх, нажму «решить», когда у меня будет возможность попробовать это! Еще раз, спасибо!
2. Работает как по волшебству, очень благодарен за предоставленную вами помощь! Могу я спросить, сколько времени вам потребовалось, чтобы разобраться с этим решением? Этот тип программирования мне очень чужд, поэтому я даже не могу дать обоснованное предположение об использовании времени!
3. @Kenny Принятие решения о первоначальном подходе, вероятно, заняло около 5 минут? Вы уже подтвердили, что существует только 3 варианта (
1
,2
и3
), поэтому отдельные проверки были выполнимы — для больших списков вам понадобятся функции Office365CONCAT
илиTEXTJOIN
. Главное там было, как включать только запятые между значениями, но, к счастью, я уже использовалMID
для удаления начальных символов подобным образом раньше.4. Столкнулся с кратким камнем преткновения, когда мой вывод не соответствовал вашему, но это было потому, что я выполнял каждую строку. Как только вы обновили изображение, я понял, что мне нужен оператор outer
IF
, чтобы убедиться, что это были только «максимальные» строки. Проверяя, что я столкнулся с ошибкой в строке 1, поскольку выше не было никакой ячейки для сравнения, но сразу же перешел к использованиюIFERROR
иOFFSET
для этого. Потратьте на это еще 2 минуты (всего ~ 7?) — а затем 10 минут, чтобы разбить его, ввести и, надеюсь, убедиться, что все было понятно!5. Многое помогло от того, что в прошлом я работал над другими вещами, которые были просто настолько похожи, что у меня были «частичные решения», копошащиеся в памяти, и это был просто случай пошагового разбиения требований, затем объединения правильных фрагментов вместе и их полировки.
Ответ №2:
Простой UDF
Предполагая, что ваш первый блок данных включает строки 2-5, вы могли бы ввести следующую формулу в ячейку, F2
например =ABSENT(D2:D5)
, или даже =ABSENT(D2:E5)
для отображения требуемой результирующей строки в столбце F (который в первом случае был бы пустым из-за OP).
Пример кода
Function absent$(rng As Range)
Dim iamp;, s$, tmp, found ' declare data types (Long, String, Variant, Variant)
For i = 1 To 3
tmp = Application.Transpose(Application.Index(rng, 0, 1)) ' change 1st column to flat array
found = Application.Match(i, tmp, 0) ' match numbers 1 to 3
If IsError(found) Then ' remember, if no match in array
s = s amp; i amp; IIf(i < 3, ",", "") ' add absent number to string
End If
Next i
absent = s ' return string
End Function
Комментарии:
1. Привет, к сожалению, данные постоянно меняются, но это не было бы проблемой, просто жестко запрограммируйте три строки, содержащие числа 1, 2 и 3 в верхней части блока данных, чтобы ваше предложение работало постоянно. Я, вероятно, буду придерживаться другого предоставленного решения, поскольку оно уже работает без каких-либо проблем. Но я обязательно поиграю и с вашим, так как это больше соответствует типу программирования, к которому я привык, поэтому я уверен, что это будет полезно в будущем!