There is no such thing as a stupid question, until you ask it
Дано: запись типа record с несколькими полями, сохраняемая в файле, StringGrid.
Суть задачи: пользуясь полями ввода, с которыми работает пользователь, организовать поиск по записи с несколькими параметрами; подходящие элементы вывести в сетку.
читать дальше

@темы: Delphi

Комментарии
27.03.2014 в 15:26

After silence that which comes nearest to expressing the inexpressible is music.
По большому счёту, если фильтров не много - то ифы это нормальный вариант.
Можно это всё обернуть конечно в паттерны стратегия и комманда, но всё равно суть одна и та же.

чтобы пользователь "включал" нужные параметры для поиска.
Это можно просто решить проверкой на заполненность фильтра. Т.е., если что-то выбрано или заполнено в фильтре - значит он включён.

Я не дельфи программер, но алгоритм бы сделал таким :

если включен фильтр 1, то выбираю записи по нему.
если включен фильтр 2, то выбираю записи, выбранные перед этим, согласно фильтру 2
если ... и так далее

т.е. пройдя через все фильтры - останутся только те строки, которые подходят им всем. их и выводить
27.03.2014 в 15:33

There is no such thing as a stupid question, until you ask it
выбираю записи, выбранные перед этим
Хм... Отличный вариант, но один вопрос:
останутся только те строки
останутся где? Нужно же как-то запоминать подходящие строки, а поскольку в записи нет индексов (что отстой полный, кстати), но тут надо или организовать вторую запись, или... использовать указатели? (что было бы очень хитро и крайне желательно).
Скажем, динамический массив указателей, которые бы хранили адреса нужных записей. В процессе фильтрации ненужные обнулялись бы.
27.03.2014 в 15:46

Птичка шизокрылая, Борец С Режимом Дня
1. Файл в процессе работы программы меняет или гарантированно без изменений? Если без изменеий, но считать в массив/список (TList) и юзать по мере использования.
2. Для работы вот прям обязательно использовать эти читалки? TFileStream - более современная и удобная штука
Тогда i-й элемент будете определять как
GetElement(n)
begin
fs.position:=n*sizeof(TSomeRecord);
fs.read(...);
end;


Далее, что до поиска. Вы хотите как, искать, по подстроке во всех полях? TChecklistbox вам в помощь. с условием в духе
for lines:=0 to count-1 do
begin
for i:= 0 to fieldCOunt-1
if fieldCheck[i].checked then
condition:=*проверяе подстроку*
end

Проверки, ессно, не по содержимому TStringList, а по записям из файла, это намного шустрее и корректнее

И вынести условие проверки в отдельную функцию, будет выглядеть не такой жути
27.03.2014 в 16:04

There is no such thing as a stupid question, until you ask it
Neitent, гарантированно без изменений?
Не, его на поиске куцать нельзя, в приложении юзер может в любой момент дополнить его новыми строками.
TFileStream
Впервые слышу, но то, что уже прочитала про него, мне нравится.
fieldCheck[i].checked
Что это и почему у него есть индекс и состояние? ._. Разве можно так обраща--
О. ООООО.
Вы подразумеваете, что имена чекбоксов будут различаться только цифрами? ЭТО ГЕНИАЛЬНО. У меня-то они для понятности имеют индивидуальные имена, и мне и в голову не пришло сделать из них цикл.
Но разве так можно?..

ессно, не по содержимому TStringList, а по записям из файла
Конечно, кому вообще это в голову может придти? Оо
27.03.2014 в 16:13

Птичка шизокрылая, Борец С Режимом Дня
GippoRex,
Не, его на поиске куцать нельзя, в приложении юзер может в любой момент дополнить его новыми строками.
тогда без вариантов, конечно.
Вы подразумеваете, что имена чекбоксов будут различаться только цифрами?
это отдельный компонент, панель Additional, TCheckListBox. Кайф как раз для однородных/однотипных чекбоксов-списков
Для обычных тожно можно, есть штука GetTypedControls, есть поиск по контролам на форме. проход по всем контролам и тыды, но это явно не для этой задачи

