Организация сложных фильтров объединенных условием и/или (Работа со списками значений)

Программирование - Практика программирования

1С:Предприятие 7.7
Предположим у нас есть справочник, с кучей подчиненных справочников. И нам хотелось бы организовать в форме списка отбор элементов справочника по полям подчиненных справочников, причем, для простоты, используя два (хотя можно и более) условия отбора, объединенных условиями и/или. Или просто нам не помешают приведенные функции работы со списками значений:-)
Основное - возможность получить объединение и пересечение двух или более множеств, используя приведенные функции работы со СпискамиЗначений.
Один из возможных простых способов:

Пишем простенькие функции отбора (столько, сколько вариантов отбора мы хотим):
// На подобии этой, отбор списка владельцев подчиненного справочника, по полю
// подчиненного справочника. Возвращает список значений владельцев подчиненного 
// справочника.
// ----------------------------------------------------------------------
Функция гл7ПолучитьСписокСертификатовПоНомеруРамы(Знач НомерРамы) Экспорт
	Перем СпрРамыДвигатели, СпСертификатов;
	
	НомерРамы=СокрЛП(НомерРамы);
	
	СпСертификатов=СоздатьОбъект("СписокЗначений");
	СпрРамыДвигатели=СоздатьОбъект("Справочник.СертификатыРамыДвигатели");

	СпрРамыДвигатели.ВыбратьЭлементыПоРеквизиту("НомерРамы",НомерРамы,0,0);
	Пока СпрРамыДвигатели.ПолучитьЭлемент()=1 Цикл
		Если СпрРамыДвигатели.ПометкаУдаления()=1 Тогда
			Продолжить;
		КонецЕсли;
		
		ТекВлад=СпрРамыДвигатели.ТекущийЭлемент().Владелец;
		Если СпСертификатов.НайтиЗначение(ТекВлад)<1 Тогда
			СпСертификатов.ДобавитьЗначение(ТекВлад);
		КонецЕсли;
	КонецЦикла;
	
	Возврат СпСертификатов;
КонецФункции	// гл7ПолучитьСписокСертификатовПоНомеруРамы
// ----------------------------------------------------------------------


После чего мы можем получить нужное количество списков элементов, удовлетворяющих нужному количеству условий. И теперь, чтобы объединить эти условия, используем следующие функции работы со списками значений:
// ----------------------------------------------------------------------
Функция гл8УдалитьДвойниковИзСписка(Знач Сп1) Экспорт
	Перем Сп, СпУдалПоз, СпУдалЗнач;
	
	Сп=СоздатьОбъект("СписокЗначений"); 
	СпУдалПоз=СоздатьОбъект("СписокЗначений"); 
	СпУдалЗнач=СоздатьОбъект("СписокЗначений"); 
	
	Если ВРег(СокрЛП(ТипЗначенияСтр(Сп1))) <> ВРег("СписокЗначений") Тогда
		Возврат Сп;
	КонецЕсли;
	
	Если Сп1.РазмерСписка()<1 Тогда
		Возврат Сп;
	КонецЕсли;
	
	N=Сп1.РазмерСписка();
	
	i=0;
	Пока i<N Цикл
		i=i+1;
		Sx="";
		V=Сп1.ПолучитьЗначение(i,Sx);

		Если (СпУдалЗнач.НайтиЗначение(V)<1) Тогда
			j=0;
			Пока j<N Цикл
				j=j+1;
				Sx2="";
				V2=Сп1.ПолучитьЗначение(j,Sx2);
				Попытка
					Если (V=V2) И (i<>j) И (СпУдалЗнач.НайтиЗначение(V2)>0) Тогда
						СпУдалПоз.ДобавитьЗначение(j);
					КонецЕсли;	
					Если (V=V2) Тогда
						СпУдалЗнач.ДобавитьЗначение(V2,Sx2);
					КонецЕсли;
				Исключение
				КонецПопытки;
			КонецЦикла;
		КонецЕсли;
	КонецЦикла;

	i=0;
	Пока i<N Цикл
		i=i+1;
		Sx="";
		V=Сп1.ПолучитьЗначение(i,Sx); 
		Если СпУдалПоз.НайтиЗначение(i)<1 Тогда
			Сп.ДобавитьЗначение(V,Sx);	
		КонецЕсли;
	КонецЦикла;

	Возврат Сп;
