Моё почтение, джентльмены! Готов инструмент фильтрации массива с любым количеством столбцов, по любым условиям. ArrayFilterV(VARIANT* array_in, VARIANT* array_parameters, VARIANT_BOOL array_out_index, VARIANT* array_out) 1. array_in - массив входящий (одномерный, двухмерный) 2. array_parameters - массив задаваемых параметров (6 параметров для любого столбца, можно для одного и того же, можно для разных). Количество условий не ограничено. Можно создать самому из списка, можно создать двухмерный массив и заполнить, можно забрать сразу с листа Excel. 3. array_out_index - режим вывода: 0- отфильтрованный массив, 1-массив индексов 4. array_out - массив результатов
Условия в массиве параметров применяются в том же порядке, что и везде. Приоритет скобки, потом "И" и "ИЛИ" (одинаковый приоритет, выполняются в порядке следования) Параметры массива условий (сделал максимально просто для пользователя): 1-логические операторы (0-ИЛИ, 1-И) 2-скобка открывающая 3-столбец для фильтрации 4-операторы сравнения (//1-меньше, 2-равно, 4-больше, 8-содержит подстроку, 16-резерв(регулярки), 32- игнорировать регистр, 64-резерв(basic), 128-резерв(extended), 256 - LIKE (пока с символом подстановки "*"), 512-НЕ) 5-значение для фильтра 6-скобка закрывающая,
Условия можно задавать как простые: 'фильтр по первому столбцу, значение = 9 Array(, , 1, Равно, 9, "")
так и более сложные, к примеру выше Array(, "(((", 1, Содержит, "маша", , ИЛИ, , 1, Содержит, "вася", ")", И, , 1, НеРавно, "маша иванова", ")", ИЛИ, "(", 2, НеРавно, "'1", , ИЛИ, , 2, Равно, 1, "))", И, "(", 3, БольшеРавно, 12.5, , И, , 3, МеньшеРавно, 55.8, ")") или в таком виде (как удобнее) Array( _ , "(((", 1, Содержит, "маша", , _ ИЛИ, , 1, Содержит, "вася", ")", _ И, , 1, НеРавно, "маша иванова", ")", _ ИЛИ, "(", 2, НеРавно, "'1", , _ ИЛИ, , 2, Равно, 1, "))", _ И, "(", 3, БольшеРавно, 12.5, , _ И, , 3, МеньшеРавно, 55.8, ")" _ )
ArrayFilterV поиск по 10млн. строк с найденными 5 млн. - 4 сек. В улучшенном варианте (1 этап оптимизации) - 1,76 сек. Оптимизированный вариант: вывод отфильтрованного массива 5 млн строк (изначальный 10 млн) = 0,25 секунд вывод массива индексов 5 млн строк (изначальный 10 млн) = 0,17 секунд
Простой пример (с замером скорости на 10 млн строк) и сложный (с выводом условий и результата):
Код
Option Explicit
'операторы (aliases) псевдонимы
Const ИЛИ = 0, И = 1, РАВНО = 2, СОДЕРЖИТ = 8, НЕРАВНО = 512 + 2, МЕНЬШЕРАВНО = 1 + 2, БОЛЬШЕРАВНО = 4 + 2, МЕНЬШЕ = 1, БОЛЬШЕ = 4
'ОДНОМЕРНЫЙ МАССИВ, ПРОСТЫЕ УСЛОВИЯ
Sub TestArrayFilterV_1()
'Dim bVBA As New BedvitCOM.VBA 'раннее связывание
Dim bVBA As Object: Set bVBA = CreateObject("BedvitCOM.VBA") 'позднее связывание
Dim arrResult
'первоначальный массив данных
Dim arrV: arrV = Array(1, 0, 1, 0, 1, 0)
'массив условий (фильтр по первому столбцу, значение = 1)
Dim p: p = Array(, , 1, РАВНО, 1, "")
' фильтруем ===============================
bVBA.ArrayFilterV arrV, p, 0, arrResult
'========================================
Debug.Print UBound(arrResult) + 1 'начало с 0
End Sub
'ДВУХМЕРНЫЙ МАССИВ, ПРОСТЫЕ УСЛОВИЯ
Sub TestArrayFilterV_2()
'Dim bVBA As New BedvitCOM.VBA 'раннее связывание
Dim bVBA As Object: Set bVBA = CreateObject("BedvitCOM.VBA") 'позднее связывание
Dim arrResult, r, c, t, x
Dim sizeRow As Long: sizeRow = 10000000
Dim sizeCol As Long: sizeCol = 1
Dim arrV: ReDim arrV(1 To sizeRow, 1 To sizeCol) 'первоначальный массив данных, далее хаполняем рендомно
'массив условий (фильтр по первому столбцу, значение = 1)
Dim p: p = Array(, , 1, РАВНО, 1, "")
'заполняем первоначальный массив с данными
For r = 1 To sizeRow
For c = 1 To sizeCol
arrV(r, c) = CLng(Rnd * 2)
Next
Next
t = Timer
' фильтруем ===============================
bVBA.ArrayFilterV arrV, p, 0, arrResult
'========================================
Debug.Print Timer - t
Debug.Print UBound(arrResult) + 1 'начало с 0
End Sub
'ДВУХМЕРНЫЙ МАССИВ, СЛОЖНЫЕ УСЛОВИЯ
Sub TestArrayFilterV_3()
Dim arrParam, arrTest, arrRes, bCOMvba As Object: Set bCOMvba = CreateObject("BedvitCOM.VBA")
Cells.ClearContents
'создаем тестовый массив
arrTest = Array("маша иванова", "'1", 13, "маша иванова", 1, 13, "маша", "'1", 14, "вася", "'1", 14, "паша", 1, 50, "вася", 2, 52, "маша", 1, 60, "вася", 1, 65)
bCOMvba.Array1Dto2D arrTest, 1, 1, UBound(arrTest) / 3: bCOMvba.Transpose arrTest
Cells(1, 1).Resize(UBound(arrTest, 1), UBound(arrTest, 2)) = arrTest
'запись условий для фильтра:(((c1 like "маша" or like "вася") and c1<>"маша иванова") or (c2<>"1" or c2=1)) and (c3>=12,5 and c3<=55,8)
arrParam = Array(, "(((", 1, СОДЕРЖИТ, "маша", , ИЛИ, , 1, СОДЕРЖИТ, "вася", ")", И, , 1, НЕРАВНО, "маша иванова", ")", ИЛИ, "(", 2, НЕРАВНО, "'1", , ИЛИ, , 2, РАВНО, 1, "))", И, "(", 3, БОЛЬШЕРАВНО, 12.5, , И, , 3, МЕНЬШЕРАВНО, 55.8, ")")
'ИЛИ ТАК
arrParam = Array( _
, "(((", 1, СОДЕРЖИТ, "маша", , _
ИЛИ, , 1, СОДЕРЖИТ, "вася", ")", _
И, , 1, НЕРАВНО, "маша иванова", ")", _
ИЛИ, "(", 2, НЕРАВНО, "'1", , _
ИЛИ, , 2, РАВНО, 1, "))", _
И, "(", 3, БОЛЬШЕРАВНО, 12.5, , _
И, , 3, МЕНЬШЕРАВНО, 55.8, ")" _
)
bCOMvba.Array1Dto2D arrParam, 1, 1, UBound(arrParam) / 6: bCOMvba.Transpose arrParam
Cells(1, 5).Resize(UBound(arrParam, 1), UBound(arrParam, 2)) = arrParam
'применяем фильтр
bCOMvba.ArrayFilterV arrTest, arrParam, 0, arrRes
Cells(1, 12).Resize(UBound(arrRes, 1), UBound(arrRes, 2)) = arrRes
End Sub
Добавлены дополнительные условия фильтрации: 1-меньше, 2-равно, 4-больше, 8-содержит подстроку, 128-НЕ (можно смешивать, к примеру 8+128 - НЕ содержит подстроку) Добавлен вывод отфильтрованного массива.
Инструмент готов к использованию! Тестируйте. Пример и файл по ссылке в топике обновил
Список параметров (думаю это будет входящий массив) с условиями фильтрации для каждого столбца и дальнейшая оптимизация (думаю уже сильно не прибавить) будут позже. .
bedvit, впечатляющее время работы Сделал вариант на VBA — хотел бы видеть всё то же, но на порядки быстрее у тебя в библе
Цитата
bedvit: Добавлены дополнительные условия фильтрации: 1-меньше, 2-равно, 4-больше
думаю, что это должна быть отдельная функция, т.к. у неё свой набор параметров и он никак не вяжется с регистром и равно/не равно — общими для всех типов текстовых поисков (у меня).
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
bedvit написал: Тестовую версию библиотеки можно скачать здесь (релиз выложу на сайте )
просто, качаете xll, открываете или добавляете в папку надстроек/подключаете,что бы само открывалось, пишите код vba и получаете результат, все просто:)
bedvit, нужно ещё добавить массив столбцов для фильтрации, массив критериев для каждого и массив параметров. Плюс маска и регулярки
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Ты же не хотел ругулярки?) С масками тоже не все просто. Что такое маска? Какое-то правило обработки строки? Массив критериев - да надо сделать. Это следующее улучшение функционала инструмента.
bedvit, лишними не будут) Маска - это метод Like в VBA. Подобного метода нет в сях? Если нет, то нужен свой аналог - для начала хватит только поддержки *.
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
bedvit, да что ты говоришь) Ну почитай про положение звёздочек, знаки вопроса, символы в квадратных скобках и восклицательный знак первым символом в них...
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Добавил возможность задавать любые условия для любого столбца или для гескольких сразу - задавая массив условий. Приоритет логических операций "и", "или" в порядке следования условий Параметры: '1-логическое or или and (0,1), применяется к последнему результату предыдущего условия. '2-номер столбца к которому применяются условия '3-входящее условие для фильтра или строка для регулярки '4-режим фильтрации: 1-меньше, 2-равно, 4-больше, 8-содержит подстроку, 16-строка содержит нуль терминатор(в след.версии), 32-исользовать регулярки(в след.версии), 64-игнорировать регистр, 128-НЕ '5-грамматика регулярного выражения Регулярные выражения будут добавлены в след. версии. Это сложный и медленный инструмент, пока неясна его нкобходимость, при текущем уровне задавать неограниченный условия для фильтра.
Библиотеку и пример обновил. Задаваемое количество условий не ограниченно.
bedvit, про Or/And непонятно, режим фильтрации можно сделать проще (не заставлять пользователя запоминать волшебные числа) ну или добавить штатное описание/подсказки к процедуре
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
bedvit, прочитал, смотрел, но не выполнял… А что мне даст выполнение в плане понимания? Если без выполнения непонятно, то это уже тревожный звоночек
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Приложи усилия прочитай внимательно, сделай тест. Я сделал максимально просто и понятно. Задаем массив условий (5 параметров для каждого столбца, так же как и в функции ранее было, только не для одного столбца можно задавать, а для разных, вот и все) .Если что останется непонятного, пиши, что конкренно, отвечу подробнее.
Для начала, расшифруй словами, как ты фильтруешь массив из примера. Добавь строки - у тебя одни цифры пока что…
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Относительно просто, т.к. это позволяет задать любые условия одним мезанизмом, одним набором параметров. Стандартный find по диапазону намного лучше? Можно не указывать последний аргумент в нашем случае arrP(1, 1) = 0: arrP(1, 2) = 2: arrP(1, 3) = "маша": arrP(1, 4) = 2 '1е условие arrP(2, 1) = 1: arrP(2, 2) = 4: arrP(2, 3) = "игорь": arrP(2, 4) = 2+128 '2е условие
bedvit, необходимость условия Or сомнительна, зато пользователю сложнее. Плюс, как я понимаю, это условие работает ограниченно
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
bedvit, как будет выглядеть фильтр двухмерного массива, в котором значение в столбце №…: • …1 равно "маша" или равно "вася" • …2 не равно "игорь" или равно "юля" • …3 больше или равно 12,5 и меньше или равно 55,8
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
bedvit, у меня в примере и/или внутри столбца. Так нельзя? 1ый или 3ий - выбираем, что подошло. 2ой дофильтровывает.
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