Конечно, кому вообще это в голову может придти? Оо
таки может:nope: Люди страааанные бывают
27.03.2014 в 16:33

There is no such thing as a stupid question, until you ask it
Neitent, если честно, немного непонятно, как с этой штуковиной управится.
Сейчас у меня интерфейс типо поиска выглядит так:
читать дальше
Как в случае с TCheckListBox установить связь между конкретным ComboBox и связанным с ним CheckBox? Ведь в ЧекЛистБоксе у меня будут не конкретные значения, а их группы. Я так понимаю, аналогичного TCheckListBox'y класса для ComboBox нет Х)
27.03.2014 в 16:58

Птичка шизокрылая, Борец С Режимом Дня
Тогда напрашивается массив контролов, создаваемый в рантайме.
Как-то так

Ну или если 4 условия - ручками, ручками. Если их больше или потенциально может стать больше - то тогда да, имеет смысл дергаться

Далее, галочки только утежеляют интерфейс. Просто комбики, текст есть - фильтр включен, нет - выключен.
27.03.2014 в 17:03

After silence that which comes nearest to expressing the inexpressible is music.
GippoRex, уберите чекбоксы, зачем они вам? У вас заполненность = присутствие там данных. Это достаточно чтобы учитывать этот комбобокс как фильтр.

вот примерный код на C# :


27.03.2014 в 17:06

After silence that which comes nearest to expressing the inexpressible is music.
Тогда напрашивается массив контролов, создаваемый в рантайме.
Можно гибридный вариант.

Combobox обозвать combo_0, combo_1 и тд
а все чекбоксы check_0, check_1 и тд.

при клике на чекбоксе - забираем его имя, достаём всё что после "_" - парсим в integer, делаем combo_ +"5" или что там у нас в integer - у нас готово имя комбика.

Дальше итерируемся по всей форме в поисках комбобокса с таким именем и забираем его текст для фильтра.

но это дико через жопу.
27.03.2014 в 17:09

There is no such thing as a stupid question, until you ask it
Neitent, массив контролов, создаваемый в рантайме
Это таки круто! Жаль, что у меня всего 4 условия, и такая конструкция для них чуть ли не более громоздкая, чем прописать для каждого индивидуально.
ручками, ручками
Но это так тривильано и... какой-то индусский код XD

текст есть - фильтр включен, нет - выключен.
А если юзер, скажем, забыл полностью стереть текст в форме или случайно вбил пробел? С галками как-то надежнее.

И все равно я не до конца понимаю, как организовать этот чертов поиск.
Нужно же как-то запоминать подходящие строки, а поскольку в записи нет индексов (что отстой полный, кстати), но тут надо или организовать вторую запись, или... использовать указатели? (что было бы очень хитро и крайне желательно). Скажем, динамический массив указателей, которые бы хранили адреса нужных записей. В процессе фильтрации ненужные обнулялись бы. Или это вообще моя грибная теория?
27.03.2014 в 17:21

Птичка шизокрылая, Борец С Режимом Дня
GippoRex,
тык пробел не учитывать.
trim(s)<>''
Забыл стереть - сам дурак. Точно так же может галку забыть

поскольку в записи нет индексов
Я выше написала пример функции GetElement (n)
Как с этим работать:
-узнать количество элементов:
fs.size div sizeof(record)
- Потом любой элемент по этому GetElement вытаскивается - вуаля! В какой-то степени похоже на работу с массивом


А вообще-то это задача для БД в чистом виде:nope:

