Склеивание текста по условию
Про то, как можно быстро склеивать текст из нескольких ячеек в одну и, наоборот, разбирать длинную текстовую строку на составляющие я уже писал. Теперь же давайте рассмотрим близкую, но чуть более сложную задачу - как склеивать текст из нескольких ячеек при выполнении определенного заданного условия.
Допустим, что у нас имеется база данных по клиентам, где одному названию компании может соответствовать несколько разных email'ов ее сотрудников. Наша задача состоит в том, чтобы собрать все адреса по названиям компаний и сцепить их (через запятую или точку с запятой), чтобы сделать потом, например, почтовую рассылку по клиентам, т.е. получить на выходе что-то похожее на:
Другими словами, нам нужен инструмент, который будет склеивать (сцеплять) текст по условию - аналог функции СУММЕСЛИ (SUMIF), но для текста.
Способ 0. Формулой
Не очень изящный, зато самый простой способ. Можно написать несложную формулу, которая будет проверять отличается ли компания в очередной строке от предыдущей. Если не отличается, то приклеиваем через запятую очередной адрес. Если отличается, то "сбрасываем" накопленное, начиная заново:
Минусы такого подхода очевидны: из всех ячеек полученного дополнительного столбца нам нужны только последние по каждой компании (желтые). Если список большой, то чтобы их быстро отобрать придется добавить еще один столбец, использующий функцию ДЛСТР (LEN), проверяющий длину накопленных строк:
Теперь можно отфильтровать единички и скопировать нужные склейки адресов для дальнейшего использования.
Способ 1. Макрофункция склейки по одному условию
Если исходный список не отсортирован по компаниям, то приведенная выше простая формула не работает, но можно легко выкрутиться с помощью небольшой пользовательской функции на VBA. Откройте редактор Visual Basic нажатием на сочетание клавиш Alt+F11 или с помощью кнопки Visual Basic на вкладке Разработчик (Developer). В открывшемся окне вставьте новый пустой модуль через меню Insert - Module и скопируйте туда текст нашей функции:
Function MergeIf(TextRange As Range, SearchRange As Range, Condition As String) Dim Delimeter As String, i As Long Delimeter = ", " 'символы-разделители (можно заменить на пробел или ; и т.д.) 'если диапазоны проверки и склеивания не равны друг другу - выходим с ошибкой If SearchRange.Count <> TextRange.Count Then MergeIf = CVErr(xlErrRef) Exit Function End If 'проходим по все ячейкам, проверяем условие и собираем текст в переменную OutText For i = 1 To SearchRange.Cells.Count If SearchRange.Cells(i) Like Condition Then OutText = OutText & TextRange.Cells(i) & Delimeter Next i 'выводим результаты без последнего разделителя MergeIf = Left(OutText, Len(OutText) - Len(Delimeter)) End Function
Если теперь вернуться в Microsoft Excel, то в списке функций (кнопка fx в строке формул или вкладка Формулы - Вставить функцию) можно будет найти нашу функцию MergeIf в категории Определенные пользователем (User Defined). Аргументы у функции следующие:
Способ 2. Сцепить текст по неточному условию
Если заменить в 13-й строчке нашего макроса первый знак = на оператор приблизительного совпадения Like, то можно будет осуществлять склейку по неточному совпадению исходных данных с критерием отбора. Например, если название компании может быть записано в разных вариантах, то мы можем одной функцией проверить и собрать их все:
Поддерживаются стандартные спецсимволы подстановки:
- звездочка (*) - обозначает любое количество любых символов (в т.ч. и их отсутствие)
- вопросительный знак (?) - обозначает один любой символ
- решетка (#) - обозначает одну любую цифру (0-9)
По умолчанию оператор Like регистрочувствительный, т.е. понимает, например, "Орион" и "оРиОн" как разные компании. Чтобы не учитывать регистр можно добавить в самое начало модуля в редакторе Visual Basic строчку Option Compare Text, которая переключит Like в режим, когда он невосприимчив к регистру.
Таким образом можно составлять весьма сложные маски для проверки условий, например:
- ?1##??777RUS - выборка по всем автомобильным номерам 777 региона, начинающимся с 1
- ООО* - все компании, название которых начинается на ООО
- ##7## - все товары с пятизначным цифровым кодом, где третья цифра 7
- ????? - все названия из пяти букв и т.д.
Способ 3. Макрофункция склейки текста по двум условиям
В работе может встретиться задача, когда сцеплять текст нужно больше, чем по одному условию. Например представим, что в нашей предыдущей таблице добавился еще один столбец с городом и склеивание нужно проводить не только для заданной компании, но еще и для заданного города. В этом случае нашу функцию придется немного модернизировать, добавив к ней проверку еще одного диапазона:
Function MergeIfs(TextRange As Range, SearchRange1 As Range, Condition1 As String, SearchRange2 As Range, Condition2 As String) Dim Delimeter As String, i As Long Delimeter = ", " 'символы-разделители (можно заменить на пробел или ; и т.д.) 'если диапазоны проверки и склеивания не равны друг другу - выходим с ошибкой If SearchRange1.Count <> TextRange.Count Or SearchRange2.Count <> TextRange.Count Then MergeIfs = CVErr(xlErrRef) Exit Function End If 'проходим по все ячейкам, проверяем все условия и собираем текст в переменную OutText For i = 1 To SearchRange1.Cells.Count If SearchRange1.Cells(i) = Condition1 And SearchRange2.Cells(i) = Condition2 Then OutText = OutText & TextRange.Cells(i) & Delimeter End If Next i 'выводим результаты без последнего разделителя MergeIfs = Left(OutText, Len(OutText) - Len(Delimeter)) End Function
Применяться она будет совершенно аналогично - только аргументов теперь нужно указывать больше:
Способ 4. Группировка и склейка в Power Query
Решить проблему можно и без программирования на VBA, если использовать бесплатную надстройку Power Query. Для Excel 2010-2013 ее можно скачать здесь, а в Excel 2016 она уже встроена по умолчанию. Последовательность действий будет следующей:
Power Query не умеет работать с обычными таблицами, поэтому первым шагом превратим нашу таблицу в "умную". Для этого ее нужно выделить и нажать сочетание Ctrl+T или выбрать на вкладке Главная - Форматировать как таблицу (Home - Format as Table). На появившейся затем вкладке Конструктор (Design) можно задать имя таблицы (я оставил стандартное Таблица1):
Теперь загрузим нашу таблицу в надстройку Power Query. Для этого на вкладке Данные (если у вас Excel 2016) или на вкладке Power Query (если у вас Excel 2010-2013) жмем Из таблицы (Data - From Table):
В открывшемся окне редактора запросов выделяем щелчком по заголовку столбец Компания и сверху жмем кнопку Группировать (Group By). Вводим имя нового столбца и тип операции в группировке - Все строки (All Rows):
Жмем ОК и получаем для каждой компании мини-таблицу сгруппированных значений. Содержимое таблиц хорошо видно, если щелкать левой кнопкой мыши в белый фон ячеек (не в текст!) в получившемся столбце:
Теперь добавим еще один столбец, где с помощью функции склеим через запятую содержимое столбцов Адрес в каждой из мини-таблиц. Для этого на вкладке Добавить столбец жмем Пользовательский столбец (Add column - Custom column) и в появившемся окне вводим имя нового столбца и формулу сцепки на встроенном в Power Query языке М:
Обратите внимание, что все М-функции регистрочувствительные (в отличие от Excel). После нажатия на ОК получаем новый столбец со склееными адресами:
Осталось удалить ненужный уже столбец ТаблАдресов (правой кнопкой мыши по заголовку - Удалить столбец) и выгрузить результаты на лист, нажав на вкладке Главная - Закрыть и загрузить (Home - Close and load):
Важный нюанс: в отличие от предыдущих способов (функций), таблицы из Power Query не обновляются автоматически. Если в будущем произойдут какие-либо изменения в исходных данных, то нужно будет щелкнуть правой кнопкой в любое место таблицы результатов и выбрать команду Обновить (Refresh).
Не понятно, зачем MergeIf = CVErr(xlErrRef)?
Функция MergeIf определена как строковая, она не может вернуть значение ошибки #ССЫЛКА!, результатом данной строки будет ошибка #ЗНАЧ!, т.к. присваивается недопустимое значение строковой переменной и до строки Exit Function дело даже не доходит.
Если тип функции был Variant, тогда другое дело.
И еще, Николай, почему Вы не объявляете все переменные, в данном примере не объявлена переменная OutText, что не дает возможности использовать Option Explicit
Объявление не всех переменных наблюдается и в PLEX.
Единственный вопрос: а как заставить формулу сцепить только уникальные значения?
Например, у меня есть сводная таблица:
Фирма1Город1 Продавец1
Фирма1Город1 Продавец2
Фирма1Город1 Продавец3
Фирма1Город2 Продавец4
Фирма1Город2 Продавец3
Фирма1Город2 Продавец2
Фирма1Город3 Продавец1
Фирма1Город3 Продавец4
Фирма2 ...
Фирма3 ...
...
Я по маске хочу вытащить всех продавцов для Фирмы1 во всех городах (пишу *фирма1*) и оно мне выдает не 4 уникальных продавца, а все 8 записей, (предсказуемо) задваивая некоторых продавцов.
Мне кажется, как-то можно дописать условие проверки на повторы, но мои познания в VBA не столь глубоки
Заранее спасибо!
Решилось (немного коряво) формированием пайвота и перетягиванием его каждый раз так так, чтобы для каждой отдельной переменной были уникальные сочетания..
Но ряд значений действительно повторяются, существует ли какая-то альтернатива?
сделал как все написано, но выдает ошибку #ЗНАЧ -связано ли это с тем что в первом столбце не текст а номера (числовые значения)? Вроде все столбцы перевел в "текстовый" формат -все равно не получается запустить.
Заранее спасибо!
А какую надо строчку добавить в макрос по нескольким условиям, чтобы пропускало пустые вместо отображения #ЗНАЧ!
Есть таблица:
Как это сделать?
пожалуйста, помогите с решением такой задачи: склеить текст в ячейках до ячейки содержащей "MF". По сути, необходимо чтобы текст, стоящий в ячейках До ячейки с MF отображался как единое целое, а ячейка с MF стояла отдельно.
Спасибо!
Спасибо за ваши примеры!
Рассматриваю ваш вариант №1. Работает отлично, но если необходимо проработать объемную базу, то для каждого значения необходимо в ручную указывать условие. Это не совсем удобно.
Если же использовать эту формулу для первого значения, а затем распространить на все значения, то получается такая картина как у меня на скрине. Тоесть формула прописывает возле каждого дубля количество электронных адресов минус один эл адрес..
Возможно ли в формуле использовать какое то решение VBA по поиску дублей? При этом если распространить данную формулу по всему массиву, то электронные адреса бы склеивались у одного из дублей, но у последующих значений, которые дублируются сцепка не производилась бы.
Динамические не меняются, если "растянуть" формулу.
Т.е. в Вашем случае в ячейке D2 пишем =MergeIf($B$2:$B$9;$A$2:$A$9;C2).
Потом растянуть формулу на весь столбец и будет меняться только последнее значение C2, C3 и т.д
Версия лучше тем, что есть возможность сцеплять значения без их повторения (уникальные). Браво! 8)
подскажите пожалуйста, как следует изменить макрос, чтобы он вписывал в ячейку данные таким образом:
1 2 3 4 5
1 а б в д а,e;б,ё;в,ж
2 е ё ж з;и;й
3 з и й к л;м;н
4 л м н о
т.е грубо говоря, поочерёдно вписывала все значения ячеек данного столбца через запятую, после данные из следующих столбцов (между столбцами разделитель точка с запятой) до тех пор пока не будет выведено не пустое значение в контрольном столбце, а после проделала бы то же самое на следующей строчке. гарантируется, что первое и последнее значение контрольного столбца не пустые, но значения столбцов из которых нужно брать данные могут оказаться пустыми, в этом случае не нужно выводить несколько разделителей. да, и ещё, желательно, чтобы перед значениями указывалось бы название столбца (т.е в моей таблице -это значение первой ячейки), тогда первая строка исходных значений в приведённой мной таблице для примера будет иметь вид: название ячейки 1:а,е;название ячейки 2:б,ё;название ячейки 3:в,ж (между названиями столбцов и данными ячеек пусть будет разделитель двоеточие, а в случае пустых значений (во всех ячейках) -не указывалось бы название вообще). Заранее спасибо за ответ.
Ваше решение особенно по не точным условиям считаю лучшим ,как по простоте реализации так и по функциональности. Большое спасибо.
Пытаюсь адаптировать под мою задачу.
Подскажите, пожалуйста,
как в качестве разделителя использовать:
вначале нумерацию, затем значение подобранное по условию, затем точку с запятой,
при этом что бы начать с нвой строки я так понял втавить (Delimeter = Chr(10)).
С уважением, Алексей.
у меня есть 2 поля(INTEGER)
INN PhoneNumber
344565667678 80976338834
Мне нужно склеить так:
(344565667678, 80976338834)
желательно макрос, можно и функцию.
Заранее благодарна
Требуется сцепить даты с одного периода до другого периода включительно. как это реализовать?
По данной формуле оно вытягивает вообще все даты которые есть в реестре.
т.е не через запятой или точка-запятой, а именно чтобы находились друг под другом
Ошибка выходит если я пишу просто NewLine
Text.Combine([дата][Организация],NewLine)
я пишу дату С использованием дроби,например, 01/12/2017 а excel автоматически меняет формат и ставить точку вместо дроби что нужно делать чтобы это не произошло
Расшифровка
1. У меня таблица оформлена как элемент таблица, поэтому, вы не увидите стандартных адресов;
2. Конструкции "Подставить()" фильтрует образовавшиеся нули там, где объединения было пустое, почему то параметр "1" в формуле "объединить" в массиве не сработал
3. Адреса в формуле "объединить()" заданы областями - без наложения массива по "Ctrl+Shift+Enter" конструкция работать не будет и вы получите ошибку.
4. Формула работает так: если в строке в ячейке есть "х", то берётся значение из шапки и так с перебором всех столбцов, после чего включается фильтр "Подставить" и выпалывает значения "0, " и ", 0".
И не надо надстроек и макросов. Только наложить чары в виде массива "Ctrl+Shift+Enter".
P.S А как изменить имя функции?
Николай добавил в Вашу функцию параметр "Сумма" теперь если ввести в конце "1" она будет складывать как "СуммаЕсли", а если "2" то перемножит все найденные значения, исправления конечно бесполезные, но прикольно. И ещё написал на русском название параметров функции, просто мне кажется так удобнее
"Текст для сшивания "
Столбец 1 Столбец 2 Столбец 3
Строка 1
Строка 2
Строка 3
Лист 1 Лист 2
На листе 1 хочу получить сшитые данные, которые находятся на листе 2.
Хочу объединить Столбы 1 - 3, но при этом Строка 2 (таких строк конечно может быть много) мне не нужна. Я ее скрываю фильтром, но если я использую формулу =ПРОМЕЖУТОЧНЫЕ.ИТОГИ или =АГРЕГАТ, а потом растягиваю вниз, то происходит сшивание данных и скрытых строк тоже. Как этого избежать?
Таблица а должно быть так
Продавец1 7/25/2019 Продавец1 25.07.2019; 03.08.2019; 03.08.2019 7/25/2019
Продавец2 7/26/2019 Продавец2 7/26/2019 7/26/2019
Продавец3 7/27/2019
Продавец4 7/28/2019
Продавец5 7/29/2019
Продавец6 7/30/2019
Продавец1 7/31/2019
Продавец8 8/1/2019
Продавец9 8/2/2019
Продавец1 8/3/2019
Продавец11 8/4/2019
Продавец5 8/5/2019
Без "-", через сводную и две вспомогательные таблицы результат был получен, но хотелось бы упростить работу и решить вопрос со знаком "-".
количество строк 3000
При автоматических вычислениях сильно подвешивает файл excel и не дождаться окончательного пересчета формул в чем может быть причина ?
Как ускорить этот процесс ?
Сейчас приходится руками каждую ячейку в которой есть изменения пересчитывать в ручном режиме.
Подскажите, как сделать склейку по 3 условиям.