Страницы: 1 2 3 След.
RSS
Универсальная функция Сцепить, Быстрая UDF для сцепки любых данных по 3 критериям
 
Привет, Планетяне!

Я думаю, не только мне, но и многим другим часто требуется сцепить диапазон ячеек в одну.
Стандартная функция Excel крайне неудобна, но можно найти несколько UDF, отличающихся по:
- ресурсоёмкости и рабочему диапазону (ActiveSheet / CurrentRegion / UsedRange)
- количеству критериев для сцепки (от 3 до 0, т.е. просто сцепить)
- ограничениям на тип данных (числа, текст, даты)
- способу указания разделителей, диапазонов, критериев и операторов сравнения (=, >/<, ≥/≤) и др.

По мере необходимости подгружаешь то одну, то другую UDF.
И в итоге сам начинаешь путаться в них, а файл - тормозить при пересчёте.

Может кто-нибудь сможет написать / найти быструю UDF для сцепки любых данных по 3 критериям, которые можно задавать по необходимости.
Примерно такого вида
Код
=СЦЕПКА(Разделитель; Диапазон сцепки;
Optional Диап. усл._1; Оператор_1 & Условие_1;
Optional Диап. усл._2; Оператор_2 & Условие_2;
Optional Диап. усл._3; Оператор_3 & Условие_3)

Очень надеюсь на Вашу помощь и понимание.

Заранее огромное спасибо!

PS: Мои поздравления по поводу перехода на современный движок.
Стало намного лучше!
Изменено: Acid Burn - 24.12.2012 08:59:33
 
Приложил вариант функции Concat(), допускающей до 9 условий в Excel 2003 и до 84 условий в Excel 2007+.

Порядок аргументов функции:
1. Диапазон ячеек (столбец или несколько смежных), которые должны попасть в  результат
2. Разделитель результата
3. Столбец №1 ячеек, участвующих в условии №1
4. Оператор условия №1, может быть одним из: "=", ">=", "<=", "<>"
5. Значение условия №1, с которым сравниваются значения ячеек из п.3
и т.д. следующие условия аналогично п.п. 3,4,5

Все условия работают по И-логике (And)

Пример вызова функции:
=Concat(A1:A6; ","; B1:B6;"=";3; C1:C6;">=";20; D1:D6;"=";"text3")
 
Хорошо, что все переехали  :)  

ZVI, согласитесь, не очень гибко
Цитата
ZVI пишет:
Оператор условия №1, может быть одним из: "=", ">=", "<=", "<>"
Все условия работают по И-логике (And)
В данном случае идеальный вариант (на мой взгляд) передавать функцию, возвращающую степень истинности условия, что то вроде
Код
a > b
Изменено: nerv - 23.12.2012 18:26:55
Чебурашка стал символом олимпийских игр. А чего достиг ты?
Тишина - самый громкий звук


https://github.com/nervgh
 
ZVI, Вы - гений!

Погонял функцию:
- Правильно работает с текстом, числами и датами
- Операторы и разделители можно задавать ссылкой на ячейку
- Время выполнения в массиве 4х1048576 (4х10.000 строк заполнено):
10.98/ 9.11/ 4.98 сек. (Excel 2007/ 2010/ 2013)

Несколько вопросов: можно ли
- добавить в макрос операторы > и < (есть только =, >=, <=, <> ;) ?
- подрезать диапазон строк под UsedRange?
- изменить синтаксис
Код
=Concat(B1:B6;",";  C1:C6;"=";3;  D1:D6;">=";20;  E1:E6;"=";"text3") до вида
=Concat(B1:B6;",";  C1:C6=3;      D1:D6>=20;      E1:E6="text3") ?

Спасибо!
Изменено: Acid Burn - 24.12.2012 09:02:38
 
Цитата
ZVI, согласитесь, не очень гибко
куда правильней (на мой взгляд) передавать функцию, возвращающую степень истинности условия.

Не соглашусь, просто сделайте "куда правильнее" и выложите здесь  ;)
Изменено: ZVI - 23.12.2012 18:28:34
 
Цитата
Несколько вопросов:
1. как добавить в макрос операторы > и < (есть только =, >=, <=, <> ;) ?
2. если "пробить" 1048576 строк, функция прорабатывает их все (или пустые отбрасывает)?
3. можно ли изменить синтаксис
Код
 на =Concat(B1:B6;",";  C1:C6=3;      D1:D6>=20;      E1:E6="text3") 