Но это так тривильано и... какой-то индусский код XD
нет, почему же. Для маленькой задачки - вай нот? Так-то можно было бы нагородить таблицы контролов, вынести это в особый компонент, создание в рантайме, загрузка списка полей из бд - что-то вроде вида на я.маркете, но усилия для этого случая... кхем.
Индийская оптимизация - оптимизация того, чтобы выполняется разово и наносекунды, но потребует таааааааких трудозатрат

27.03.2014 в 17:37

There is no such thing as a stupid question, until you ask it
Kakou ECTb, вот примерный код на C#
То есть, вы хотите сделать это через вторую запись?
Алсо, почему у вас только один флаг? Разве двойное равно не используется только при сравнивании?
Если честно, я не до конца понимаю, что у вас творится в теле if. Там флагу присваивается тру/фолс в зависимости от того, соответствует ли поле записи тексту, введенному в комбобокс? Но разве, раз флаг один, не будет ли он равен лишь последнему значению, и тогда не будет ли проверка осуществляться лишь по одному критерию (а то и вообще ни по какому)?

Neitent, Я выше написала
Да, я заметила ответ лишь после отправки комментария, простите.
функции GetElement (n)
Я пока еще не успела вкурить этот ФайлСтрим до конца :nope:

А вообще-то это задача для БД в чистом виде
Ам... Это значит, что я что-то упускаю из виду в своем подходе?

загрузка списка полей из бд
Но у меня и так что-то такое будет. Список значений комбобокса будет черпаться из файла, и если встретит новый... Но это так геморрно звучит, надо будет заводить отдельный файл, куда бы сохранялись Item для комбика, ведь только так можно будет провести анализ, встречается ли такое значение там ранее... Я уже подумываю над тем, чтобы поменять комбобоксы на простой TEdit ._.
27.03.2014 в 17:41

After silence that which comes nearest to expressing the inexpressible is music.
и тогда не будет ли проверка осуществляться лишь по одному критерию? Зачем проверять все остальные, если достаточно, чтобы провалился хотя бы один? Фильтры же это логическое И, а не ИЛИ, если я правильно понял.
Разве двойное равно не используется только при сравнивании? Да, и в чём проблема?
То есть, вы хотите сделать это через вторую запись? немного не понял про какую запись речь.
27.03.2014 в 17:41

There is no such thing as a stupid question, until you ask it
надо будет заводить отдельный файл
И тут я поняла, что придется их тогда заводить не один, а целых 4: для каждого комбика. Что-то вообще перспективы нерадужные. Или... можно это сделать через указатели?..
27.03.2014 в 17:46

There is no such thing as a stupid question, until you ask it
Kakou ECTb, не понял про какую запись речь.
А, меня просто переклинило, извините, уже разобралась.
достаточно, чтобы провалился хотя бы один?
Но тогда ваш способ статичен и подходит только для случая, где поиск осуществляется только по всем четырем---
Аааа! Там же условия на непустоту комбобокса! Слушайте, а какой красивый способ получился-то. Спасибо вам огромное!
27.03.2014 в 17:51

After silence that which comes nearest to expressing the inexpressible is music.
GippoRex, Пожалуйста :cheek:
27.03.2014 в 17:59

Птичка шизокрылая, Борец С Режимом Дня
GippoRex,
по поводу файлстрима, очень в двух словах: он читает/пишет в файле с опред. позиции n байт.
Соответственное, поиск i-го элемента по файлу сводится к тому, что зная его номер и умножив его на размер записи, мы можем узнать, с какой позиции в фале записан элемент. Ну и считываем этот сам элемент, считав байт по размеру записи.

Кстати, ВАЖНО! Используйте не Record, а packed record, если у вас дельфи повыше, чем 7. Или/И в настройках компилятора выключите record field alignment, иначе результаты могут не совпадать с мечтаниями.

