рубрики: Язык программирования 1С | Дата: 13 июля, 2017
Скачать обработку с примерами из статьи: ValueTable.epf
Платформа: 8.3; Тип формы: управляемая.
В процессе разработки и отладки кода 1С очень часто используется такой объект как Таблица значений. Визуально его можно представить как обычную двумерную таблицу. Поэтому он очень легок для человеческого восприятия. А свойства и методы этого объекта позволяют работать как с его строками, так и с колонками. В этой статье рассмотрим как можно создавать таблицу значений средствами языка программирования 1С, а также наиболее часто встречающиеся приемы работы с ней.
В качестве примера давайте создадим простейшую таблицу значений в которой будет отображаться приход товара на склад.
Дата | Наименование товара | Количество |
---|---|---|
01.07.2017 | Ручка | 10 |
02.07.2017 | Карандаш | 7 |
03.07.2017 | Карандаш | 8 |
04.07.2017 | Лампа | 2 |
А потом рассмотрим различные приемы работы с ней.
Если открыть синтакс-помощник и посмотреть где доступна таблица значений, то мы увидим, что она недоступна на тонком клиенте. Соответственно при использовании управляемых форм мы сможем работать с ней только на сервере, т.е. в процедурах и функциях перед которыми установлена директива компиляции &НаСервере.
И конечно же мы не сможем вернуть таблицу значений из серверной функции в клиентскую.
Для программного создания таблицы значений нам необходимо с помощью конструктора создать сам объект, потом создать необходимые колонки, и наконец заполнить ее строками. Вот код функции которая создаст и вернет нам таблицу, которая приведена выше:
&НаСервере
Функция ЗаполнитьТаблицуЗначений()
//Создаем таблицу
ТаблицаТовары = Новый ТаблицаЗначений;
//Добавляем колонки
ТаблицаТовары.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата"));
ТаблицаТовары.Колонки.Добавить("НаименованиеТовара", Новый ОписаниеТипов("Строка"));
ТаблицаТовары.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
//Добавляем в таблицу строки
НоваяСтрока = ТаблицаТовары.Добавить();
НоваяСтрока.Дата = '20170701';
НоваяСтрока.НаименованиеТовара = "Ручка";
НоваяСтрока.Количество = 10;
НоваяСтрока = ТаблицаТовары.Добавить();
НоваяСтрока.Дата = '20170702';
НоваяСтрока.НаименованиеТовара = "Карандаш";
НоваяСтрока.Количество = 7;
НоваяСтрока = ТаблицаТовары.Добавить();
НоваяСтрока.Дата = '20170703';
НоваяСтрока.НаименованиеТовара = "Карандаш";
НоваяСтрока.Количество = 8;
НоваяСтрока = ТаблицаТовары.Добавить();
НоваяСтрока.Дата = '20170704';
НоваяСтрока.НаименованиеТовара = "Лампа";
НоваяСтрока.Количество = 2;
Возврат ТаблицаТовары;
КонецФункции
Очень часто при работе с таблицами значений возникает задача обхода всех ее строк. Делается это с помощью циклов. Причем можно это делать как с использованием коллекции строк таблицы значений, когда мы последовательно получаем строки таблицы значений из коллекции строк, так и с использованием счетчика, когда мы получаем строку таблицы по ее индексу. Как правило используется первый способ — с обходом коллекции строк:
Для каждого СтрокаТаблицы Из ТаблицаТовары Цикл
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = Строка(СтрокаТаблицы.Дата) + "; " + СтрокаТаблицы.НаименованиеТовара + "; " + СтрокаТаблицы.Количество;
Сообщение.Сообщить();
КонецЦикла;
Второй способ применяется гораздо реже, как правило когда первый не подходит по каким то причинам. Пример рассмотрим позднее, когда будем говорить про удаление строк из таблицы значений.
Метод Найти(ИскомоеЗначение, [СписокКолонок]) возвращает строку таблицы в которой есть поле с нужным нам значением. Если значение не найдено, возвращается Неопределено. Список колонок в которых будет производиться поиск указывать необязательно, в этом случае поиск будет производиться во всех полях таблицы значений. Данный метод как правило используется для проверки наличия в таблице строки с искомым значением. Например, в момент обхода в цикле какой-то коллекции, мы формируем таблицу значений в которой значения некоторого поля не должны повторяться дважды. Тогда мы с помощью метода Найти() проверяем наличие строки с этим значением, и если получили Неопределено, тогда добавляем новую строку, в противном случае не делаем ничего. Применительно к нашему примеру давайте сделаем поиск строки со значением «Карандаш»:
СтрокаТаблицы = ТаблицаТовары.Найти("Карандаш");
Если СтрокаТаблицы = Неопределено Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Товар не найден";
Сообщение.Сообщить();
Иначе
НомерСтроки = ТаблицаТовары.Индекс(СтрокаТаблицы) + 1;
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Товар найден в строке №" + НомерСтроки;
Сообщение.Сообщить();
КонецЕсли;
В исходной таблице у нас две строки с товаром «Карандаш», но найдена будет только первая из них. Поэтому данный метод не представляет особой ценности, если нам надо получить все строки с заданным значением. Следует понимать, что найденная строка — это по сути ссылка на строку таблицы значений, и если мы проделаем с ее полями какие-либо манипуляции, то это отразится на исходной таблице. Например если выполнить вот такой код:
СтрокаТаблицы = ТаблицаТовары.Найти("Карандаш");
СтрокаТаблицы.Количество = 100;
то мы обнаружим, что во второй строке нашей исходной таблицы соответствующим образом изменилось количество товара:
Дата | Наименование товара | Количество |
---|---|---|
01.07.2017 | Ручка | 10 |
02.07.2017 | Карандаш | 100 |
03.07.2017 | Карандаш | 8 |
04.07.2017 | Лампа | 2 |
В отличие от предыдущего метода, метод НайтиСтроки(СтруктураПоиска) возвращает не одну, а сразу все строки таблицы значений, которые удовлетворяют условию поиска. Точнее возвращается массив, каждый элемент которого представляет собой ссылку на строку таблицы значений. В качестве параметра метод использует структуру, где ключом является имя колонки, а значением — искомое значение. Удобно использовать, когда нам надо выбрать несколько строк по определенному условию, и проделать с ними какие-то действия. Допустим, нам надо в нашей таблице заменить наименование «Карандаш» на «Карандаш автоматический». В этом случае нам поможет следующий код:
СтруктураОтбора = Новый Структура;
СтруктураОтбора.Вставить("НаименованиеТовара", "Карандаш");
МассивСтрок = ТаблицаТовары.НайтиСтроки(СтруктураОтбора);
Для каждого СтрокаТаблицы Из МассивСтрок Цикл
СтрокаТаблицы.НаименованиеТовара = "Карандаш автоматический";
КонецЦикла;
Структура для поиска может быть и более сложной. Например, мы можем добавить еще одно условие по колонке Дата:
СтруктураОтбора = Новый Структура;
СтруктураОтбора.Вставить("НаименованиеТовара", "Карандаш");
СтруктураОтбора.Вставить("Дата", '20170703');
Очевидно, что мы будем рассматривать удаление строк из таблицы значений по какому-то условию. В противном случае у нас произойдет просто очистка всей таблицы, что не имеет особого смысла. И на первый взгляд эта операция кажется достаточно простой. Первое, что приходит в голову — это перебор в цикле всех строк, проверка на выполнение условия, и если условие выполняется, то удаление текущей строки. Но на самом деле такой способ не всегда отрабатывает корректно. Давайте убедимся в этом на нашем примере. Удалим из таблицы все записи с наименованием «Карандаш»:
Для каждого СтрокаТаблицы Из ТаблицаТовары Цикл
Если СтрокаТаблицы.НаименованиеТовара = "Карандаш" Тогда
ТаблицаТовары.Удалить(СтрокаТаблицы);
КонецЕсли;
КонецЦикла;
Но в итоге мы наблюдаем следующую картину
Дата | Наименование товара | Количество |
---|---|---|
01.07.2017 | Ручка | 10 |
03.07.2017 | Карандаш | 8 |
04.07.2017 | Лампа | 2 |
То есть у нас удалилась только одна строка вместо двух. Такое происходит когда строки, удовлетворяющие условию, следуют сразу же одна за другой. В этом случае при удалении первой строки, которая удовлетворяет условию, происходит сдвиг всех следующих за ней строк на одну вверх. Но курсор в выборке уже установлен на следующей строке, поэтому строка следующая за удаленной и занявшая ее место никак не обрабатывается. Что же делать в таком случае? Я обычно использую также обход строк в цикле, но не сверху вниз, а наоборот — снизу вверх. В этом случае сдвиг строк не страшен, т.к. нижние строки уже обработаны. При этом приходится вместо обхода коллекции строк использовать получение строки по индексу в цикле со счетчиком. Вот таким образом:
Индекс = ТаблицаТовары.Количество() - 1;
Пока Индекс >= 0 Цикл
СтрокаТаблицы = ТаблицаТовары.Получить(Индекс);
Если СтрокаТаблицы.НаименованиеТовара = "Карандаш" Тогда
ТаблицаТовары.Удалить(СтрокаТаблицы);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
И наконец попробуем с помощью метода НайтиСтроки() сразу получить все нужные строки, и удалить их поочередно:
СтруктураОтбора = Новый Структура;
СтруктураОтбора.Вставить("НаименованиеТовара", "Карандаш");
МассивСтрок = ТаблицаТовары.НайтиСтроки(СтруктураОтбора);
Для каждого СтрокаТаблицы Из МассивСтрок Цикл
ТаблицаТовары.Удалить(СтрокаТаблицы);
КонецЦикла;
И в результате получаем:
Дата | Наименование товара | Количество |
---|---|---|
01.07.2017 | Ручка | 10 |
04.07.2017 | Лампа | 2 |
То есть такой метод тоже работает.
Допустим, что нас больше не интересует дата, и мы хотим получить по каждому наименованию общее количество. То есть привести исходную таблицу вот к такому виду:
Наименование товара | Количество |
---|---|
Ручка | 10 |
Карандаш | 15 |
Лампа | 2 |
В этом случае нам надо сгруппировать строки таблицы. Напомню, что группировка в языке запросов также активно используется. А в таблице значений для этого используется метод Свернуть(КолонкиГруппировок, КолонкиСуммирования). В общем то, чтобы достигнуть результата нам потребуется всего одна строчка кода:
ТаблицаТовары.Свернуть("НаименованиеТовара", "Количество");
Таблица значений может сериализоваться с помощью объекта СериализаторXDTO. Проще говоря таблицу значений можно записать в формате XML или JSON, что открывает широкие возможности для передачи таблиц значений в качестве параметров при обмене данными. В том числе и через веб-сервисы.Рассмотрим использование обоих форматов.
В качестве примера выгрузим таблицу значений в строку XML, а потом создадим копию таблицы выполнив загрузку из строки XML:
ЗаписьХМЛ = Новый ЗаписьXML;
ЗаписьХМЛ.УстановитьСтроку();
СериализаторXDTO.ЗаписатьXML(ЗаписьХМЛ, ТаблицаТовары);
СтрокаХМЛ = ЗаписьХМЛ.Закрыть();
ЧтениеХМЛ = Новый ЧтениеXML;
ЧтениеХМЛ.УстановитьСтроку(СтрокаХМЛ);
ТаблицаКопияХМЛ = СериализаторXDTO.ПрочитатьXML(ЧтениеХМЛ);
ЧтениеХМЛ.Закрыть();
Начиная с версии платформы 8.3.7.1759 появилась возможность сериализации прикладных типов 1С:Предприятия в JSON. Принцип использования тот же, что и для XML:
ЗаписьJS = Новый ЗаписьJSON;
ЗаписьJS.УстановитьСтроку();
СериализаторXDTO.ЗаписатьJSON(ЗаписьJS, ТаблицаТовары, НазначениеТипаXML.Явное);
СтрокаJS = ЗаписьJS.Закрыть();
ЧтениеJS = Новый ЧтениеJSON;
ЧтениеJS.УстановитьСтроку(СтрокаJS);
ТаблицаКопияJS = СериализаторXDTO.ПрочитатьJSON(ЧтениеJS);
ЧтениеJS.Закрыть();
Хочу обратить внимание на параметр НазначениеТипаXML.Явное в методе ЗаписатьJSON без кот
орого при чтении из строки будет выдаваться ошибка, либо надо будет при чтении указать тип объекта.
Добавить комментарий