Долой дубли!

16.03.09

Разработка - Рефакторинг и качество кода

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

Речь идет об алгоритме создания документов по свернутым данным из таблицы значений (ТЗ), в которой кроме данных для заполнения табличной части присутствуют также данные для шапки документа.

Традиционный способ:
1. Сортируем ТЗ по этим колонкам с данными для шапки (назовем их ключами)
2. Перебираем ТЗ и по мере смены ключей создаем новый/записываем предыдущий документ для заполнения
3. После перебора проверяем и записываем документ.

ТЗ.Сортировать("Ключ_1,Ключ_2,...,Ключ_N");
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл 
  Если ТЗ.НомерСтроки = 1 Тогда
    // БЛОК 1 Создать новый документ
    // БЛОК 2 Заполнить заголовок документа
  ИначеЕсли	(ТЗ.Ключ_1 <> ПредЗначение_Ключ_1) ИЛИ
            (ТЗ.Ключ_2 <> ПредЗначение_Ключ_2) ИЛИ
            ...
            (ТЗ.Ключ_N <> ПредЗначение_Ключ_N) Тогда
    // сменились ключи			
    // БЛОК 3 Заполнить подвал документа
    // БЛОК 4 Записать / вывести документ 

    // БЛОК 1 Создать новый документ !дублируем код!
    // БЛОК 2 Заполнить заголовок документа !дублируем код!
  КонецЕсли;
  // БЛОК 5 Заполнение строки спецификации документа

  ПредЗначение_Ключ_1 = ТЗ.Ключ_1; 
  ПредЗначение_Ключ_2 = ТЗ.Ключ_2;
  ...
  ПредЗначение_Ключ_N = ТЗ.Ключ_N;
КонецЦикла;
// БЛОК 3 Заполнить подвал документа !дублируем код!
// БЛОК 4 Записать / вывести документ !дублируем код!


Тот же алгоритм применяется при группировке и выводе данных из ТЗ в печатную форму.

"Копипасте" кода - вешь нехорошая, неэстетичная, и даже бы сказал отбивающая желание заниматься этим делом вообще, а если еще этот код будут читать другие...
Можно, конечно, все это вывести в процедуры, но тогда в них надо передать массу параметров, что не совсем удобно.

Первое, и самое простое, что можно сделать - добавить и перестроить условия в цикле ( комментарии (6) Tarasenkov и (10) Orefkov ) :

ПредЗначение_Ключ_1	= ПолучитьПустоеЗначение();
ПредЗначение_Ключ_2	= ПолучитьПустоеЗначение();
...
ПредЗначение_Ключ_N	= ПолучитьПустоеЗначение();

ТЗ.Сортировать("Ключ_1,Ключ_2,...,Ключ_N");
ТЗ.НоваяСтрока();
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл 
  Если (ТЗ.Ключ_1 <> ПредЗначение_Ключ_1) ИЛИ
       (ТЗ.Ключ_2 <> ПредЗначение_Ключ_2) ИЛИ
       ...
       (ТЗ.Ключ_N <> ПредЗначение_Ключ_N) ИЛИ (ТЗ.НомерСтроки = 1) Тогда
    Если ТЗ.НомерСтроки > 1 Тогда
      // БЛОК 3 Заполнить подвал документа
      // БЛОК 4 Записать / вывести документ
    КонецЕсли;
    Если ТЗ.НомерСтроки = ТЗ.КоличествоСтрок() Тогда
      Прервать;
    КонецЕсли;
    // БЛОК 1 Создать новый документ
    // БЛОК 2 Заполнить заголовок документа
  КонецЕсли;
  // БЛОК 5 Заполнение строки спецификации документа

  ПредЗначение_Ключ_1 = ТЗ.Ключ_1; 
  ПредЗначение_Ключ_2 = ТЗ.Ключ_2;
  ...
  ПредЗначение_Ключ_N = ТЗ.Ключ_N;
КонецЦикла; 
ТЗ.УдалитьСтроку(ТЗ.КоличествоСтрок());


Вариант poppy (комментарий (23)) c избавлением от нагромождения условий и самым простым кодом: достигается вынесением в функцию чтения и сравнения ключей двух соседних записей:

                                        
// ПроверитьСтрокуТаблицы(<ТЗ>,<СписокКлючей>)
// 
// функция проверяет достижение текущей строки ТЗ ее последней записи, 
// или несовпадение значений по списку колонок в текущей и следующей записи, возвращает:
// 1 в случае выполнения условий, 0 - иначе.
// Параметры:
// <ТЗ>             - таблица значений
// <СписокКлючей>   - список значений - идентификаторов сравниваемых колонок

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