1. Не понял вопроса, все условия итак считаются по И. Если в строке найдено первое несовпадение условия, то проверка в этой строке прекращается.

2. Пустые ячейки в данных пропускаются, но задавать полностью столбцы, когда много пустых ячеек, будет слишком роскошно. Впрочем, могу добавить подрезку до последней используемой строки листа.

3. Так будет плохо, потому что даты придется задавать текстом, а Excel тогда может произвольно поменять местами месяц и число, если ему так захочется
Изменено: ZVI - 23.12.2012 18:42:22
 
Цитата
ZVI пишет:
Не соглашусь, просто сделайте "куда правильнее" и выложите здесь
можно заe valить немного  :)  Правда в этом случае вряд ли будет
Цитата
Acid Burn пишет:
быструю UDF

Цитата
ZVI пишет:
"куда правильнее"
Я формулировку изменил, если что ) Теперь это можно  ;)
Изменено: nerv - 23.12.2012 18:42:31
Чебурашка стал символом олимпийских игр. А чего достиг ты?
Тишина - самый громкий звук


https://github.com/nervgh
 
Цитата
nerv пишет:
можно за evaluate-ить немного  :)  Правда в этом случае вряд ли будет "быструю UDF"
Александр, да, тогда будет медленнее, как плата за универсальность.
Встроенная функция СУММЕСЛИМН() использует аналогичные моему варианту простые операторы и этого хватает для большинства случаев.
 
To ZVI.

> 1. Не понял вопроса...
Я имел ввиду, что, если заменить C1:C6;"=";3 на C1:C6;">";3, то функция выдаёт #Cond1?
> 2. могу добавить подрезку до последней используемой строки листа.
Было бы здорово. Если при этом она будет правильно работать на всех листах...
> 3. Так будет плохо
Тогда лучше оставить, как есть.

Ещё 2 вопроса:
- можно ли дописать кусок кода, который будет выдавать подсказку при вводе UDF
(так, как это реализовано для "родных" формул)
- как Вам удалось сделать выполнение макроса в 2013 офисе быстрее, чем в 2007
(во всех моих макросах получается наоборот)
Изменено: Acid Burn - 23.12.2012 18:58:12
 
ZVI, как-то так на скорую руку. Правда, не уверен, что будет работать в Office x64

Код
Function Foo(ByVal Valid As String, a, b) As Boolean
    With CreateObject("ScriptControl")
        .Language = "JScript"
        .AddCode "function fn(a,b,c,d,e){return " & Valid & "}"
        Foo = .Run("fn", a, b)
    End With
End Function


Sub Example()
    MsgBox Foo("a > b", 4, 3) ' True
    MsgBox Foo("a > b", 4, 7) ' False
End Sub
Чебурашка стал символом олимпийских игр. А чего достиг ты?
Тишина - самый громкий звук


https://github.com/nervgh
 
Цитата
nerv пишет:
ZVI,  как-то так на скорую руку. Правда, не уверен, что будет работать в Office x64
Александр, медленно все это будет. Можно значительно быстрее.
Но мне-то это зачем?  Здесь же автору темы помогаем, а не мне  :D
 
Цитата
ZVI пишет:
Но мне-то это зачем?
сделайте "куда правильнее" и выложите здесь
т.е. я бы сделал так
Цитата
ZVI пишет:
медленно все это будет
осмелюсь предположить, что не значительно. Функция эвалится один раз. В дальнейшем вызывается с параметрами. Вместо
Код
.Language = "JScript"

можно писать на "родном" vbs.
Изменено: nerv - 23.12.2012 19:46:34
Чебурашка стал символом олимпийских игр. А чего достиг ты?
Тишина - самый громкий звук


https://github.com/nervgh
 
Цитата
Acid Burn пишет:
To ZVI.

> 1. Не понял вопроса...
Я имел ввиду, что, если заменить  C1:C6;"=";3  на  C1:C6;">";3 , то функция выдаёт  #Cond1?
> 2. могу добавить подрезку до последней используемой строки листа.
Было бы здорово. Если при этом она будет правильно работать на всех листах...
> 3. Так будет плохо
Тогда лучше оставить, как есть.

