Как сцепить несколько значений в одну ячейку по критерию? СцепитьЕсли
Все чаще вижу на разных форумах вопросы типа:
Получить такую:
Стандартными функциями это сделать весьма проблематично, т.к. заранее неизвестно сколько будет этих оценок и фамилий.. MIcrosoft работает над усовершенствованием Excel и теперь стало возможным сделать это и стандартными функциями. Правда, с небольшими ограничениями: сделать это могут только пользователи
Аргументы функции:
("; ") - символ(или несколько символов), которым необходимо объединять найденные значения( - диапазон, в котором искать критерийA2:A20 )( - критерий. Значение, на основании которого необходимо сцеплять значения. Значение просматривается в диапазоне значений(A2 ) )A2:A20 ( - из этого диапазона берется значение для сцепления, если значение напротив в диапазонe(B2:B20 ) ) совпадает с искомым значениемA2:A20 A2
Для любителей "старой школы" можно вместо функции
Аргументы точно такие же, как в формуле выше. Правда эта формула вводится в ячейку как формула массива(т.е. одновременным нажатием трех клавиш
А для пользователей Excel 2016 и ниже я написал небольшую функцию пользователя на VBA, которая решает данную проблему. Так же подобную функцию называют "многоразовый ВПР", потому что она по критерию возвращает ВСЕ значения для этого критерия, а не только первое.
'--------------------------------------------------------------------------------------- ' Author : The_Prist(Щербаков Дмитрий) ' Профессиональная разработка приложений для MS Office любой сложности ' Проведение тренингов по MS Excel ' http://www.excel-vba.ru ' Purpose: '--------------------------------------------------------------------------------------- Function СцепитьЕсли(ByRef Диапазон As Range, ByVal Критерий As String, ByRef Диапазон_сцепления As Range, Optional Разделитель As String = " ", Optional БезПовторов As Boolean = False) As String Dim li As Long, sStr As String, avItem, avDateArr(), avRezArr(), lUBnd As Long If Диапазон.Count > 1 Then avDateArr = Intersect(Диапазон, Диапазон.Parent.UsedRange).Value avRezArr = Intersect(Диапазон_сцепления, Диапазон_сцепления.Parent.UsedRange).Value If Диапазон.Rows.Count = 1 Then avDateArr = Application.Transpose(avDateArr) avRezArr = Application.Transpose(avRezArr) End If Else ReDim avDateArr(1, 1): ReDim avRezArr(1, 1) avDateArr(1, 1) = Диапазон.Value avRezArr(1, 1) = Диапазон_сцепления.Value End If lUBnd = UBound(avDateArr, 1) 'Определяем вхождение операторов сравнения в Критерий Dim objRegExp As Object, objMatches As Object Set objRegExp = CreateObject("VBScript.RegExp") objRegExp.Global = False: objRegExp.Pattern = "=|<>|=>|>=|<=|=<|>|<" Set objMatches = objRegExp.Execute(Критерий) 'Если есть вхождения If objMatches.Count > 0 Then Dim sStrMatch As String sStrMatch = objMatches.Item(0) Критерий = Replace(Replace(Критерий, sStrMatch, "", 1, 1), Chr(34), "", 1, 2) If IsNumeric(Критерий) And Критерий <> "" Then Критерий = CDbl(Критерий) End If Select Case sStrMatch Case "=" For li = 1 To lUBnd If avDateArr(li, 1) = Критерий Then If Trim(avRezArr(li, 1)) <> "" Then _ sStr = sStr & IIf(sStr <> "", Разделитель, "") & avRezArr(li, 1) End If Next li Case "<>" For li = 1 To lUBnd If avDateArr(li, 1) <> Критерий Then If Trim(avRezArr(li, 1)) <> "" Then _ sStr = sStr & IIf(sStr <> "", Разделитель, "") & avRezArr(li, 1) End If Next li Case ">=", "=>" For li = 1 To lUBnd If avDateArr(li, 1) >= Критерий Then If Trim(avRezArr(li, 1)) <> "" Then _ sStr = sStr & IIf(sStr <> "", Разделитель, "") & avRezArr(li, 1) End If Next li Case "<=", "=<" For li = 1 To lUBnd If avDateArr(li, 1) <= Критерий Then If Trim(avRezArr(li, 1)) <> "" Then _ sStr = sStr & IIf(sStr <> "", Разделитель, "") & avRezArr(li, 1) End If Next li Case ">" For li = 1 To lUBnd If avDateArr(li, 1) > Критерий Then If Trim(avRezArr(li, 1)) <> "" Then _ sStr = sStr & IIf(sStr <> "", Разделитель, "") & avRezArr(li, 1) End If Next li Case "<" For li = 1 To lUBnd If avDateArr(li, 1) < Критерий Then If Trim(avRezArr(li, 1)) <> "" Then _ sStr = sStr & IIf(sStr <> "", Разделитель, "") & avRezArr(li, 1) End If Next li End Select Else 'Если нет вхождения For li = 1 To lUBnd If avDateArr(li, 1) Like Критерий Then If Trim(avRezArr(li, 1)) <> "" Then _ sStr = sStr & IIf(sStr <> "", Разделитель, "") & avRezArr(li, 1) End If Next li End If If БезПовторов Then Dim oDict As Object, sTmpStr Set oDict = CreateObject("Scripting.Dictionary") sTmpStr = Split(sStr, Разделитель) On Error Resume Next For li = LBound(sTmpStr) To UBound(sTmpStr) oDict.Add sTmpStr(li), sTmpStr(li) Next li sStr = "" sTmpStr = oDict.keys For li = LBound(sTmpStr) To UBound(sTmpStr) sStr = sStr & IIf(sStr <> "", Разделитель, "") & sTmpStr(li) Next li End If СцепитьЕсли = sStr End Function |
Чтобы правильно использовать приведенный код, необходимо сначала ознакомиться со статьей Что такое функция пользователя(UDF)?. Вкратце: скопировать текст кода выше, перейти в редактор VBA(
По принципу работы функция похожа на стандартную СУММЕСЛИ. Указывается диапазон значений(где просматривать значение), критерий и диапазон значений для сцепления. Символ для разделения слов указывать необязательно.
Диапазон
Критерий
Диапазон_сцепления
Разделитель
БезПовторов - если указать 1 или ИСТИНА, то в результате получится строка, в которой нет одинаковых значений. Если указать 0 или ЛОЖЬ, то будут выведены все значения. По умолчанию значение ЛОЖЬ.
Примечание: для работы функции должны быть разрешены макросы
Пример СцепитьЕсли.xls (68,0 KiB, 16 095 скачиваний)
Также см.:
ВПР_МН
Сцепить_МН
СцепитьЕсли
Что такое функция пользователя(UDF)?
ВПР с возвратом всех значений
Статья помогла? Поделись ссылкой с друзьями!

Поиск по меткам
Access apple watch Multex Power Query и Power BI VBA управление кодами Бесплатные надстройки Дата и время Записки ИП Надстройки Печать Политика Конфиденциальности Почта Программы Работа с приложениями Разработка приложений Росстат Тренинги и вебинары Финансовые Форматирование Функции Excel акции MulTEx ссылки статистикаКомментарии, не имеющие отношения к комментируемой статье, могут быть удалены без уведомления и объяснения причин. Если есть вопрос по личной проблеме - добро пожаловать на Форум
Макрос работает не корректно, в частности не распознает четырёхзначные значения (например поле «Все сотрудники, с окладом меньше или равным 15000» не указывает значение «Петрова Вера», пока вместо её оклада «6000» не вбить значение «06000»
Подскажите пожалуйста, почему у меня не работает?
http://screenshot.ru/b12ea1e7e077eeea29a9af222c82879c.png
Попробуйте при объявлении
ByVal Критерий
записать без типа:
ByVal Критерий
Добрый день, Дмитрий.
Не вдупляю как это сделать, подскажите пожалуйста.
Вроде бы поправил нужное ))
Но теперь формула =СцепитьЕсли(D2:D16;">9";B2:B16;",") и вовсе не работает. Выдает ошибку #ЗНАЧ!
Простите затупил, не то правил. Все заработало. Ура!!! Спасибо огромное за формулу.
Функция работает на отлично. Огромное СПАСИБО разработчику.
Пользовалась другими макросами, но после их запуска можно было идти спать.
Татьяна, спасибо! Рад, что код пришелся по душе :)
Добрый день.
Скажите, пожалуйста, вот на версии х86 (32) работает нормально.
А на версии офис х64, выдает ошибку #знач / # value.
Может что-то нужно изменить в коде?
Камиль, сложно сказать. Сам код не зависит от битности системы. Ошибка у Вас появляется прямо в скачанном файле, без внесения в него правок? Макросы разрешены? Если нет - надо включить как положено, потому что очень похоже именно на это.
Дмитрий, здравствуйте
подскажите как удалить этот макрос в случае необходимости?
Установка и работа проблем не вызвали, всё работает как надо. Большое спасибо!
В общем-то ничего сложного: заходите в редактор VBA(Alt+F11) -переходите в модуль, в котором записан код функции и удаляете весь код от Function СцепитьЕсли до End Function.Как удалить в ячейке формулу, оставив значения?
Только перед этим не забудьте преобразовать в значения результат вычисления этой функций:
иначе все расчеты, сделанные этой функцией, будут утеряны.
Я неточно выразился, мне нужно удалить сам модуль, а он под паролем
Андрей, вопрос не совсем по адресу тогда. В приложенном к статье файле нет пароля ни на листы, ни на проект VBA. Так что ничем помочь не могу, Вы, скорее всего используете файл не из статьи.
Дмитрий, разобрался. Это надстройка которую я сам включил, Solver. Не знал что она решена как макрос и отобразится в окне с модулями) Извините за беспокойство, еще раз спасибо за качественный продукт.
Спасибо за данную функцию. Единственно, она не понимает именованные диапазоны, полученные в Диспетчере имен виде ФОРМУЛ. Если вывести такие именованные диапазоны в ячейки и сослаться на эти ячейки, то все работает, а вот если в формуле прямо сослаться на данные именованные диапазоны, то выдает #ЗНАЧ. Буду рад, если подскажете, как в коде прописать определение переменных, чтобы такие диапазоны все-таки понимались. Спасибо!
Доброго! А подскажите, если мне нужно сцепить все данные по диапазону, за исключением данных в строке диапазона с КРИТЕРИЕМ?
к примеру: =СцепитьЕсли(B5:B18;B10;C5:C18;"-";0)
т.е. в сцепляемом диапазоне данных C5:C18, данные по В10 мы не сцепляем, а сцепка идет C5:C9 + C11:C18
Помогите пожалуйста
Vic, здесь надо в коде обрабатывать этот момент. Притом придется либо еще один параметр вводить(вроде номеров строк для исключения из просмотра), либо переписывать принцип обработки.
Дмитрий, а если предположим, в следующей ячейке, с помощью какой-то формулы, вычтем из сцепленных данных, значение ячейки С10,
т.е. D10 =СцепитьЕсли(B5:B18;B10;C5:C18;"-";0)
D11 =.....D10-C10
есть что-то подобное в excel ?
вопрос снят
использовал в соседней ячейки минус "слово" =ПОДСТАВИТЬ(BE253;AZ253;) как раз из диапазона убирает ненужное значение
unction СЦЕПИТЬЕСЛИ(ByRef Диапазон As Range, ByVal Критерий As String, ByVal Критерий2 As String, ByRef Диапазон_сцепления As Range, Optional Разделитель As String = ",", Optional БезПовторов As Boolean = False) As String
Dim li As Long, sStr As String, avItem, avDateArr(), avRezArr(), lUBnd As Long
If Диапазон.Count > 1 Then
avDateArr = Intersect(Диапазон, Диапазон.Parent.UsedRange).Value
avRezArr = Intersect(Диапазон_сцепления, Диапазон_сцепления.Parent.UsedRange).Value
If Диапазон.Rows.Count = 1 Then
avDateArr = Application.Transpose(avDateArr)
avRezArr = Application.Transpose(avRezArr)
End If
Else
ReDim avDateArr(1, 1): ReDim avRezArr(1, 1)
avDateArr(1, 1) = Диапазон.Value
avRezArr(1, 1) = Диапазон_сцепления.Value
End If
lUBnd = UBound(avDateArr, 1)
'Определяем вхождение операторов сравнения в Критерий
Dim objRegExp As Object, objMatches As Object
Set objRegExp = CreateObject("VBScript.RegExp")
objRegExp.Global = False: objRegExp.Pattern = "=||=>|>=|<=|=| 0 Then
Dim sStrMatch As String
sStrMatch = objMatches.Item(0)
Критерий = Replace(Replace(Критерий, sStrMatch, "", 1, 1), Chr(34), "", 1, 2)
Критерий2 = Replace(Replace(Критерий2, sStrMatch, "", 1, 1), Chr(34), "", 1, 2)
If IsNumeric(Критерий) And Критерий "" Then
Критерий = CDbl(Критерий)
If IsNumeric(Критерий2) And Критерий2 "" Then
Критерий2 = CDbl(Критерий2)
End If
Select Case sStrMatch
Case "="
For li = 1 To lUBnd
If avDateArr(li, 1) = Критерий And avDateArr(li, 1) = Критерий2 Then
If Trim(avRezArr(li, 1)) "" Then _
sStr = sStr & IIf(sStr "", Разделитель, "") & avRezArr(li, 1)
End If
Next li
Case ""
For li = 1 To lUBnd
If avDateArr(li, 1) Критерий And avDateArr(li, 1) Критерий2 Then
If Trim(avRezArr(li, 1)) "" Then _
sStr = sStr & IIf(sStr "", Разделитель, "") & avRezArr(li, 1)
End If
Next li
Case ">=", "=>"
For li = 1 To lUBnd
If avDateArr(li, 1) >= Критерий And avDateArr(li, 1) >= Критерий2 Then
If Trim(avRezArr(li, 1)) "" Then _
sStr = sStr & IIf(sStr "", Разделитель, "") & avRezArr(li, 1)
End If
Next li
Case "<=", "=<"
For li = 1 To lUBnd
If avDateArr(li, 1) <= Критерий And avDateArr(li, 1) <= Критерий2 Then
If Trim(avRezArr(li, 1)) "" Then _
sStr = sStr & IIf(sStr "", Разделитель, "") & avRezArr(li, 1)
End If
Next li
Case ">"
For li = 1 To lUBnd
If avDateArr(li, 1) > Критерий And avDateArr(li, 1) > Критерий2 Then
If Trim(avRezArr(li, 1)) "" Then _
sStr = sStr & IIf(sStr "", Разделитель, "") & avRezArr(li, 1)
End If
Next li
Case "<"
For li = 1 To lUBnd
If avDateArr(li, 1) < Критерий And avDateArr(li, 1) < Критерий2 Then
If Trim(avRezArr(li, 1)) "" Then _
sStr = sStr & IIf(sStr "", Разделитель, "") & avRezArr(li, 1)
End If
Next li
End Select
Else 'Если нет вхождения
For li = 1 To lUBnd
If avDateArr(li, 1) Like Критерий And avDateArr(li, 1) Like Критерий2 Then
If Trim(avRezArr(li, 1)) "" Then _
sStr = sStr & IIf(sStr "", Разделитель, "") & avRezArr(li, 1)
End If
Next li
End If
If БезПовторов Then
Dim oDict As Object, sTmpStr
Set oDict = CreateObject("Scripting.Dictionary")
sTmpStr = Split(sStr, Разделитель)
On Error Resume Next
For li = LBound(sTmpStr) To UBound(sTmpStr)
oDict.Add sTmpStr(li), sTmpStr(li)
Next li
sStr = ""
sTmpStr = oDict.keys
For li = LBound(sTmpStr) To UBound(sTmpStr)
sStr = sStr & IIf(sStr "", Разделитель, "") & sTmpStr(li)
Next li
End If
СЦЕПИТЬЕСЛИ = sStr
Написал формулу для сцепления с 2мя условиями( по вашей рекомендации) не могли бы подпраить, что я сделал не так
Да много что не так. Вам помимо добавления второго критерия нужен и доп.диапазон, в котором критерий просматривать. А сейчас у Вас получается, что условия могут быть невыполнимы. Например, строка:
здесь одно и то же значение сравнивается с двумя критериями. Т.е. еслиКритерий=Банан , а Критерий2 = Апельсин - условие никогда не выполнится, т.к. одно и тоже значение не может быть одновременно и Бананом и Апельсином.
Уважаемый Дмитрий большое спасибо за ответ, не могли бы вы по много численным просьбам (заметил в коментариях, что не я один в этом заинтересован) составить функцию с 2мя критериями, чтобы по аналогии можно было писать коды с большим количесвом критериев. Спасибо Вам за проделаный труд.
Спасибо большое!
А как из Вашего примера можно получить перечень сотрудников, у которых оклад находится в диапазоне от 50000 до 10000 ?
Евгений, по сути никак без внесения правок в код. В этом случае необходимо добавлять еще один критерий и обрабатывать его(примерно так:If avDateArr(li, 1) < = Критерий And avDateArr(li, 1) >= Критерий2 Then )