КонецФункции	// гл8УдалитьДвойниковИзСписка
// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
Функция гл8ПолучитьПересечениеСписков(Знач Сп1, Знач Сп2) Экспорт
	Перем Сп;

	Сп=СоздатьОбъект("СписокЗначений");
	
	Если ВРег(СокрЛП(ТипЗначенияСтр(Сп1))) <> ВРег("СписокЗначений") Тогда
		Возврат Сп;
	КонецЕсли;

	Если ВРег(СокрЛП(ТипЗначенияСтр(Сп2))) <> ВРег("СписокЗначений") Тогда
		Возврат Сп;
	КонецЕсли;
	
	Если Сп1.РазмерСписка()<1 Тогда
		Возврат Сп;
	КонецЕсли;

	Если Сп2.РазмерСписка()<1 Тогда
		Возврат Сп;
	КонецЕсли;

	Сп1=гл8УдалитьДвойниковИзСписка(Сп1);
	Сп2=гл8УдалитьДвойниковИзСписка(Сп2);
	
	N=Сп1.РазмерСписка();

	i=0;
	Пока i<N Цикл
		i=i+1;
		Sx="";
		V=Сп1.ПолучитьЗначение(i,Sx);
		Если Сп2.НайтиЗначение(V)>0 Тогда
			Сп.ДобавитьЗначение(V,Sx);
		КонецЕсли;
	КонецЦикла;

	Возврат Сп;
КонецФункции	// гл8ПолучитьПересечениеСписков
// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
Функция гл8ПолучитьОбъединениеСписков(Знач Сп1, Знач Сп2) Экспорт
	Перем Сп;

	Сп=СоздатьОбъект("СписокЗначений");

	Если ВРег(СокрЛП(ТипЗначенияСтр(Сп1))) <> ВРег("СписокЗначений") Тогда
		Сп1=СоздатьОбъект("СписокЗначений");
	КонецЕсли;

	Если ВРег(СокрЛП(ТипЗначенияСтр(Сп2))) <> ВРег("СписокЗначений") Тогда
		Сп2=СоздатьОбъект("СписокЗначений");
	КонецЕсли;
	
	Если Сп1.РазмерСписка()<1 Тогда
		Если Сп2.РазмерСписка()<1 Тогда
			Возврат Сп;
		Иначе
			Сп2.Выгрузить(Сп);
			Возврат Сп;
		КонецЕсли;
	КонецЕсли;

	Если Сп2.РазмерСписка()<1 Тогда
		Если Сп1.РазмерСписка()<1 Тогда
			Возврат Сп;
		Иначе
			Сп1.Выгрузить(Сп);
			Возврат Сп;
		КонецЕсли;	
	КонецЕсли;
	
	Сп1=гл8УдалитьДвойниковИзСписка(Сп1);
	Сп2=гл8УдалитьДвойниковИзСписка(Сп2);
	
	N=Сп1.РазмерСписка();

	i=0;
	Пока i<N Цикл
		i=i+1;
		Sx="";
		V=Сп1.ПолучитьЗначение(i,Sx);
		Поз=Сп2.НайтиЗначение(V);
		Если Поз>0 Тогда
			Сп2.УдалитьЗначение(Поз);
		КонецЕсли;
	КонецЦикла;
	
	Сп=гл9ОбъединитьСпискиЗначений(Сп1,Сп2);
	
	Возврат Сп;
КонецФункции	// гл8ПолучитьОбъединениеСписков
// ----------------------------------------------------------------------

Использовать можно следующим образом (упрощенно):
Перем СпСертификатов, СпСертификатов1, СпИтоговый;

СпСертификатов=гл7ПолучитьСписокСертификатовПоНомеруРамы(ЗначениеВВидеСтроки);
СпСертификатов1=гл7ПолучитьСписокСертификатовПоНомеруДвигателя(ЗначениеВВидеСтроки1);