Теперь по поводу БД: конечно, просто файл - это быстрее, проще, бла-бла. Но! Как только у вас в один файл начинает писать несколько человек - это уже геморрой. Даже с учетом того, что файлстрим позволяет контролировать письмо-чтение в файл для шары, вы запросто можете потерять данные.
Проблемы безопасности, опять же - даже простейшие бд позволяют хоть как-то защититься.
Да и запросы такие в sql логичнее выглядят.
Все зависит от масштабов и потенциалов роста.

Или... можно это сделать через указатели?..
ээээ.... Что за ЭТО? Указатели - не панацея, а инструмент, который может добавить в жизнь много веселых моментов при отладке. В голом виде вообще-то говоря, сейчас редко нужная *не то, чтобы это избавляло от не обходимости их понимать*
Вы хотите хранить список набранных когда-то значений? 4 файла выглядят противоестественно
файл с рекордами вида вроде
TPrevValues = record
fieldIndex: byte;
value: string;
end;
Или банальный эдит, да.
*или тоже в бд
27.03.2014 в 18:14

There is no such thing as a stupid question, until you ask it
Neitent, в sql
Увы-увы, задачу надо решать без SQL, даже если так получается более топорно. Может, позже мне дадут задание именно на это... может.
27.03.2014 в 20:08

There is no such thing as a stupid question, until you ask it
список набранных когда-то значений?
Проблема в том, что они могут повторяться, и рассовывать их надо по 4-м комбобоксам.
В голом виде вообще-то говоря, сейчас редко нужная
Поверю, просто после C++ у меня развилась паранойя "что это? получается слишком сложно? значит, я что-то делаю не так, и нужны указатели!"
28.03.2014 в 09:01

Птичка шизокрылая, Борец С Режимом Дня
GippoRex,
Проблема в том, что они могут повторяться, и рассовывать их надо по 4-м комбобоксам.
поэтому я говорю - набор рекордов id-значение. По id толпа значений распихивается по комбикам хотя я бы сначала сделала просто с эдитами - достаточно, чтобы работало хотя бы так, а подгрузку значений в комбик тестила вообще на отдельном проекте
В рантайме они хранятся в, скажем, 4 TStringlist-ах *если не знаю - очень рекомендую. Гораздо корректнее и удобнее массива строк

Поверю, просто после C++ у меня развилась паранойя "что это? получается слишком сложно? значит, я что-то делаю не так, и нужны указатели!"
о.о Это сейчас, мягко говоря, не так. Кроме с/с++ не так уж и много языков любят указатели. Ссылочные типы, разумеется, используются, куда уж без них. Но именно операции с голыми указателями в большинстве клиентских программ редкость, которая скорее усложнит жизнь, чем упростит. Почти везде есть готовые классы для работы практически со всем, что вообще может в жизни понадобиться. И использовать их и их наследников правильно: и меньше шанса на ошибку, и периферия прописана, и спроектированы они с учетом всех радостей языка. И все указатели там зарыты глубо от шаловливых ручек пользователей класса.
За все языки не скажу, но вот в дельфях, си#, перле для того, чтобы использовать указатель, должна быть прямо-таки Причина. И даже без нее часто можно обойтись.
28.03.2014 в 15:20

There is no such thing as a stupid question, until you ask it
Neitent, должна быть прямо-таки Причина
Нас просто пока штудируют на механику работы указателей, вот и повернулась чутка на этом :laugh:

должна быть прямо-таки Причина
М, например? Если не брать С++ с его имитацией структурных типов. Ну и помимо списков/стеков/очередей.

А вот поиск, реализованный по алгоритму Kakou ECTb, у меня все же работает некорректно. Я, конечно, еще посижу над ним, но если ничего так и не получится, то отпишусь :с
28.03.2014 в 15:44

