Долой дубли!

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 Записать / вывести документ НовДок
КонецЦикла;


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

См. также

Результаты ревью кода 1500+ решений каталога Инфостарт: наиболее частые ошибки разработчиков в коде

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

Поделюсь своим опытом аудита кода авторских продуктов с Infostart.ru как одним из элементов применения DevOps-практик внутри Инфостарт. Будет настоящий код, боевые скриншоты, внутренние мемы от команды ИТ-лаборатории Инфостарт и прочее мясо – все, что любят разработчики.

10.04.2024    4783    artbear    70    

70

Ниндзя-код

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

Предлагаю вашему вниманию советы мастеров древности. Программисты прошлого использовали их, чтобы заострить разум тех, кто после них будет поддерживать код. Гуру разработки при найме старательно ищут их применение в тестовых заданиях. Новички иногда используют их ещё лучше, чем матёрые ниндзя. Прочитайте их и решите, кто вы: ниндзя, новичок или, может быть, гуру? (Адаптация статьи "Ниндзя-код" из учебника JavaScript)

01.04.2024    2247    DrAku1a    15    

33

Практическое программирование: когда скорость важнее совершенства

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

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

01.04.2024    581    Prepod2003    6    

2

Когда понадобился новый оператор

Рефакторинг и качество кода Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Когда понадобился новый оператор, но его нет в синтакс-помощнике, что делать?

18.03.2024    1339    ZhokhovM    4    

4

Когда разработчик платформы не добавил проверку препроцессоров

Рефакторинг и качество кода Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Когда разработчик платформы решил пойти на кухню за кофе, а проверку препроцессоров не добавил, и вот тут-то и началось: "Что, опять все сломалось? Ну и кофе же я забыл сделать!".😅

18.03.2024    2976    ZhokhovM    4    

9

Реструктуризация - бесконечная история

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

При разработке программ требуемый функционал ставят на первое место, но есть еще и архитектура программы. На горизонте 5-10 лет она становится важнее функционала, который должен работать при масштабировании и росте данных. Реструктуризация 5 терабайтной базы 1С 8.2 в формат 1С 8.3, складывает весь пазл архитектурных просчетов, которые сделали ради функционала. Как это исправить? - для разработки правильной архитектуры, нужно всего лишь сместить фокус с функционала и подумать о «вечном».

29.09.2023    2059    1CUnlimited    15    

23
Комментарии
Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. CheBurator 3119 12.01.08 20:31 Сейчас в теме
неэстетично...
имхается проще и красивше рекурсивную процедуру заюзать...
8. Shaman100M 1150 14.01.08 09:57 Сейчас в теме
(1) (2) Рекурсивная, скорее всего, на сравнение и заполнение строк ТЧ. Иногда может получиться громоздко, т.к. нужно хранить / передавать в процедуру(ы) ключи и все, что там необходимо будет сделать. Я попытался реализовать одной вспомогательной универсальной процедурой с четко определенными параметрами.
(3) +1 хороший индекс! Единственное, добавил бы после свертки ТЗИндекс
Код
 ТЗИндекс.Сортировать(СтрокаКолонок); // Сортировка после Свернуть() может сбиться. 
Показать полностью
14. int3 14.01.08 13:57 Сейчас в теме
(8) Согласен, никто не гарантирует порядка после свертки, правда ни разу не сталкивался с такой ситуацией :-/ (Хотя в реальных алгоритмах дополнительную сортировку включаю в код - надежность дороже ;-) )
(12) такой индекс даст прирост производительности на больших объемах данных, т.е. когда сложность условия в цикле превысит накладные расходы на индексирование, т.е. к примеру при десятке ключей на сотне тысяч записей
ну или иначе - при варировании ключевых условий, тут уже выйдет на первое место гибкость :-/
17. Shaman100M 1150 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 1150 14.01.08 15:41 Сейчас в теме
(15) Замерял не в отладчике, а с пом. _GetPerformanceCounter() . Отладчик показывает +100% Максимум времени ушло на Свернуть()
18. int3 15.01.08 08:30 Сейчас в теме
(16) хм... Такая картина - несовпадение результатов разных методов замера наверное объясняется "особенностями" работы платформы, в частности уборщика мусора.
(17) т.е. сфера применения у него найдется, и не только в плане "красивости" кода? буду рад, если пригодится :)
когда-то на проклабе обнаружил конкурс на самый быстрый метод удаления строк из ТЗ (правда он к тому времени уже закончился), заинтересовался и написал свой вариант с использованием такого индексирования - занятный вариант получился, но не без "особенностей" :)
20. Shaman100M 1150 15.01.08 12:49 Сейчас в теме
(18) да, полезный конкурс, столько особенностей при оптимизации обнаружилось, вплоть до наличия запятой в НоваяКолонка() :)
(19) )))) Ну тогда какое-нить другое красивое слово.
21. Shaman100M 1150 15.01.08 12:53 Сейчас в теме
(18) если не против, добавлю в статью с ссылкой.
22. int3 15.01.08 13:53 Сейчас в теме
4. tarasenkov 340 14.01.08 09:06 Сейчас в теме
А не проще написать так:
Код
Если (ТЗ.НомерСтроки    = 1) ИЛИ
(ТЗ.Ключ_1<>ПредЗначение_Ключ_1) ИЛИ
(ТЗ.Ключ_2<>ПредЗначение_Ключ_2) ИЛИ
...
(ТЗ.Ключ_N<>ПредЗначение_Ключ_N) Тогда

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

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

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

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


Дублирование записи документа/вывода печатной формы после цикла не так страшно.
Хотя тоже можно избежать добавив в таблицу фиктивную последнюю строку.
Shaman100M; +1 Ответить
12. Shaman100M 1150 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 269 14.01.08 09:17 Сейчас в теме
Как оригинальный вариант решения имеет право на существование...
Shaman100M; +1 Ответить
10. orefkov 1152 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 1150 14.01.08 11:41 Сейчас в теме
(10) а если ТЗ.ПолучитьЗначение(1,"Ключ_1") = ПолучитьПустоеЗначение() ?
19. orefkov 1152 15.01.08 09:57 Сейчас в теме
(11)
Ну, сделай
ключ1 = "bcad7cf3-2fd3-4286-a654-139b37840a79"
13. Shaman100M 1150 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 1150 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 1150 26.01.08 18:09 Сейчас в теме
24. Shaman100M 1150 17.01.08 08:54 Сейчас в теме
Так. Включаем в статью?
25. poppy 17.01.08 11:33 Сейчас в теме
(24)
Я не против.

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

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

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

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

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

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