Применение функции:
СтрокаКолонок = "Ключ_1,Ключ_2,...,Ключ_N";
СписокКлючей = СоздатьОбъект("СписокЗначений");
СписокКлючей.ИзСтрокиСРазделителями("""" + СтрЗаменить(СтрокаКолонок, ",", """,""") + """");
   	
Флаг = 1;
ТЗ.Сортировать(СтрокаКолонок);
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл 
  Если Флаг = 1 Тогда
    // БЛОК 1 Создать новый документ
    // БЛОК 2 Заполнить заголовок документа
  КонецЕсли;
  // БЛОК 5 Заполнение строки спецификации документа
  Флаг = ПроверитьСтрокуТаблицы(ТЗ, СписокКлючей);
  Если Флаг = 1 Тогда
    // БЛОК 3 Заполнить подвал документа
    // БЛОК 4 Записать / вывести документ
  КонецЕсли;
КонецЦикла;

Просто и понятно.

Метод автора, на момент появления статьи описывался здесь в ед. числе.
Он заключается в предварительном прогоне таблицы с проверкой условий и заполнением двух новых колонок в ТЗ с значениями-индикаторами о создании и записи документа.

// ИзмененияКлючейТЗ(<ТЗ>,<СтрокаКолонок>,<ИмяКолонкиНач>,<ИмяКонКолонкиКон>)
//
// Процедура выполняет сравнение значений в указанных колонках ТЗ в соседних записях и 
// записывает результат сравнения  в две добавляемые колонки 
//
// ТЗ             - таблица значений
// СтрокаКолонок  - Строка колонок (ключей) через запятую, по которым сравниваются значения между соседними записями
// ИмяКолонкиНач  - Имя добавляемой в ТЗ первой колонки, необязательное 
//   (если не задано, обращение к ней возможно по номеру "1") . 
//   В данную колонку заносится число, результат сравнения текущей и предыдущей записи, 
//   а именно, ближайший номер колонки (по порядку из <СтрокаКолонок>), по которой 
//   значения в указанных записях не совпадают; 0 если несовпадений нет. 
// ИмяКолонкиКон	- Имя добавляемой в ТЗ второй колонки, необязательное 
//   (если не задано, обращение к ней возможно по номеру "1")
//   В данную колонку заносится число, результат сравнения текущей и следующей записи, 
//   а именно, ближайший номер колонки (по порядку из <СтрокаКолонок>), по которой 
//   значения в указанных записях не совпадают; 0 если несовпадений нет. 

Процедура ИзмененияКлючейТЗ(ТЗ,Знач СтрокаКолонок,Знач ИмяКолонкиНач = "",Знач ИмяКолонкиКон = "")
  Перем ИДКолонок[10]; // идентификаторы колонок-ключей 
  Перем Ключи[10];     // значения ключей
  Перем КолКлючей;     // количество ключей          
  Перем М1,М2;
  Перем КолСтрокТЗ;  
  Перем ПромЗнач;
   
  КолСтрокТЗ = ТЗ.КоличествоСтрок();

  Если КолСтрокТЗ > 0 Тогда
    СтрокаКолонок = СтрЗаменить(СтрокаКолонок,",",РазделительСтрок);
    КолКлючей = СтрКоличествоСтрок(СтрокаКолонок);

    // получить идентификаторы колонок
    Для М1 = 1 По КолКлючей Цикл
      ИДКолонок[М1] = СтрПолучитьСтроку(СтрокаКолонок,М1);
    КонецЦикла;

    ТЗ.ВставитьКолонку(ИмяКолонкиНач,1);
    ТЗ.ВставитьКолонку(ИмяКолонкиКон,2);

    ТЗ.Заполнить(0,,,"1,2");

    ТЗ.УстановитьЗначение(1,1,1); 	// начало в 1-й записи
    ТЗ.УстановитьЗначение(КолСтрокТЗ,2,1);	// конец в последней

    // заполнить ключи по первой записи
    Для М1 = 1 По КолКлючей Цикл
      Ключи[М1] = ТЗ.ПолучитьЗначение(1,ИДКолонок[М1]);
    КонецЦикла;

    // начинаем сравнение со второй по последнюю
    Для М2 = 2 По КолСтрокТЗ Цикл

      М1 = 1;
      Пока М1 <=КолКлючей Цикл
        ПромЗнач = ТЗ.ПолучитьЗначение(М2,ИДКолонок[М1]);
        Если ПромЗнач   <> Ключи[М1] Тогда
          ТЗ.УстановитьЗначение(М2 - 1,2,М1); // конец старой группировки в пред записи
          ТЗ.УстановитьЗначение(М2    ,1,М1); // начало новой группировки в текущей записи 
          // обновим оставшиеся ключи 
          Ключи[М1]   = ПромЗнач;
          М1 = М1 + 1;
          Пока М1 <= КолКлючей Цикл
            Ключи[М1] = ТЗ.ПолучитьЗначение(М2,ИДКолонок[М1]);
            М1 = М1 + 1;
          КонецЦикла;
          Прервать;
        КонецЕсли;     
        М1 = М1 + 1;
      КонецЦикла;               
    КонецЦикла;
  КонецЕсли;
КонецПроцедуры                        

Процедура отрабатывает сравнение ключей полностью, с указанием номера несовпадающей колонки-ключа.
Получилось не совсем кратко, вследствие оптимизации, зато использование очень компактное:
СтрокаКолонок = "Ключ_1,Ключ_2,...,Ключ_N";	
ТЗ.Сортировать(СтрокаКолонок);
ИзмененияКлючейТЗ(ТЗ,СтрокаКолонок,"Нач","Кон");
	
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл 
  Если ТЗ.Нач > 0 Тогда
    // БЛОК 1 Создать новый документ 
    // БЛОК 2 Заполнить заголовок документа 
  КонецЕсли;
  // БЛОК 5 Заполнение строки спецификации документа
  Если ТЗ.Кон > 0 Тогда
    // БЛОК 3 Заполнить подвал документа
    // БЛОК 4 Записать документ 
  КонецЕсли;
КонецЦикла;


Оригинальный способ указан int3 (комментарий (3)). Он заключается в создании вспомогательной таблицы индексов, которая представляет из себя исходную ТЗ, свернутую по колонкам-ключам с суммированием количества записей. (изменено оформление).
// СоздатьИндексТЗ(<ТЗ>,<СтрокаКолонок>,<ИмяИндексКолонки>)
// функция возвращает свернутую таблицу значений по указанным колонкам 
// с суммированием количества записей в добавляемой колонке
// параметры: 
// ТЗ               - таблица значений для свертки
// СтрокаКолонок    - строка колонок, разделенных запятыми, для свертки.
// ИмяИндексКолонки - добавляемая колонка №1, в которой суммируется количество записей.

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

    ТЗИндекс.ВставитьКолонку(ИмяИндексКолонки,1);
    ТЗИндекс.Заполнить(1,,,"1");
    ТЗИндекс.Свернуть(СтрокаКолонок,"1");
    ТЗИндекс.Сортировать(СтрокаКолонок,1);
  КонецЕсли;
  Возврат ТЗИндекс;
КонецФункции


Применение таблицы значений - "индекса":
СтрокаКолонок = "Ключ_1,Ключ_2,...,Ключ_N";	
ТЗ.Сортировать(СтрокаКолонок);

ТЗИндекс = СоздатьИндексТЗ(ТЗ,СтрокаКолонок,"Кол");
ТЗИндекс.ВыбратьСтроки();

ТЗ.ВыбратьСтроки();
Пока ТЗИндекс.ПолучитьСтроку()  = 1 Цикл
  // БЛОК 1 Создать новый документ 
  // БЛОК 2 Заполнить заголовок документа 
  Для М1 = 1 По ТЗИндекс.Кол Цикл
    ТЗ.ПолучитьСтроку();
    // БЛОК 5 Заполнение строки спецификации документа
  КонецЦикла;
  // БЛОК 3 Заполнить подвал документа
  // БЛОК 4 Записать документ 
КонецЦикла;


Автор выражает благодарность Poppy, Int3, Tarasenkov, Orefkov за активное участие в статье.

P.S. О быстродействии.
Самый быстрый, - это метод Int3. Он выигрывает у традиционного при средней длине "спецификации" от 50-ти строк ("2% шапок"), или по 1 строке в 98% случаях.
Метод автора дает к традиционному от +50 до +130%.
Но даже сотня процентов дает очень незначительный прирост по времени, - "голые" циклы и условия (сравнивалось на них) на 100т записях прогоняются за секунды, так что красота, удобочитаемость и универсальность кода того ст'оит
 
Изменения от 16.03.2009
 
Еще один метод, с хранением всех новых документов в списке значений и общей записью документов вне цикла

                                        
// ОбщийКлюч(<ТЗ>,<СписокКлючей>)
// 
// функция формирует и возвращает общий строковый ключ по значениям из текущей строки ТЗ, 
// по переданному списку колонок
// Параметры:
// <ТЗ>             - таблица значений
// <СписокКлючей>   - список значений - идентификаторов колонок

Функция ОбщийКлюч(ТЗ, СписокКлючей)
  Перем НомСтр;
  Перем М1;
  Перем ТекКлюч;
  НомСтр = ТЗ.НомерСтроки;
  ТекКлюч = ЗначениеВСтрокуВнутр(  ТЗ.ПолучитьЗначение(НомСтр,
                                                      СписокКлючей.ПолучитьЗначение(1)));
  Для М1 = 2 По СписокКлючей.РазмерСписка()  Цикл
     ТекКлюч = ТекКлюч +  
                 ЗначениеВСтрокуВнутр(   ТЗ.ПолучитьЗначение(НомСтр ,
                                                      СписокКлючей.ПолучитьЗначение(М1)));
  КонецЦикла;
  Возврат 0;
КонецФункции

Применение функции:
СтрокаКолонок = "Ключ_1,Ключ_2,...,Ключ_N";
СписокКлючей = СоздатьОбъект("СписокЗначений");
СписокНовДок = СоздатьОбъект("СписокЗначений");
СписокКлючей.ИзСтрокиСРазделителями("""" + СтрЗаменить(СтрокаКолонок, ",", """,""") + """");
   	
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл 
  ТекКлюч = ОбщийКлюч(ТЗ, СписокКлючей);
  НовДок = СписокНовДок.Получить(ТекКлюч);
  Если ТипЗначенияСтр(НовДок) = "Документ" Тогда
    // БЛОК 1 Создать новый документ в НовДок с использованием СоздатьОбъект()
    // БЛОК 2 Заполнить заголовок документа НовДок
    СписокНовДок.Установить(ТекКлюч, НовДок);
  КонецЕсли;
  // БЛОК 5 Заполнение строки спецификации документа НовДок
КонецЦикла;
Для М1 = 1 По СписокНовДок.РазмерСписка() Цикл
    НовДок = СписокНовДок.ПолучитьЗначение(М1);
      // БЛОК 3 Заполнить подвал документа НовДок
      // БЛОК 4 Записать / вывести документ НовДок
КонецЦикла;


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

Вступайте в нашу телеграмм-группу Инфостарт

Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».

  • 0% комиссии — оплата напрямую исполнителю;
  • Исполнители любого масштаба — от отдельных специалистов до команд под проект;
  • Прямой обмен контактами между заказчиком и исполнителем;
  • Безопасная сделка — при необходимости;
  • Рейтинги, кейсы и прозрачная система откликов.

См. также

Рефакторинг и качество кода Нейросети Программист 1С:Предприятие 8 Бесплатно (free)

Продолжение статьи «BSL Language Server внутри 1С:EDT: коннектор, которого не хватало». Там мы научили EDT видеть расширенную диагностику BSLLS, а здесь замкнем контур: научим ИИ-агентов автоматически исправлять эти ошибки строго по официальным стандартам разработки 1С.

02.09.2026    1195    malikov_pro    2    

6

Нейросети Рефакторинг и качество кода Программист 1С 8.3 Бесплатно (free)

В справочнике дополнительных обработок любой базы старше трёх лет лежит два-три десятка файлов. Часть писали вы, часть предыдущий разработчик, часть прислал подрядчик. Завтра одну из них надо править, и первый вопрос не "как править", а "что она вообще делает". Обычный ответ - открыть и читать: полторы тысячи строк это полдня. Показываю, как уложиться в пять минут: пять шагов, у каждого сказано, что он даёт. Внутри распределение 1 135 находок по ста чужим обработкам (четыре первых места из шести - одно и то же), что нашлось в наших собственных обработках, и честный список того, чего этот способ не даёт.

01.09.2026    1173    nedomolkov.ivan    6    

2

Инструментарий разработчика Нейросети Рефакторинг и качество кода Программист 1С 8.3 Абонемент ($m)

Вам досталась чужая обработка на полторы тысячи строк. Автор уволился, документации нет, а править её завтра. Обработка читает код за вас: что он делает, что в нём не так и с чего начинать. Всё считается локально, в интернет ничего не уходит.

10 стартмани

01.09.2026    425    1    nedomolkov.ivan    5    

1

Рефакторинг и качество кода Нейросети Программист 1С:Предприятие 8 Бесплатно (free)

Рассказываю, как подключить к языковым моделям (в Cursor, VS Code, Claude Code) настоящий Синтакс-помощник 1С - со всеми типами, методами, параметрами, примерами кода, контекстами вызова и версиями платформы. Без ручного парсинга HTML: плагин для 1C:EDT в один клик выгружает официальную справку в Docker-контейнер с MCP-сервером и гибридным поиском (FTS5 + векторный индекс).

01.09.2026    1095    malikov_pro    4    

4

Рефакторинг и качество кода Программист 1С 8.3 Бесплатно (free)

Синтаксический контроль прошёл, валидатор формы прошёл, сборка прошла, автопрогон написал что ошибок нет. Через час четыре падения подряд, все на том, что мы сами не нажимали. Разбираю все четыре, включая самое коварное: загрузчик молча выбрасывает элемент формы из-за одной буквы в теге, и отладка уходит в модуль, где всё правильно.

01.09.2026    807    nedomolkov.ivan    0    

2

Рефакторинг и качество кода Программист 1С:Предприятие 8 Бесплатно (free)

Если вы разрабатываете в 1C:EDT, то наверняка знакомы со штатной системой статического анализа через плагин 1C:Code style V8. Но вы также могли заметить, что база встроенных правил давно не обновлялась: последний стабильный релиз плагина вышел ещё в декабре 2023 года. Чтобы вернуть в EDT свежие и актуальные проверки, я форкнул и доработал проект коннектора к BSL Language Server. В этой статье расскажу, как устроена интеграция, зачем она нужна в эпоху LLM и как подключить плагин за пять минут.

01.09.2026    840    malikov_pro    5    

4

Инструментарий разработчика Рефакторинг и качество кода Программист 1С 8.3 1С 8.5 Абонемент ($m)

Внешняя обработка для 1С, которая ищет в коде нарушения стандартов БСП и платформы (модальные вызовы, запросы в цикле, незакрытый привилегированный режим и др.) и часть из них чинит автоматически. Работает по фрагменту кода и по всей выгрузке конфигурации, с фоновым режимом через БСП и отчётом по найденному.

3 стартмани

27.08.2026    364    0    KonMa    0    

2

Перенос данных 1C Рефакторинг и качество кода Программист 1С 8.3 Бесплатно (free)

Правила обмена умеют лежать в двух местах сразу: в макете конфигурации, где их находит глобальный поиск и где их так удобно поправить, и в регистре сведений, откуда они на самом деле исполняются. Пока не проверишь, какой источник включён у плана обмена, правка макета выглядит сделанной и не делает ничего. Разбор задачи, где одно и то же значение ставки оказалось зашито в четырёх местах трёх разных слоёв: карта соответствий в модуле веб-сервиса, обработчик "ПослеЗагрузки" в правиле обмена, числовой ВЫБОР с внутренним ИНАЧЕ 0 внутри сгенерированных правил и общий модуль интеграции с внешним поставщиком чеков. Отдельно - почему у истории с вернувшимися карточками нет доказанной причины, хотя признак сузили до одного вида номенклатуры, и как тестовый прогон на пяти записях сломал сходимость итогового отчёта.

27.08.2026    676    nedomolkov.ivan    0    

1
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. CheBurator 3234 12.01.08 20:31 Сейчас в теме
неэстетично...
имхается проще и красивше рекурсивную процедуру заюзать...
8. Shaman100M 1155 14.01.08 09:57 Сейчас в теме
(1) (2) Рекурсивная, скорее всего, на сравнение и заполнение строк ТЧ. Иногда может получиться громоздко, т.к. нужно хранить / передавать в процедуру(ы) ключи и все, что там необходимо будет сделать. Я попытался реализовать одной вспомогательной универсальной процедурой с четко определенными параметрами.
(3) +1 хороший индекс! Единственное, добавил бы после свертки ТЗИндекс
Код
 ТЗИндекс.Сортировать(СтрокаКолонок); // Сортировка после Свернуть() может сбиться. 
Показать полностью
14. int3 14.01.08 13:57 Сейчас в теме
(8) Согласен, никто не гарантирует порядка после свертки, правда ни разу не сталкивался с такой ситуацией :-/ (Хотя в реальных алгоритмах дополнительную сортировку включаю в код - надежность дороже ;-) )
(12) такой индекс даст прирост производительности на больших объемах данных, т.е. когда сложность условия в цикле превысит накладные расходы на индексирование, т.е. к примеру при десятке ключей на сотне тысяч записей
ну или иначе - при варировании ключевых условий, тут уже выйдет на первое место гибкость :-/
17. Shaman100M 1155 14.01.08 16:30 Сейчас в теме
(14) при десятке ключей на сотне т: минус 40%
2. poppy 12.01.08 21:18 Сейчас в теме
Как-то уж очень мудрено...

Самый простой способ избавиться от дублей - использовать процедуры. Ведь они именно для этого придуманы?
3. int3 13.01.08 23:22 Сейчас в теме
Если есть желание сэкономить число сравнений, почему бы не построить индекс? Например так:
Код
СтрокаКолонок = "Ключ_1,Ключ_2,...,Ключ_N";   
ТЗ.Сортировать(СтрокаКолонок);
ТЗиндекс = СоздатьОбъект("ТаблицаЗначений");
ТЗ.Выгрузить(ТЗиндекс,,,СтрокаКолонок);
ИндексКолонка = ТЗиндекс.КоличествоКолонок()+1;
ТЗиндекс.КоличествоКолонок(ИндексКолонка);
ТЗиндекс.Заполнить(1,,,ИндексКолонка);
ТЗиндекс.Свернуть(СтрокаКолонок,ИндексКолонка);
ТЗ.ВыбратьСтроки();
РазмерБлока = 0;
НомерБлока = 0;
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
    Если РазмерБлока=0 Тогда
      НомерБлока = НомерБлока + 1;
      РазмерБлока = ТЗиндекс.ПолучитьЗначение(НомерБлока,ИндексКолонка);
        // БЛОК 1 Создать новый документ 
        // БЛОК 2 Заполнить заголовок документа 
    КонецЕсли;
    // БЛОК 5 Заполнение строки спецификации документа
   РазмерБлока = РазмерБлока - 1;
    Если РазмерБлока=0 Тогда
        // БЛОК 3 Заполнить подвал документа
        // БЛОК 4 Записать документ 
    КонецЕсли;
КонецЦикла;
Показать полностью

всяко пошустрее будет
Shaman100M; +1 Ответить
15. int3 14.01.08 14:26 Сейчас в теме
> 3 ключа, значения от 1 до 10:
> (3) +400%
> мой: +180%

Кстати, очень интересный результат. На что ушло время?
16. Shaman100M 1155 14.01.08 15:41 Сейчас в теме
(15) Замерял не в отладчике, а с пом. _GetPerformanceCounter() . Отладчик показывает +100% Максимум времени ушло на Свернуть()
18. int3 15.01.08 08:30 Сейчас в теме
(16) хм... Такая картина - несовпадение результатов разных методов замера наверное объясняется "особенностями" работы платформы, в частности уборщика мусора.
(17) т.е. сфера применения у него найдется, и не только в плане "красивости" кода? буду рад, если пригодится :)
когда-то на проклабе обнаружил конкурс на самый быстрый метод удаления строк из ТЗ (правда он к тому времени уже закончился), заинтересовался и написал свой вариант с использованием такого индексирования - занятный вариант получился, но не без "особенностей" :)
20. Shaman100M 1155 15.01.08 12:49 Сейчас в теме
(18) да, полезный конкурс, столько особенностей при оптимизации обнаружилось, вплоть до наличия запятой в НоваяКолонка() :)
(19) )))) Ну тогда какое-нить другое красивое слово.
21. Shaman100M 1155 15.01.08 12:53 Сейчас в теме
(18) если не против, добавлю в статью с ссылкой.
22. int3 15.01.08 13:53 Сейчас в теме
4. tarasenkov 341 14.01.08 09:06 Сейчас в теме
А не проще написать так:
Код
Если (ТЗ.НомерСтроки    = 1) ИЛИ
(ТЗ.Ключ_1<>ПредЗначение_Ключ_1) ИЛИ
(ТЗ.Ключ_2<>ПредЗначение_Ключ_2) ИЛИ
...
(ТЗ.Ключ_N<>ПредЗначение_Ключ_N) Тогда