Птичка шизокрылая, Борец С Режимом Дня
М, например?
Как раз списки-стеки-очереди в том же дельфи,шарпе, да вообще везде, наверное, уже реализованы в стандартных библиотеках. бери и пользуйся, ну или бери, наследуй и пользуйся.:chup2:
Но штудировать их надо, хоть понимаешь, что откуда растет.
По поводу Причины: придумались или какая-то очень масштабная оптимизация, или портирование сишного кода, когда проще так, чем разбираться, что происходит. Больше и придумать-то не могу:hmm:
*вы пока по хотя бы одному фильтру сделайте, все проще.
28.03.2014 в 16:54

There is no such thing as a stupid question, until you ask it
Neitent, *вы пока по хотя бы одному фильтру сделайте, все проще.
Я так и делаю, когда пытаюсь найти ошибку.

В данном случае я обнаружила, что условие flag=true ломает поиск. Бред какой-то.
Моя процедурка поиска:


Так вот. Даже если поле заполнить 100% верно, ничего не выводится. Соответственно, если убрать упоминание про флаг, все работает норм. Почему? Ведь я еще до цикла делаю флаг=тру, следовательно, по условию если должно проходить. Ан-нет!
Собственно, именно поэтому поиск с несколькими критериями не работает. Из-за этого флага.
Еще я поигралась с условиями и получила следующие результаты (флаг все еще объявлен трушным вначале):
Не работает:

Работает:

Не работает:


Из всего этого я тогда вообще не понимаю системы Оо
1-й случай. Флаг по умолчанию тру, в условие попадает в любом случае. Даже если поля совпадают, флаг принимает значение фолс почему-то (а должен остаться по умолчанию тру), вывода в сетку нет.
2-й случай. Флаг по умолчанию тру, в условие попадает в любом случае. Поля совпадают - флаг принимает значение тру, вывод в сетку осуществляется.
3-й случай. Должен быть аналогичен 2-му, но, видимо, не проходит по условию (checkflad=true), и в него не заходит.

Вывод: несмотря на то, что флагу присваивается значение тру по умолчанию, видимо, этого не происходит, и флаг становится по умолчанию фолс. Или же еще до входа в цикл флаг каким-то мистическим образом меняет свое тру на фолс.
Моя реакция: ВТФ???
28.03.2014 в 17:01

There is no such thing as a stupid question, until you ask it
Хм. Может ли это вытекать из того, что я флаг обозначаю трушным вне цикла, и тогда он после первой же проверки остается со значением, которое было последним после всех этих ифов?.. Надо проверить.
28.03.2014 в 18:57

Птичка шизокрылая, Борец С Режимом Дня
1. Не пишите Checkflag=true! просто if checkflag.
2. Вам нужно полное соответствие строки или частичное? я для частичного сделала
2.5 Вы не возвращали флаг после каждого прохода цикла.
3. Не забывайте закрывать файлы
4. With - далеко не лучшая привычка. В большом методе с ним долбануться можно. В примере от него же избавилась, нужно только g:TStringGrid обявить


28.03.2014 в 19:22

There is no such thing as a stupid question, until you ask it
Neitent, Не пишите Checkflag=true! просто if checkflag.
Я обычно так и пишу, даже не знаю, что на меня тут нашло.
2.5 Вы не возвращали флаг после каждого прохода цикла.
Да, уже поправила, собственно, в этом и заключалась главная проблема С:

flag:=flag and pos (EditCreatror.text, proga.creator)
Блджад, логические уравнения, я уже и забыла, что их можно так использовать при присвоении!
Идея с pos отличная.
29.04.2014 в 10:08

Связать комбобоксы с полями. Например, тэгом. И на все повесить событие onChange

if (Sender AS TComboBox).Checked then {
(не помню функцию, есть в хелпе, поиск по имени элемента на форме) ("Edit"+Sender.Tag).Enabled:=true;
} else {
(не помню функцию, есть в хелпе, поиск по имени элемента на форме) ("Edit"+Sender.Tag).Enabled:=false;
(не помню функцию, есть в хелпе, поиск по имени элемента на форме) ("Edit"+Sender.Tag).text=''
}

А дальше уже проверка по пустоте.