Ещё 2 вопроса:
- можно ли дописать кусок кода, который будет выдавать подсказку при вводе UDF
(так, как это реализовано для "родных" формул)
- как Вам удалось сделать выполнение макроса в 2013 офисе быстрее, чем в 2007
(во всех моих макросах получается наоборот)

По п.1 это я забыл про ">" и "<". Добавил в приложенной версии.
По п.2 - добавил подрезку диапазонов.
По п.3 - оставил из-за дат.

Полноценные подсказки при вводе UDF в ячейку не поддерживаются.
При вводе с помощью мастера функций можно добавить подсказки, но для старых версиях 20003/2007 это делается очень криво. Поэтому просто добавил пример использования, который виден в мастере функций.
Вот встроенную справку для данной функции было бы сделать разумнее, но это требует немало времени, попробуйте сделать сами и подключить через Application.MacroOptions.
 
По поводу того, чтобы в Excel 2013 работало быстрее, специально не старался, просто всю обработку сделал в VBA-массивах. Это стандартный метод ускорения обработки для всех версий.
Еще для операторов заменил сравнение текста сравнением с числами, это тоже выполняется быстрее.
Изменено: ZVI - 23.12.2012 20:02:19
 
Цитата
nerv пишет:
т.е. я бы сделал так
Цитата
ZVI пишет:
медленно все это будет
осмелюсь предположить, что не значительно. Функция эвалится один раз. В дальнейшем вызывается с параметрами. Вместо
Код
 .Language = "JScript" 

можно писать на "родном" vbs.

Александр, хотелось бы в теме автора решать проблемы автора.
У меня есть правило: решая проблему сначала убедиться, проблема ли это вообще.
А автор темы не озвучивал проблему, которые Вы предлагаете решать.

Что тормозит - проверьте, конечно. Я когда-то проверял.
Но тот факт, что и JScript и VBScript могут быть отключены администратором, уменьшают мой интерес к данному решению. Тем более, что есть гораздо более быстрые методы, но они на грани здравого смысла, поэтому о них тоже не распространяюсь  :)
Изменено: ZVI - 23.12.2012 20:47:45
 
Жаль, что нельзя сделать полноценные подсказки при вводе UDF в ячейку...
Но это не критично, ведь синтаксис прост.
Завтра обязательно проверю Concat1 на рабочем файле (сейчас нет под рукой).
Думаю, проблем не возникнет.

To ZVI: огромное спасибо за UDF и советы!
Изменено: Acid Burn - 24.12.2012 09:04:19
 
Цитата
ZVI пишет:
Тем более, что есть гораздо более быстрые методы, но они на грани здравого смысла
А для общего развития можно познакомить?
 
Цитата
Acid Burn пишет:
To ZVI: огромное спасибо за UDF и советы!
Рад был помочь, справку могу в принципе и нарисовать, но тогда придется все это засовывать в надстройку. Задачка была специфическая, но интересная, спасибо и Вам  - за развлечение :)

To RAN:
Цитата
А для общего развития можно познакомить?
Андрей, там же было продолжение (подчеркнуто): "есть гораздо более быстрые методы, но они на грани здравого смысла, поэтому о них тоже не распространяюсь" ;)
Не готов грузить этим сумасшествием.
 
Цитата
ZVI пишет: все это засовывать в надстройку
О подсказках / справке к UDF я поинтересовался для общего развития.
Вы сделали отличную вещь, которая не нуждается в каких-либо комментариях.
Не тратьте на это Ваше драгоценное время.

PS: Проверил новую версию.
Время обработки на том же тестовом файле сократилось до 0,06/ 0,05/ 0,04 сек. (Excel 2007/10/13), т.к. из 1.048.576 строк заполнено лишь 10.000.
Нормально работает (не пересчитывается и не выдаёт ошибку) на не активных листах с другим UsedRange. Это просто фантастика!
Всем рекомендую к применению!


Ещё раз СПАСИБО ZVI и с Наступающим всех!
Изменено: Acid Burn - 23.12.2012 22:01:38
 
Все же приведу код для сравнения скорости вызова простейшей функции в VBScript и VBA.
У меня VBA оказалась быстрее в 21 раз (0.4 секунды) чем VBScript (9.5 секунды)