// И
СпИтоговый=гл8ПолучитьПересечениеСписков(СпСертификатов,СпСертификатов1);
ИспользоватьСписокЭлементов(СпИтоговый);

// ИЛИ
СпИтоговый=гл8ПолучитьОбъединениеСписков(СпСертификатов,СпСертификатов1);
ИспользоватьСписокЭлементов(СпИтоговый);

Забыл, используется еще одна функция работы со списками:

Автор: Сергей Попов, Усинск, Коми
// ----------------------------------------------------------------------
//Объединяются списки значений
//Функция возвращает суммарный список
//При этом, Спис1 выгружается, а Спис2 построчно добавляется в конец списка
Функция гл9ОбъединитьСпискиЗначений(Спис1,Спис2) Экспорт
	//_new_ 19.03.2003 сп_
	Перем Res;
	Res=СоздатьОбъект("СписокЗначений");
	Если ВРег(СокрЛП(ТипЗначенияСтр(Спис1)))= ВРег("СписокЗначений") Тогда
		Спис1.Выгрузить(Res);
	КонецЕсли;
	Если ВРег(СокрЛП(ТипЗначенияСтр(Спис2)))= ВРег("СписокЗначений") Тогда
		N=Спис2.РазмерСписка();
		Если N>0 Тогда 
			i=0;
			Пока i<N Цикл
				i=i+1;
				Sx="";
				V=Спис2.ПолучитьЗначение(i,Sx);
				Res.ДобавитьЗначение(V,Sx);
			КонецЦикла;
		КонецЕсли;
	КонецЕсли;
	Возврат Res;
КонецФункции //гл9ОбъединитьСпискиЗначений
// ----------------------------------------------------------------------

См. также

Комментарии
1. Venger (venger) 23.07.08 12:08 Сейчас в теме
Забыл, используется еще одна функция работы со списками:

Автор: Сергей Попов, Усинск, Коми
Код
//===================================
//Объединяются списки значений
//Функция возвращает суммарный список
//При этом, Спис1 выгружается, а Спис2 построчно добавляется в конец списка
Функция гл9ОбъединитьСпискиЗначений(Спис1,Спис2) Экспорт
   //_new_ 19.03.2003 сп_
   Перем Res;
   Res=СоздатьОбъект("СписокЗначений");
   Если ВРег(СокрЛП(ТипЗначенияСтр(Спис1)))= ВРег("СписокЗначений") Тогда
      Спис1.Выгрузить(Res);
   КонецЕсли;
   Если ВРег(СокрЛП(ТипЗначенияСтр(Спис2)))= ВРег("СписокЗначений") Тогда
      N=Спис2.РазмерСписка();
      Если N>0 Тогда 
         i=0;
         Пока i<N Цикл
            i=i+1;
            Sx="";
            V=Спис2.ПолучитьЗначение(i,Sx);
            Res.ДобавитьЗначение(V,Sx);
         КонецЦикла;
      КонецЕсли;
   КонецЕсли;
   Возврат Res;
КонецФункции //гл9ОбъединитьСпискиЗначений
//=========================================
Показать полностью
2. Poppy (poppy) 3304 23.07.08 13:24 Сейчас в теме
Чем хороша такая конструкция?
Код
N=Спис2.РазмерСписка();
Если N>0 Тогда 
   i=0;
   Пока i<N Цикл
      i=i+1;
       //...
   КонецЦикла;
КонецЕсли;
Показать полностью


Почему не используется такая?
Код
Пока i = 1 По Спис2.РазмерСписка() Цикл
   //...
КонецЦикла;
Показать полностью

3. Евгений Мартыненков (JohnyDeath) 290 23.07.08 13:54 Сейчас в теме
Люди, курите прямые запросы! Зачем такой геморрой?
4. Venger (venger) 23.07.08 14:03 Сейчас в теме
(2) Чем хороша такая конструкция?

А чем плоха? Чем они вообще отличаются?:-)

Это все-равно, что спорить, что плохо, что индексы ТаблицЗначений или СписковЗначений начинаются с 1, а не с 0.
Я так с нуля люблю, но это дело привычки.