Если (ТЗ.НомерСтроки    = 1) Тогда
        // БЛОК 1 Создать новый документ !дублируем код!
        // БЛОК 2 Заполнить заголовок документа !дублируем код!
Иначе
        // сменились ключи         
        // БЛОК 3 Заполнить подвал документа
        // БЛОК 4 Записать / вывести документ 
КонецЕсли;

КонецЕсли;
Показать полностью

При этом никакого дублирования кода нет,
и удобочитаемость остается.
9. Shaman100M 1155 14.01.08 10:10 Сейчас в теме
(4) (5) (6) от объединения условий отказался, - можно запутаться в условиях. Блоки 3 и 4 всегда должны идти после блока 5 (для текущего документа), их дублирования условиями не избежать. Если добавлять фиктивную строку, - необходимо проверить/задать уникальные ключи для нее.
5. tarasenkov 341 14.01.08 09:09 Сейчас в теме
И почему нельзя редактировать комментарии?
Я имел ввиду такой код:

Если (ТЗ.НомерСтроки = 1) ИЛИ
(ТЗ.Ключ_1<>ПредЗначение_Ключ_1) ИЛИ
(ТЗ.Ключ_2<>ПредЗначение_Ключ_2) ИЛИ
...
(ТЗ.Ключ_N<>ПредЗначение_Ключ_N) Тогда
// БЛОК 1 Создать новый документ
// БЛОК 2 Заполнить заголовок документа
Если (ТЗ.НомерСтроки > 1) Тогда
// сменились ключи
// БЛОК 3 Заполнить подвал документа
// БЛОК 4 Записать / вывести документ
КонецЕсли;
КонецЕсли;
6. tarasenkov 341 14.01.08 09:15 Сейчас в теме
* и предварительного просмотра нет - тоже плохо :(
Итак.
Код
Если (ТЗ.НомерСтроки = 1) ИЛИ
(ТЗ.Ключ_1<>ПредЗначение_Ключ_1) ИЛИ
(ТЗ.Ключ_2<>ПредЗначение_Ключ_2) ИЛИ
...
(ТЗ.Ключ_N<>ПредЗначение_Ключ_N) Тогда
   Если (ТЗ.НомерСтроки > 1) Тогда
      // сменились ключи 
      // БЛОК 3 Заполнить подвал документа
      // БЛОК 4 Записать / вывести документ 
   КонецЕсли;
   // БЛОК 1 Создать новый документ
   // БЛОК 2 Заполнить заголовок документа
КонецЕсли;
Показать полностью


Дублирование записи документа/вывода печатной формы после цикла не так страшно.
Хотя тоже можно избежать добавив в таблицу фиктивную последнюю строку.
Shaman100M; +1 Ответить
12. Shaman100M 1155 14.01.08 12:21 Сейчас в теме
Замерил время выполнения, 10т записей
традиционный, (6) (10) 100%

3 ключа, значения от 1 до 100:
(3) от +40% до +50%
мой: от +250 до +300%

3 ключа, значения от 1 до 10:
(3) +400%
мой: +180%

1 ключ, значения от 1 до 10, от 1 до 100
(3) от + 20% до +40%
мой: +130%

Ст'оит ли красота времени?
7. begemot 270 14.01.08 09:17 Сейчас в теме
Как оригинальный вариант решения имеет право на существование...
Shaman100M; +1 Ответить
10. orefkov 1158 14.01.08 10:56 Сейчас в теме
Я бы это сделал так:
Код
ПредЗначение_Ключ_1   = ПолучитьПустоеЗначение(99);
ПредЗначение_Ключ_2   = ПолучитьПустоеЗначение(99);
...
ПредЗначение_Ключ_N   = ПолучитьПустоеЗначение(99);

ТЗ.Сортировать("Ключ_1,Ключ_2,...,Ключ_N");
ТЗ.НоваяСтрока();
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл 
    Если (ТЗ.Ключ_1<>ПредЗначение_Ключ_1) ИЛИ
      (ТЗ.Ключ_2<>ПредЗначение_Ключ_2) ИЛИ
      ...
      (ТЗ.Ключ_N<>ПредЗначение_Ключ_N) Тогда
      Если ТЗ.НомерСтроки > 1 Тогда
           // БЛОК 3 Заполнить подвал документа
           // БЛОК 4 Записать / вывести документ
      КонецЕсли;
      Если ТЗ.НомерСтроки = ТЗ.КоличествоСтрок() Тогда
         Прервать;
      КонецЕсли;
        // БЛОК 1 Создать новый документ
        // БЛОК 2 Заполнить заголовок документа
    КонецЕсли;
    // БЛОК 5 Заполнение строки спецификации документа
      
    ПредЗначение_Ключ_1   = ТЗ.Ключ_1; 
    ПредЗначение_Ключ_2   = ТЗ.Ключ_2;
    ...
    ПредЗначение_Ключ_N   = ТЗ.Ключ_N;
КонецЦикла;

Показать полностью
Shaman100M; +1 Ответить
11. Shaman100M 1155 14.01.08 11:41 Сейчас в теме
(10) а если ТЗ.ПолучитьЗначение(1,"Ключ_1") = ПолучитьПустоеЗначение() ?
19. orefkov 1158 15.01.08 09:57 Сейчас в теме
(11)
Ну, сделай
ключ1 = "bcad7cf3-2fd3-4286-a654-139b37840a79"
13. Shaman100M 1155 14.01.08 12:43 Сейчас в теме
Хотя мож и стоит. Сравнивается с голым циклом с парой-тройкой условий и присваиваний.
23. poppy 16.01.08 11:37 Сейчас в теме
Внесу свои пять копеек в решение задачи "Простой метод исключения дублирования кода в одном из часто используемых алгоритмов."

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


   СтрокаКолонок = "Ключ_1,Ключ_2,...,Ключ_N";
   
   СписокКлючей = СоздатьОбъект("СписокЗначений");
   СписокКлючей.ИзСтрокиСРазделителями("""" + СтрЗаменить(СтрокаКолонок, ",", """,""") + """");

      ТЗ.Сортировать(СтрокаКолонок);
      
   Флаг = 1;

   ТЗ.ВыбратьСтроки();
   Пока ТЗ.ПолучитьСтроку() = 1 Цикл 
      
      Если Флаг = 1 Тогда
         // БЛОК 1 Создать новый документ
         // БЛОК 2 Заполнить заголовок документа
      КонецЕсли;
      
      // БЛОК 5 Заполнение строки спецификации документа
      
      Флаг = ПроверитьСтрокуТаблицы(ТЗ, ТЗ.НомерСтроки, СписокКлючей);
   
      Если Флаг = 1 Тогда
         // БЛОК 3 Заполнить подвал документа
         // БЛОК 4 Записать / вывести документ
      КонецЕсли;
      
   КонецЦикла;
Показать полностью


Очевидно, что представленный код работает медленнее, чем при индексировании. Но ведь речь идет о простоте и понятности алгоритма и кода? Не так ли?
magics; Shaman100M; +2 Ответить
30. maloi_a 24.01.08 09:49 Сейчас в теме
(23)
Внесу свою копейку.
В Функции ПроверитьСтрокуТаблицы(ТЗ, СписокКлючей)
надо добавить объявление локальных переменных НомСтр и М1.
Перем НомСтр и М1;
Иначе может поменять глобальные переменные с такими именами.
31. poppy 24.01.08 09:59 Сейчас в теме
(30) Правильное замечание.

В (23) вкралась ошибка. В условиях вместо НомСтр должно быть написано НомерСтроки.

Получается, что локальными переменными нужно объявлять: Перем ии, Колонка.
32. Shaman100M 1155 24.01.08 10:07 Сейчас в теме
(30) нашел, таки, но про колонку забыл. ;)
Правило про переменные, - это классика. Стараюсь его выполнять. Ну, вряд ли кто-то додумается сделать глобальные переменные с такими короткими и простыми именами, но вероятность есть. Обычно добавляется префикс "гл".
(31) (23) в статье скорректировано, ошибка с НомСтр изначально была устранена. Кстати, зачем передавался НомерСтроки ?
33. poppy 25.01.08 00:19 Сейчас в теме
(32)
> Кстати, зачем передавался НомерСтроки ?

Наверно, для бОльшей универсальности. Ведь можно написать и так:

Код
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл 
   Если ПроверитьСтрокуТаблицы(ТЗ, ТЗ.НомерСтроки - 1, СписокКлючей) = 1 Тогда
      // БЛОК 1 Создать новый документ
      // БЛОК 2 Заполнить заголовок документа
   КонецЕсли;
   
   // БЛОК 5 Заполнение строки спецификации документа
   
   Если ПроверитьСтрокуТаблицы(ТЗ, ТЗ.НомерСтроки, СписокКлючей) = 1 Тогда
      // БЛОК 3 Заполнить подвал документа
      // БЛОК 4 Записать / вывести документ
   КонецЕсли;
КонецЦикла;
Показать полностью
34. Shaman100M 1155 26.01.08 18:09 Сейчас в теме
(33) Да, действительно.
24. Shaman100M 1155 17.01.08 08:54 Сейчас в теме
Так. Включаем в статью?
25. poppy 17.01.08 11:33 Сейчас в теме
(24)
Я не против.

Можно еще описать решение (6). Считаю, что оно вполне справляется с поставленной в статье задаче.

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

З.Ы. Попробуй длинный комментарий к процедуре ИзмененияКлючейТЗ() сделать в несколько строк, а то он у меня на экран не помещается - неудобно читать статью. :-(
26. Shaman100M 1155 17.01.08 14:49 Сейчас в теме
(25) насчет (6) ( и (10) ) да, подсознательно фильтровал универсальные, но для полной картины включу. Можно, в принципе и его сделать универсальным, вынеся операции сравнения и присваивания в функции.
По комментарию - разве он у тебя не переносится на след. строки? Хотя, при копировании в конфигуратор лучше, чтобы строки были короткие.
27. poppy 18.01.08 02:45 Сейчас в теме
(26)
> По комментарию - разве он у тебя не переносится на след. строки?

Есть ощущение, что строки внутри конструкции ( code ) ( /code) не переносятся... :( В IE7

Но это претензия скорее к саппорту, чем к тебе. Сделай опиcание ИмяКолонкиНач и ИмяКолонкиКон в несколько строк, возможно, будет лучше...

З.Ы.
Решение описанное в (10) не стоит внимания, потому-что неправильное.
28. Shaman100M 1155 18.01.08 10:42 Сейчас в теме
(27) Чем оно неправильное? Решения (6) и (10) - одно и тоже, просто (10) дописано до конца, а в (6) код + идея.
29. poppy 18.01.08 16:52 Сейчас в теме
(28)
Да, действительно, решение (10) правильное. Я невнимательно его прочтала. Приношу извинения перед автором за напрасный поклеп. ;)
35. Shaman100M 1155 16.03.09 19:41 Сейчас в теме
Для отправки сообщения требуется регистрация/авторизация