Код
Option Explicit
Dim objScript As Object

' Сравнение скорости выполнения простейшей функции в VBScript и VBA
Sub Time_Test()
  
  Const N& = 1000000
  Dim i&, t1!, t2!, ok As Boolean
  Debug.Print "N = " & N
  
  t1 = Timer
  For i = 1 To N
    ok = Foo1(i, "Text3")
  Next
  t1 = Timer - t1
  Debug.Print "VBScript", Round(t1, 3) & " sec"
  
  t2 = Timer
  For i = 1 To N
    ok = Foo2(i, "Text3")
  Next
  t2 = Timer - t2
  Debug.Print "VBA", Round(t2, 3) & " sec"
  Debug.Print "VBA is faster in " & Round(t1 / t2, 0) & " times"
  
End Sub

' VBScript function
Function Foo1(a, b)
  If objScript Is Nothing Then
    Set objScript = CreateObject("ScriptControl")
    With objScript
      .Language = "VBScript"
      .AddCode "Function Fn(a,b):Fn = a=1 And b=""text3"":End Function"
    End With
  End If
  Foo1 = objScript.Run("Fn", a, b)
End Function

' VBA function
Function Foo2(a, b)
  Foo2 = a = 1 And b = "text3"
End Function
'
Изменено: ZVI - 23.12.2012 22:12:35
 
2 вопроса:
- как убрать ошибку #ЗНАЧ! при отсутствии критериев сцепления
(когда просто надо сцепить диапазон)?
- как к этой UDF добавить опцию "Без повторов"?
Изменено: Acid Burn - 23.12.2012 22:08:41
 
Цитата
Acid Burn пишет: 2 вопроса...
1. В моем примере работает: =Concat(A1:A6; ","  )  
2. Нужно сначала придумать, как задавать , что требуется именно "уникально".
Наверное, проще сделать еще одну функция с другим именем, например, ConcatUniq(), чтобы не усложнять синтаксис аргументов. Или требуется, чтобы всегда были только уникальные?
Потом еще и сортировка потребуется?  ;)
Изменено: ZVI - 15.06.2013 01:30:45
 
> работает: =Concat(A1:A6; ",")  
Да, но есть ограничение количества строк (где-то ~5652 строк).
Т.е. =Concat(A1:A10000; ",")  уже не сработает.

> или требуется, чтобы всегда были только уникальные?
> Потом еще и сортировка потребуется?
Нет, по ситуации требуется выбрать все или только уникальные.
А сортировку и разбивку по строкам выполняет мой примитивный макрос.
На самом деле здорово бы сделать ConcatUniq() с сортировкой...
Но я уже стесняюсь просить Вас об этом...
Изменено: Acid Burn - 23.12.2012 22:39:33
 
Цитата
Acid Burn пишет:
> работает: =Concat(A1:A6; ","  )  
Да, но есть ограничение количества строк: =Concat(A1:A10000; ","  )  уже не сработает.
Это не ограничение функции, а ограничение Excel - в ячейке не может быть больше 32767 символов. Если в ячейках A1:A10000 значения длиной не более 2-х символов, то все сработает (3 символа на ячейку с учетом одного символа разделителя, итого 30000 символов).
С учетом этого, Вы уверены, что правильно поступаете, накапливая все в одной ячейке?
Что потом с такой ячейкой планируете делать?

Цитата
А сортировку и разбивку по строкам выполняет мой примитивный макрос.
Может, нужно было сразу в одном (нашем) макросе все это и сделать?
Изменено: ZVI - 15.06.2013 01:32:00
 
Цитата
ZVI пишет: это ограничение Excel
Точно, что-то я не подумал... Такая ситуация в реальных условиях вряд ли встретится.
Извиняюсь.

Цитата
Может, нужно было сразу в одном (нашем) макросе все это и сделать?
Если можно будет включать и отключать эти опции, и это не вызовет разрастания кода и трудностей в реализации... То было бы отлично  :oops:
Изменено: Acid Burn - 24.12.2012 09:05:53
 