1С язык мне не родной, я его вспоминаю, когда в 1С'не наваять что нуно:-)
5. Venger (venger) 23.07.08 14:05 Сейчас в теме
(3) Затем что в прямых запросах нельзя строки неограниченной длины обрабатывать. Или по части поля сравнивать.

Хотя во многих случаях это хороший вариант, более быстрый.

Да и функции эти - работы со СпискамиЗначений - можно использовать и в других случаях, не только для этой задачи.
6. Евгений Мартыненков (JohnyDeath) 290 23.07.08 14:15 Сейчас в теме
(5) не понял.
1) Ты хочешь устанавливать фильтры на основе реквизитов, которые имеют неопределённую длину?
2) Что значит "в прямых запросах нельзя по части поля сравнивать"? (желательно пример)
7. Venger (venger) 23.07.08 14:24 Сейчас в теме
(6)

1) Я не то, чтобы хочу, но если будет такая необходимость организовать фильтрацию или поиск, то какие проблемы:-)

2) Например, поле номер рамы: LAPP12345ERT569835789. Пользователям удобно искать или фильтровать не полный номер рамы, а только по последним символам, допустим по 5789.
8. Venger (venger) 23.07.08 14:28 Сейчас в теме
И вообще, злые вы....:-)

Хоть бы плюсик поставили:-)
9. Venger (venger) 23.07.08 15:42 Сейчас в теме
По сути, ценно тут то, что можно получить объединение и пересечение двух множеств.

А для каких задач это использовать (фильтры это так, пример), это уже второй вопрос.
10. Михаил Ражиков (tango) 474 23.07.08 16:11 Сейчас в теме
11. Venger (venger) 23.07.08 16:46 Сейчас в теме
Тут пришла мысль:-)

Объединение можно получить еще одним способом.

Слить два списка вместе, а потом удалить двойников из результирующего.

Хоть это тоже самое, только в профиль:-)

Что-то типа такого:
Код
// ----------------------------------------------------------------------
Функция гл8ПолучитьОбъединениеСписков(Знач Сп1, Знач Сп2) Экспорт
   Перем Сп;

   Сп=СоздатьОбъект("СписокЗначений");

   Если ВРег(СокрЛП(ТипЗначенияСтр(Сп1))) <> ВРег("СписокЗначений") Тогда
      Сп1=СоздатьОбъект("СписокЗначений");
   КонецЕсли;

   Если ВРег(СокрЛП(ТипЗначенияСтр(Сп2))) <> ВРег("СписокЗначений") Тогда
      Сп2=СоздатьОбъект("СписокЗначений");
   КонецЕсли;
   
   Если Сп1.РазмерСписка()<1 Тогда
      Если Сп2.РазмерСписка()<1 Тогда
         Возврат Сп;
      Иначе
         Сп2.Выгрузить(Сп);
         Возврат Сп;
      КонецЕсли;
   КонецЕсли;

   Если Сп2.РазмерСписка()<1 Тогда
      Если Сп1.РазмерСписка()<1 Тогда
         Возврат Сп;
      Иначе
         Сп1.Выгрузить(Сп);
         Возврат Сп;
      КонецЕсли;   
   КонецЕсли;

   Сп=гл9ОбъединитьСпискиЗначений(Сп1,Сп2);
   Сп=гл8УдалитьДвойниковИзСписка(Сп);

   Возврат Сп;
КонецФункции   // гл8ПолучитьОбъединениеСписков
// ----------------------------------------------------------------------
Показать полностью

12. Евгений Мартыненков (JohnyDeath) 290 23.07.08 17:25 Сейчас в теме
Venger,
фильтровать не полный номер рамы, а только по последним символам, допустим по 5789
это очень даже просто реализуется в прямых запросах так:
Код
 ...
Where НомерРамы LIKE '%5789' 
Показать полностью

Я не злой, я просто пропагандирую 1с++ ;)
По соединениям/объединениям: http://www.1cpp.ru/docum/icpp/html/IndexedTable.html#innerjoin
13. Евгений Мартыненков (JohnyDeath) 290 23.07.08 17:28 Сейчас в теме
+(12) она, кстати, и с буфером обмена умеет работать: http://www.1cpp.ru/docum/icpp/html/extobjs.html#id101 ;)
14. Евгений Мартыненков (JohnyDeath) 290 23.07.08 17:51 Сейчас в теме
По текущей задаче (отбор элементов по полям подч. справочника):
предложенный тобой код будет работать только для полей, у которых стоит галочк "Сортировка", т.к
Код
СпрРамыДвигатели.ВыбратьЭлементыПоРеквизиту("НомерРамы",НомерРамы,0,0);
Показать полностью


