Найти недостающее число с переменным диапазоном для цикла

#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 ), поэтому отдельные проверки были выполнимы — для больших списков вам понадобятся функции Office365 CONCAT или 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 в верхней части блока данных, чтобы ваше предложение работало постоянно. Я, вероятно, буду придерживаться другого предоставленного решения, поскольку оно уже работает без каких-либо проблем. Но я обязательно поиграю и с вашим, так как это больше соответствует типу программирования, к которому я привык, поэтому я уверен, что это будет полезно в будущем!