gifts2017

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

Опубликовал Александр Венгер (venger) в раздел Программирование - Практика программирования

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) 23.07.08 13:24
Чем хороша такая конструкция?
Код
N=Спис2.РазмерСписка();
Если N>0 Тогда 
   i=0;
   Пока i<N Цикл
      i=i+1;
       //...
   КонецЦикла;
КонецЕсли;
Показать полностью


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

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

Я не злой, я просто пропагандирую 1с++ ;)
По соединениям/объединениям: http://www.1cpp.ru/docum/icpp/html/IndexedTable.html#innerjoin
13. Евгений Мартыненков (JohnyDeath) 23.07.08 17:28
+(12) она, кстати, и с буфером обмена умеет работать: http://www.1cpp.ru/docum/icpp/html/extobjs.html#id101 ;)
14. Евгений Мартыненков (JohnyDeath) 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) 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
У меня работает отбор по нескольким свойствам, создал справочник, где папка - свойство, а содержимое-возможные выборы. Меньше геморроя при заведении нового свойства и т.д. Единственно, гемор. при заполнении в номенклатуре, но написал табличную обработку...
Вообще Юзеры довольны, когда из громадного списка мебели отбираешь : тумбочки, нужного цвета
и нужной коллекции... (Я использовал СписокВСтроку и обратно для поиска по ТЗ)