Примерно так будет выглядеть твоя задача с использованием 1sqlite http://infostart.ru/projects/2127/
Код
БД = СоздатьОбъект("SQLiteBase");
БД.Открыть(":memory:");            
Запрос=БД.НовыйЗапрос();
ТекстЗапроса="
SELECT Товары.ID [Товар :Справочник.Номенклатура]
FROM Справочник_Номенклатура as Товары
INNER JOIN Справочник_СертификатыРамыДвигатели as СРД ON СРД.PARENTEXT = Товары.ID
Where СРД.НомерРамы LIKE '%5789'"; 

спНужныхТоваров=СоздатьОбъект("СписокЗначений");
Запрос.ВыполнитьЗапрос(ТекстЗапроса, спНужныхТоваров);
Показать полностью

ВСЁ! Если надо ещё и по другим подч. справочникам или добавить условие, то это легко формируется динамически, т.к. ТекстЗапроса - это строка и ей оперировать очень просто!
А если условий будет 10? У тебя списки будут 10 раз объединятся и фильтроваться??
15. Venger (venger) 23.07.08 18:11 Сейчас в теме
С этими ВК (1sqlite и 1С++) - да согласен, задача фильтров лучше решается.

Пасибки, многие ВК я еще не изучал на предмет использования. FormEx смотрел только одним глазком, что он может.

Но, во-первых, эти функции для списоков могут пригодится и для других целей возможно. Фильтры были как пример для этих функций.

Во-вторых, использовать чужие ВК - это зависимость, а если там баг или глюк, а если надо что-то особенное.
Поэтому я склонен идти по пути "придумывания велосипеда", чтоб когда надо будет "придумывать космический корабль", уже быть готовым к этому:-)

Вот собираюсь основательно взяться за изучение Delphi вообще, и в приложении к 1С (ВК и ВП) в частности.
Правда времени как всегда мало на это все:-)

Но в любом случае спасибо, я всегда рад советам.
16. Евгений Мартыненков (JohnyDeath) 290 23.07.08 23:14 Сейчас в теме
(15) Разработчики 1с++ всегда рады помочь: есть форум, багзилла. К тому же исходники 1с++ ты всегда можешь скачать и сам у себя собрать свою собственную 1с++.

А в каких, например, случаях могут понадобится такие объединения списков? (просто интересно)

Лучше возьмись за изучение С++, оно к 1С ближе ;)
Удачи!
17. Venger (venger) 24.07.08 12:07 Сейчас в теме
(16) >А в каких, например, случаях могут понадобится такие объединения списков?

Надо подумать:-) У кого-то есть идеи?:-) Самому интересно:-)

(16) >Лучше возьмись за изучение С++, оно к 1С ближе ;)

C++ года четыре назад в универе учил, в основном под консоль правда, но обновить знания и пойти дальше не помешает и в С++, конечно.
Но и Delphi не помешает;-)

Кстати, сам язык 1С 7.7 больше Бейсик напоминает. У кого какие ассоциации?
Понятно, что 1С на С++ писался, поэтому и ближе. Верно?

В любом случае, я рад, что есть кому подсказать, так что Вам все-равно пасибки:-)

18. Mirt1207 (mirt1207) 30.07.08 11:28 Сейчас в теме
У меня работает отбор по нескольким свойствам, создал справочник, где папка - свойство, а содержимое-возможные выборы. Меньше геморроя при заведении нового свойства и т.д. Единственно, гемор. при заполнении в номенклатуре, но написал табличную обработку...
Вообще Юзеры довольны, когда из громадного списка мебели отбираешь : тумбочки, нужного цвета
и нужной коллекции... (Я использовал СписокВСтроку и обратно для поиска по ТЗ)
Оставьте свое сообщение