Версия для печати

Как сцепить несколько значений в одну ячейку по критерию? СцепитьЕсли

Что умеет Excel

 

Все чаще вижу на разных форумах вопросы типа: Есть таблица, в одном столбце фамилии, в другом оценки(виды работ и т.д.). Как сцепить в одной ячейке для каждой фамилии только принадлежащие ей оценки? Стандартными функциями это сделать весьма проблематично, т.к. заранее неизвестно сколько будет этих оценок. Вот и решил написать небольшую функцию на VBA, которая решает данную проблему.

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

Для применения функции в своем файле достаточно создать стандартный модуль(как это сделать написано здесь) и просто вставить приведенный код. После этого в диспетчере функций появиться новая категория(если до этого её не было)Определенные пользователем. В ней эта функция — СцепитьЕсли.

По принципу работы функция похожа на стандартную СУММЕСЛИ. Указываете диапазон значений, критерий и диапазон значений для сцепления. Символ для разделения слов указывать необязательно.

По умолчанию Разделитель слов — пробел, но можно задать любой другой символ/символы.

Диапазон — диапазон с критериями(указывается один столбец)

Критерий — критерий. Собственно то, с чем сравниваем. Может содержать символы подстановки — * и ?. Просматривается Диапазон. Можно указать символы сравнения с самим критерием сравнения(<>»", <23, >0, <>&A1 и т.п.)

Диапазон_сцепления — из этого диапазона берется значение для сцепления, если значение в аргументе Диапазон совпадает с аргументом Критерий(указывается один столбец)

БезПовторов — если указать 1 или ИСТИНА, то в резульате получится строка, в которой нет одинковых значений. Если указать 0 или ЛОЖЬ, то будут выведены все значения. По умолчанию значение ЛОЖЬ.
Примечание: для работы функции должны быть разрешены макросы.

Скачать пример »

  Tips_Macro_CoupleIf.xls (49,0 KiB, 1 823 скачиваний)

Также см.:
Couple_Cells - быстрое сцепление диапазона ячеек
Что такое функция пользователя(UDF)?



Поддержать автора сайта
Поделиться ссылкой
  1. Юлия
    8 Июль 2011 в 10:27 | #1

    Огромное Спасибо!!!!!!!!!! Функция супер просто!!!!!!!!!!!

  2. Ирина
    24 Октябрь 2011 в 12:16 | #2

    Вы пишете——Для применения функции в своем файле достаточно создать стандартный модуль(как это сделать написано здесь) и просто вставить приведенный код. После этого в диспетчере функций появиться новая категория(если до этого её не было) – Определенные пользователем. В ней эта функция – СцепитьЕсли.——

    А новая функция не появляется в диспетчере функций. И куда именно ставить код, ведь в форме пользователя ее не поставить.Так я поставила в модуль листа. Или я что-то не так сделала.Подскажите. Эта проблема мучает не один день :-)

  3. 24 Октябрь 2011 в 12:52 | #3

    Ирина :

    достаточно создать стандартный модуль(как это сделать написано здесь) и просто вставить приведенный код

    А новая функция не появляется в диспетчере функций. И куда именно ставить код, ведь в форме пользователя ее не поставить.Так я поставила в модуль листа. Или я что-то не так сделала.

    Конечно неверно сделали. Я ведь даже ссылки в статье активные сделал, чтобы можно было на соответствующую статью перейти и почитать — не поленитесь, зайдите и почитайте что такое стандартный модуль и как его создать. И в стандартном модуле запишите данный код. Так же можно скачать пример файла с данной функцией и посмотреть, где там записан код.

  4. Ирина
    25 Октябрь 2011 в 09:37 | #4

    Sorry! Действительно все работает, нужно только внимательно читать. Спасибо, функция работает как часы, даже интересно стало работать на Excelе, а была бы эта функция стандартной, то использовали бы ее, особо не вникая в подробности.

  5. Александр
    14 Декабрь 2011 в 15:27 | #5

    Здравствуйте. А скажите пожалуйста возможно ли вывод 2-х отдельных Диапазонов_сцепления, один из которых будет постоянным, например
    получено от Иванова — ххх руб., от Петрова — ххх руб., и т.д. (ФИО — постоянные составляющие из 1-го диапазона, ххх — меняющиеся значения из 2-го диапазона). Заранее благодарен. Александр.

  6. Валентин
    2 Февраль 2012 в 10:19 | #6

    Здравствуйте, классная формула, а как ее усовершенствовать,чтобы она сцепляла по 2 критериям. Помогите пожалуйста.Оченьнадо. И данные могла брать с разныъх листов

  7. 2 Февраль 2012 в 14:05 | #7

    Для добавления второго условия дописать еще один критерий к аргументам функции:

    ByVal Критерий2 As String
    ByVal Критерий2 As String

    и в коде у каждого условия дописать сравнение с ним:

    If avDateArr(li, 1) = Критерий And avDateArr(li, 1) = Критерий2 Then
    If avDateArr(li, 1) = Критерий And avDateArr(li, 1) = Критерий2 Then
  8. Николай
    24 Апрель 2012 в 09:39 | #8

    Такой вопрос, функция круто работает, но можно ли найденные значения сразу суммировать?

  9. 24 Апрель 2012 в 16:16 | #9

    Николай — нет. Это же функция для текста. Для суммирования как раз СУММЕСЛИ.

  10. Елена
    27 Апрель 2012 в 19:19 | #10

    Здравствуйте, помогоите сцепить ячейку с датой и текстовую ячейку. У меня вместо даты в сцепленном варианте получается 5-ти значное число

  11. 27 Апрель 2012 в 21:38 | #11

    потому что дата для Excel — это число. Подробнее написано в этой статье. С помощью приведенной выше функции не получится так сделать. А если без данной функции, то при помощи функции ТЕКСТ:
    =ТЕКСТ(A1;"ДД.ММ.ГГГГ")

Комментарий будет добавлен после проверки администратором.
Комментарии, не имеющие отношения к комментируемой статье, будут удаляться без уведомления и объяснения причин. Если есть вопрос по проблеме в Excel- добро пожаловаться на Форум