Цитата
ZVI пишет:
А автор темы не озвучивал проблему, которые Вы предлагаете решать
вообще-то я ничего не предлагаю решать) Я только высказываю свое мнение о том, что у одного автора одни критерии отбора
Цитата
=, >/<, ≥/≤
у другого другие, и общим оптимальным решением (с точки зрения проверки условия истинности) будет вариант предложенный мной. Т.е. вот такое
Код
a * 2 > b - 1
'или такое
a / 2 + a - b <= b - a
'можно даже так (надо только одну строчку в коде дописать, чтобы была возможность работы с приложением)
Target.Offset(1).Value = a + b

в Вашу функцию просто так не запишешь, придется лезть в код.  Т.о. если потребуется n "аналогичных" замысловатых условий, придется писать n вариантов сравнения :)
Цитата
ZVI пишет:
Все же приведу код для сравнения скорости вызова простейшей функции в VBScript и VBA.
ч и т.д. Ибо, как мне кажется, слабым местом (в данном случае) как раз является передача параметров и частый вызов функции. Мой пример.
Цитата
ZVI пишет:
У меня VBA оказалась быстрее в 21 раз (0.4 секунды) чем VBScript (9.5 секунды)
Вместе с тем, вариант реализации через объект ScriptControl, на мой взгляд, будет быстрее варианта Evaluate().
Цитата
ZVI пишет:
Александр, хотелось бы в теме автора решать проблемы автора.
считаю отхождение от темы делом обычным и не подсудным, т.к. эти отхождения, зачастую, гораздо более интересны основной поставленной задачи
Цитата
RAN пишет:
А для общего развития можно познакомить?
я за
Чебурашка стал символом олимпийских игр. А чего достиг ты?
Тишина - самый громкий звук


https://github.com/nervgh
 
Цитата
Цитата
Может, нужно было сразу в одном (нашем) макросе все это и сделать?
Если можно будет включать и отключать эти опции, и это не вызовет разрастания кода и трудностей в реализации... То было бы отлично  :oops:
Мне же неизвестно, в какие ячейки помещать результат.
Раз ситуация с 32787 символами не типичная, то пока просто добавил сортировку и сделал еще одну функцию для уникальных значений CondUnique(), аргументы - те же
 
Цитата
ZVI пишет: просто добавил сортировку и сделал еще одну функцию
Спасибо!

А можно привести синтаксис Concat() к виду:
Код
=Concat(B1:B6;",";  1/0;1/0;  C1:C6;"=";3;  D1:D6;">=";20;  E1:E6;"=";"text3"; ...)
где 1/0 - исключить/оставить повторы; выполнить/пропустить сортировку

Это было бы просто пределом мечтаний!
 
Цитата
Acid Burn пишет:
А можно привести синтаксис Concat() к виду:
Код
 =Concat(B1:B6;",";  1/0;1/0;  C1:C6;"=";3;  D1:D6;">=";20;  E1:E6;"=";"text3"; ...)
где 1/0 - исключить/оставить повторы; выполнить/пропустить сортировку 

Это было бы просто пределом мечтаний!
Сделал. Порядок аргументов функции:
1. Диапазон ячеек (столбец или несколько смежных), которые должны попасть в результат
2. Разделитель результата
3. Признак уникальности: 0/1 = Все/Уникальные
4. Признак сортировки:    0/1 = НеСортировать/Сортировать
5. Столбец №1 ячеек, участвующих в условии №1
6. Оператор условия №1, может быть одним из: "=", ">", "<", ">=", "<=", "<>"
7. Значение условия №1, с которым сравниваются значения ячеек из п.5
и т.д. следующие условия аналогично п.п. 5,6,7

Все условия работают по И-логике (And)

Пример вызова функции:
=Concat(A:A; ","; 1;1; B:B;"=";3; C:C;">=";20; D:D;"=";"text3")
 
Цитата
ZVI пишет:ZVI_Concat3
ZVI, в коде есть небольшая неточность:
Код
Критерий   Должно быть                  По факту
(0, 0)      Все, без сорт.              +
(1, 0)      Уникал., без сорт.          Все, сорт.
(0, 1)      Все, с сорт.                Уникал., без сорт.
(1, 1)      Уникал., с сорт.            +

Т.е. (1, 0) и (0, 1) работают с точностью до наоборот.
Буду благодарен, если подправите (сам пока не понял, как  :oops:  )...
Изменено: Acid Burn - 24.12.2012 09:33:26
Страницы: 1 2 3 След.
Читают тему
Наверх