////////////////////////////////////////////////////////////////////////////////
// Модуль: A1sDS
// Назначение: Упрощённая работа со структурами и парами ключ-значение
// Версия: 2.0
// Автор: A1sCode
// Сайт: https://a1scode.ru
// Лицензия: MIT
// Совместимость: 1С:Предприятие 8.3+
////////////////////////////////////////////////////////////////////////////////
#Область ФабрикиСтруктур
// Of, OfFixed, OfKeys
//
// Создает структуру из произвольного количества пар ключ-значение (до 20).
// Первый ключ
// Первое значение
// ...
// Двадцатый ключ
// Двадцатое значение
// Structure — новая структура с переданными парами
//
//
// // A1sDS.Of("name", "John", "age", 30) -> {name: "John", age: 30}
// // A1sDS.Of("x", 10, "y", 20, "color", "red") -> {x: 10, y: 20, color: "red"}
//
//
Функция Of(
Ключ1 = Неопределено, Знач1 = Неопределено,
Ключ2 = Неопределено, Знач2 = Неопределено,
Ключ3 = Неопределено, Знач3 = Неопределено,
Ключ4 = Неопределено, Знач4 = Неопределено,
Ключ5 = Неопределено, Знач5 = Неопределено,
Ключ6 = Неопределено, Знач6 = Неопределено,
Ключ7 = Неопределено, Знач7 = Неопределено,
Ключ8 = Неопределено, Знач8 = Неопределено,
Ключ9 = Неопределено, Знач9 = Неопределено,
Ключ10 = Неопределено, Знач10 = Неопределено,
Ключ11 = Неопределено, Знач11 = Неопределено,
Ключ12 = Неопределено, Знач12 = Неопределено,
Ключ13 = Неопределено, Знач13 = Неопределено,
Ключ14 = Неопределено, Знач14 = Неопределено,
Ключ15 = Неопределено, Знач15 = Неопределено,
Ключ16 = Неопределено, Знач16 = Неопределено,
Ключ17 = Неопределено, Знач17 = Неопределено,
Ключ18 = Неопределено, Знач18 = Неопределено,
Ключ19 = Неопределено, Знач19 = Неопределено,
Ключ20 = Неопределено, Знач20 = Неопределено
) Экспорт //⚙
// ib = "Создание структуры из пар ключ-значение"; //✍
Структура = Новый Структура(); //✏
Если ТипЗнч(Ключ1) = Тип("Строка") Тогда Структура.Вставить(Ключ1, Знач1); КонецЕсли;
Если ТипЗнч(Ключ2) = Тип("Строка") Тогда Структура.Вставить(Ключ2, Знач2); КонецЕсли;
Если ТипЗнч(Ключ3) = Тип("Строка") Тогда Структура.Вставить(Ключ3, Знач3); КонецЕсли;
Если ТипЗнч(Ключ4) = Тип("Строка") Тогда Структура.Вставить(Ключ4, Знач4); КонецЕсли;
Если ТипЗнч(Ключ5) = Тип("Строка") Тогда Структура.Вставить(Ключ5, Знач5); КонецЕсли;
Если ТипЗнч(Ключ6) = Тип("Строка") Тогда Структура.Вставить(Ключ6, Знач6); КонецЕсли;
Если ТипЗнч(Ключ7) = Тип("Строка") Тогда Структура.Вставить(Ключ7, Знач7); КонецЕсли;
Если ТипЗнч(Ключ8) = Тип("Строка") Тогда Структура.Вставить(Ключ8, Знач8); КонецЕсли;
Если ТипЗнч(Ключ9) = Тип("Строка") Тогда Структура.Вставить(Ключ9, Знач9); КонецЕсли;
Если ТипЗнч(Ключ10) = Тип("Строка") Тогда Структура.Вставить(Ключ10, Знач10); КонецЕсли;
Если ТипЗнч(Ключ11) = Тип("Строка") Тогда Структура.Вставить(Ключ11, Знач11); КонецЕсли;
Если ТипЗнч(Ключ12) = Тип("Строка") Тогда Структура.Вставить(Ключ12, Знач12); КонецЕсли;
Если ТипЗнч(Ключ13) = Тип("Строка") Тогда Структура.Вставить(Ключ13, Знач13); КонецЕсли;
Если ТипЗнч(Ключ14) = Тип("Строка") Тогда Структура.Вставить(Ключ14, Знач14); КонецЕсли;
Если ТипЗнч(Ключ15) = Тип("Строка") Тогда Структура.Вставить(Ключ15, Знач15); КонецЕсли;
Если ТипЗнч(Ключ16) = Тип("Строка") Тогда Структура.Вставить(Ключ16, Знач16); КонецЕсли;
Если ТипЗнч(Ключ17) = Тип("Строка") Тогда Структура.Вставить(Ключ17, Знач17); КонецЕсли;
Если ТипЗнч(Ключ18) = Тип("Строка") Тогда Структура.Вставить(Ключ18, Знач18); КонецЕсли;
Если ТипЗнч(Ключ19) = Тип("Строка") Тогда Структура.Вставить(Ключ19, Знач19); КонецЕсли;
Если ТипЗнч(Ключ20) = Тип("Строка") Тогда Структура.Вставить(Ключ20, Знач20); КонецЕсли;
Возврат Структура; //↩
КонецФункции
//
//
// Создает фиксированную (неизменяемую) структуру из пар ключ-значение (до 20).
//
// Первый ключ
// Первое значение
// ...
// Двадцатый ключ
// Двадцатое значение
// FixedStructure — неизменяемая структура
//
//
// // Константы = A1sDS.OfFixed("PI", 3.14159, "E", 2.71828);
// // Константы.PI = 3; // Ошибка! Изменение запрещено
//
//
Функция OfFixed(
Ключ1 = Неопределено, Знач1 = Неопределено,
Ключ2 = Неопределено, Знач2 = Неопределено,
Ключ3 = Неопределено, Знач3 = Неопределено,
Ключ4 = Неопределено, Знач4 = Неопределено,
Ключ5 = Неопределено, Знач5 = Неопределено,
Ключ6 = Неопределено, Знач6 = Неопределено,
Ключ7 = Неопределено, Знач7 = Неопределено,
Ключ8 = Неопределено, Знач8 = Неопределено,
Ключ9 = Неопределено, Знач9 = Неопределено,
Ключ10 = Неопределено, Знач10 = Неопределено,
Ключ11 = Неопределено, Знач11 = Неопределено,
Ключ12 = Неопределено, Знач12 = Неопределено,
Ключ13 = Неопределено, Знач13 = Неопределено,
Ключ14 = Неопределено, Знач14 = Неопределено,
Ключ15 = Неопределено, Знач15 = Неопределено,
Ключ16 = Неопределено, Знач16 = Неопределено,
Ключ17 = Неопределено, Знач17 = Неопределено,
Ключ18 = Неопределено, Знач18 = Неопределено,
Ключ19 = Неопределено, Знач19 = Неопределено,
Ключ20 = Неопределено, Знач20 = Неопределено
) Экспорт //⚙
// ib = "Создание фиксированной структуры из пар"; //✍
Структура = Of(Ключ1, Знач1, Ключ2, Знач2, Ключ3, Знач3, Ключ4, Знач4, Ключ5, Знач5,
Ключ6, Знач6, Ключ7, Знач7, Ключ8, Знач8, Ключ9, Знач9, Ключ10, Знач10,
Ключ11, Знач11, Ключ12, Знач12, Ключ13, Знач13, Ключ14, Знач14, Ключ15, Знач15,
Ключ16, Знач16, Ключ17, Знач17, Ключ18, Знач18, Ключ19, Знач19, Ключ20, Знач20); //▶️
Возврат Новый ФиксированнаяСтруктура(Структура); //↩
КонецФункции
//
// Создает структуру из массива или строки ключей, заполняя все одним значением. ✦
// Строка ключей через запятую или массив ключей ➤
// Значение по умолчанию для всех ключей ➤
// Structure — новая структура ⬅
//
//
// // A1sDS.OfKeys("x,y,z", 0) -> {x: 0, y: 0, z: 0}
//
//
Функция OfKeys(Keys, DefaultValue) Экспорт //⚙
ib = "Создание структуры из списка ключей с одним значением"; //✍
Результат = Новый Структура(); //✏
// ✅ FIX: Добавили Ложь для единообразия с Zip и Patch
Если ТипЗнч(Keys) = Тип("Строка") Тогда
МассивКлючей = СтрРазделить(Keys, ",", Ложь); //✏
ИначеЕсли ТипЗнч(Keys) = Тип("Массив") Тогда //⚡
МассивКлючей = Keys; //✏
Иначе
//ВызватьИсключение "A1sDS.OfKeys: параметр Keys должен быть типа Строка или Массив"; //✏
КонецЕсли;
Для Каждого Ключ Из МассивКлючей Цикл //⟳
Ключ = СокрЛП(Ключ); //✏
// ✅ FIX: Проверка на заполненность все еще нужна (на случай, если передали массив с мусором)
Если ЗначениеЗаполнено(Ключ) Тогда //⚡
Результат.Вставить(Ключ, DefaultValue); //✏
КонецЕсли;
КонецЦикла;
Возврат Результат; //↩
КонецФункции
#КонецОбласти
#Область Парсинг
// OfQueryString, Zip
//
// Создает структуру из QueryString (URL параметров): "name=John&age=30". ✦
// Строка параметров URL ➤
// Structure — структура с параметрами ⬅
//
//
// // A1sDS.OfQueryString("name=John&age=30&city=New%20York")
// // -> {name: "John", age: "30", city: "New York"}
//
//
Функция OfQueryString(QueryString) Экспорт //⚙
ib = "Разбор QueryString в структуру"; //✍
Результат = Новый Структура(); //✏
Параметры = СтрРазделить(QueryString, "&"); //✏
Для Каждого ПараметрСтрока Из Параметры Цикл //⟳
Пара = СтрРазделить(ПараметрСтрока, "="); //✏
Если Пара.Количество() = 2 Тогда //⚡
Ключ = СокрЛП(Пара[0]); //✏
Значение = СокрЛП(Пара[1]); //✏
// URL-декодировка частых случаев
Значение = СтрЗаменить(Значение, "%20", " "); //✏
Значение = СтрЗаменить(Значение, "%2B", "+"); //✏
Значение = СтрЗаменить(Значение, "%26", "&"); //✏
Результат.Вставить(Ключ, Значение); //✏
КонецЕсли;
КонецЦикла;
Возврат Результат; //↩
КонецФункции
//
//
// Создает структуру из строки ключей или массива ключей и массива значений, объединяя их попарно (Zip).
//
//
// Строка ключей, разделенных запятыми, или массив ключей.
//
//
// Массив значений. Обязательный параметр.
//
// Structure — новая структура
//
//
// A1sDS.Zip("x,y,z", A1sAR.Of(1, 2, 3)) -> {x: 1, y: 2, z: 3}
// A1sDS.Zip(A1sAR.Of("a","b"), A1sAR.Of(10,20)) -> {a: 10, b: 20}
//
//
Функция Zip(Ключи, Значения) Экспорт
// 1. Валидация значений (строго Массив)
Если ТипЗнч(Значения) <> Тип("Массив") Тогда
Возврат Новый Структура;
КонецЕсли;
// 2. Получение массива ключей (Объявление происходит прямо при присваивании)
Если ТипЗнч(Ключи) = Тип("Строка") Тогда
МассивКлючей = СтрРазделить(Ключи, ",", Ложь);
ИначеЕсли ТипЗнч(Ключи) = Тип("Массив") Тогда
МассивКлючей = Ключи;
Иначе
Возврат Новый Структура;
КонецЕсли;
// 3. Защита от пустых массивов (чтобы не уйти в -1)
Лимит = Мин(МассивКлючей.Количество(), Значения.Количество());
Если Лимит = 0 Тогда
Возврат Новый Структура;
КонецЕсли;
Результат = Новый Структура();
// 4. Zip
Для Индекс = 0 По Лимит - 1 Цикл
Ключ = СокрЛП(МассивКлючей[Индекс]);
Если ЗначениеЗаполнено(Ключ) Тогда
Результат.Вставить(Ключ, Значения[Индекс]);
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
//
//
// Применяет патч (набор изменений) к существующей структуре.
// Обновляет значения существующих ключей и добавляет новые.
//
// Целевая структура (изменяется "на месте").
// Строка ключей через запятую или массив ключей.
// Массив новых значений для патча.
// Structure — ссылка на целевую структуру (для цепочек).
//
//
// Options = A1sDS.Of("x", 10, "y", 25);
// A1sDS.Patch(Options, "y,z", A1sAR.Of(20, 30));
// // Результат: {x: 10, y: 20, z: 30} — y обновлен, z добавлен
//
//
Функция Patch(Target, Keys, Values) Экспорт
// 1. Валидация значений (строго Структура)
Если ТипЗнч(Target) <> Тип("Структура") Тогда
Возврат Новый Структура;
КонецЕсли;
Если ТипЗнч(Values) <> Тип("Массив") Тогда
Возврат Target;
КонецЕсли;
// 2. Парсинг массива ключей (ИСПРАВЛЕНО: МассивКлючей вместо MappedKeys)
Если ТипЗнч(Keys) = Тип("Строка") Тогда
МассивКлючей = СтрРазделить(Keys, ",", Ложь);
ИначеЕсли ТипЗнч(Keys) = Тип("Массив") Тогда
МассивКлючей = Keys;
Иначе
Возврат Target;
КонецЕсли;
// 3. Защита от пустых массивов (чтобы не уйти в -1)
Limit = Мин(МассивКлючей.Количество(), Values.Количество());
// 4. Patch
Для Индекс = 0 По Limit - 1 Цикл
Ключ = СокрЛП(МассивКлючей[Индекс]);
Если ЗначениеЗаполнено(Ключ) Тогда
Target.Вставить(Ключ, Values[Индекс]);
КонецЕсли;
КонецЦикла;
Возврат Target;
КонецФункции
#КонецОбласти
#Область СелекцияИТрансформация
//
// Выбирает из структуры только указанные ключи (Picker). ✦
// Исходная структура ➤
// Строка нужных ключей через запятую ➤
// Structure — новая структура с выбранными полями ⬅
//
//
// user = A1sDS.Of("name", "John", "age", 30, "password", "secret");
// public = A1sDS.Pick(user, "name,age");
// // -> {name: "John", age: 30}
//
//
Функция Pick(SourceStruct, KeysString) Экспорт //⚙ Переименовано с OfPick
ib = "Выборка ключей из структуры"; //✍
НужныеКлючи = СтрРазделить(KeysString, ","); //✏
Результат = Новый Структура(); //✏
Для Каждого КлючСтрока Из НужныеКлючи Цикл //⟳
Ключ = СокрЛП(КлючСтрока); //✏
Если ЗначениеЗаполнено(Ключ) И SourceStruct.Свойство(Ключ) Тогда //⚡
Результат.Вставить(Ключ, SourceStruct[Ключ]); //✏
КонецЕсли;
КонецЦикла;
Возврат Результат; //↩
КонецФункции
//
// Исключает из структуры указанные ключи. ✦
// Исходная структура ➤
// Строка исключаемых ключей через запятую ➤
// Structure — новая структура без исключенных полей ⬅
//
//
// user = A1sDS.Of("name", "John", "age", 30, "password", "secret");
// safe = A1sDS.Omit(user, "password");
// // -> {name: "John", age: 30}
//
//
Функция Omit(SourceStruct, ExcludeKeysString) Экспорт //⚙ Переименовано с OfOmit
ib = "Исключение ключей из структуры"; //✍
ИсключаемыеКлючи = СтрРазделить(ExcludeKeysString, ","); //✏
СоответствиеИсключений = Новый Соответствие(); //✏
Для Каждого КлючСтрока Из ИсключаемыеКлючи Цикл //⟳
Ключ = СокрЛП(КлючСтрока); //✏
Если ЗначениеЗаполнено(Ключ) Тогда //⚡
СоответствиеИсключений.Вставить(Ключ, Истина); //✏
КонецЕсли;
КонецЦикла;
Результат = Новый Структура(); //✏
Для Каждого Пара Из SourceStruct Цикл //⟳
Если СоответствиеИсключений[Пара.Ключ] = Неопределено Тогда //⚡
Результат.Вставить(Пара.Ключ, Пара.Значение); //✏
КонецЕсли;
КонецЦикла;
Возврат Результат; //↩
КонецФункции
//
// Создает новую структуру, преобразуя значения исходной через выражение. ✦
// Исходная структура ➤
// Выражение для вычисления (доступна переменная "Значение") ➤
// ⬅ Structure — новая структура с преобразованными значениями
///
///
/// User = A1sDS.Of("Salary", 50000, "Bonus", 10000);
/// // Добавим всем 20% к зарплате (грубо)
/// Updated = A1sDS.Map(User, "Значение * 1.2");
/// // Updated: {Salary: 60000, Bonus: 12000}
///
///
Функция Map(SourceStruct, ValueExpression) Экспорт
ib = "Трансформация значений структуры"; //✍
Результат = Новый Структура; //✏
Для Каждого Пара Из SourceStruct Цикл //⟳
Ключ = Пара.Ключ; //✏
Значение = Пара.Значение; //✏
// Безопасное вычисление
НовоеЗначение = Неопределено; //✏
Попытка
НовоеЗначение = Вычислить(ValueExpression); //⚡
Исключение
// Ошибка в выражении - оставляем старое значение
НовоеЗначение = Значение;
КонецПопытки;
Результат.Вставить(Ключ, НовоеЗначение); //✏
КонецЦикла;
Возврат Результат; //↩
КонецФункции
//
// Фильтрует структуру по условию на значения. ✦
// Исходная структура ➤
// Условие (доступна переменная "Значение") ➤
// ⬅ Structure — новая структура с подходящими парами
///
///
/// Goods = A1sDS.Of("Apple", 10, "Banana", 5, "BadApple", -1);
/// // Оставим только то, что больше 0
/// ValidGoods = A1sDS.Filter(Goods, "Значение > 0");
/// // ValidGoods: {Apple: 10, Banana: 5}
///
///
Функция Filter(SourceStruct, ConditionExpression) Экспорт
ib = "Фильтрация структуры по значению"; //✍
Результат = Новый Структура; //✏
Для Каждого Пара Из SourceStruct Цикл //⟳
Ключ = Пара.Ключ; //✏
Значение = Пара.Значение; //✏
УсловиеВыполнено = Ложь; //✏
Попытка
УсловиеВыполнено = Вычислить(ConditionExpression); //⚡
Исключение
// Если выражение кривое - пропускаем элемент (безопаснее)
Продолжить; //✖
КонецПопытки;
Если УсловиеВыполнено Тогда //⚡
Результат.Вставить(Ключ, Значение); //✏
КонецЕсли;
КонецЦикла;
Возврат Результат; //↩
КонецФункции
// Создает новую структуру с переименованными ключами. ✦
// Исходная структура ➤
// Карта переименования: {старое_имя: "новое_имя"} ➤
// Structure — структура с переименованными ключами ⬅
//
//
// user = A1sDS.Of("first_name", "John", "last_name", "Doe");
// map = A1sDS.Of("first_name", "имя", "last_name", "фамилия");
// result = A1sDS.Rename(user, map);
// // -> {имя: "John", фамилия: "Doe"}
//
//
Функция Rename(SourceStruct, RenameMap) Экспорт //⚙ Переименовано с OfRename
ib = "Переименование ключей структуры"; //✍
Результат = Новый Структура(); //✏
Для Каждого Пара Из SourceStruct Цикл //⟳
НовоеИмя = Пара.Ключ; //✏
Если RenameMap.Свойство(Пара.Ключ) Тогда //⚡
НовоеИмя = RenameMap[Пара.Ключ]; //✏
КонецЕсли;
Результат.Вставить(НовоеИмя, Пара.Значение); //✏
КонецЦикла;
Возврат Результат; //↩
КонецФункции
#КонецОбласти
#Область ДоступИЛениваяИнициализация
//
// Возвращает значение по ключу из Структуры; если ключа нет — вставляет умолчание и возвращает его.
// Целевая структура (изменяется "на месте").
// Имя ключа.
// Значение по умолчанию (вставляется как есть).
// Variant — существующее значение или вставленное умолчание.
//
//
// Prefs = Новый Структура;
// Dark = A1sDS.GetOrInsert(Prefs, "useDarkmode", Истина); // запишет Истина один раз
//
//
Функция GetOrInsert(Struct, Key, DefaultValue) Экспорт
Если ТипЗнч(Struct) <> Тип("Структура") Тогда
ВызватьИсключение "A1sDS.GetOrInsert: параметр Struct должен быть типа Структура";
КонецЕсли;
Если Struct.Свойство(Key) Тогда
Возврат Struct[Key];
КонецЕсли;
Struct.Вставить(Key, DefaultValue);
Возврат DefaultValue;
КонецФункции
//
// Ленивая версия: если ключ отсутствует — вычисляет выражение строкой, вставляет и возвращает.
// Целевая структура (изменяется "на месте").
// Имя ключа.
// BSL-выражение, которое вернёт значение (выполняется ТОЛЬКО если ключ отсутствует).
// Variant — существующее значение или вычисленное и вставленное.
//
//
// Cache = Новый Структура;
// // Компилируем дорогую регулярку лишь однажды:
// Rx = A1sDS.GetOrInsertEval(Cache, "rxINN", "Новый РегулярноеВыражение(""^\d{10}(\d{2})?$"")");
//
// Используйте с осторожностью: Вычислить() работает медленно, если вызывать часто.
//
Функция GetOrInsertEval(Struct, Key, ValueExpr) Экспорт
Если ТипЗнч(Struct) <> Тип("Структура") Тогда
ВызватьИсключение "A1sDS.GetOrInsertEval: параметр Struct должен быть типа Структура";
КонецЕсли;
Если Struct.Свойство(Key) Тогда
Возврат Struct[Key];
КонецЕсли;
Значение = Вычислить(ValueExpr);
Struct.Вставить(Key, Значение);
Возврат Значение;
КонецФункции
//
// Вставляет умолчание по «пути ключей» в Структуре (создаёт промежуточные узлы) и возвращает конечное значение.
// Целевая структура (изменяется "на месте").
// Путь вида "ui.theme.dark" (разделитель по умолчанию ".").
// Умолчание для конечного ключа.
// Разделитель пути.
// Variant — существующее конечное значение или вставленное умолчание.
//
//
// Prefs = Новый Структура;
// // Создаст: ui = { theme = { dark = Истина } }
// A1sDS.GetOrInsertPath(Prefs, "ui.theme.dark", Истина);
//
//
Функция GetOrInsertPath(Struct, Path, DefaultValue, Sep = ".") Экспорт
Если ТипЗнч(Struct) <> Тип("Структура") Тогда
ВызватьИсключение "A1sDS.GetOrInsertPath: параметр Struct должен быть типа Структура";
КонецЕсли;
parts = СтрРазделить(Path, Sep, Ложь);
count = parts.Количество();
Если count = 0 Тогда
Возврат DefaultValue;
КонецЕсли;
node = Struct;
Если count > 1 Тогда
Для i = 0 По count - 2 Цикл
k = СокрЛП(parts[i]);
Если НЕ node.Свойство(k) Тогда
node.Вставить(k, Новый Структура);
ИначеЕсли ТипЗнч(node[k]) <> Тип("Структура") Тогда
// Если по пути уже лежит не-структура — аккуратно заменяем на узел-структуру
node[k] = Новый Структура;
КонецЕсли;
node = node[k];
КонецЦикла;
КонецЕсли;
// Последний сегмент
k = СокрЛП(parts[count - 1]);
Если node.Свойство(k) Тогда
Возврат node[k];
КонецЕсли;
node.Вставить(k, DefaultValue);
Возврат DefaultValue;
КонецФункции
#КонецОбласти
#Область МодификацияИСлияние
// AddIf, Defaults, Copy, Merge
//
// Добавляет свойство в структуру по условию. ✦
// Структура для изменения ➤
// Имя свойства ➤
// Значение при истинном условии ➤
// Условие добавления свойства ➤
// Значение при ложном условии ➤
// Structure — переданная структура с добавленным свойством или без изменений ⬅
//
//
// // struct = A1sDS.Of("name", "John");
// // A1sDS.AddIf(struct, "age", 30, Истина, 0);
// // -> {name: "John", age: 30}
//
//
Функция AddIf(Структура, Ключ, ЗначениеЕслиИстина, Условие = Истина, ЗначениеЕслиЛожь = Неопределено) Экспорт //⚙
ib = "Добавление свойства по условию"; //✍
Если Условие Тогда //⚡
Структура.Вставить(Ключ, ЗначениеЕслиИстина); //✏
ИначеЕсли ЗначениеЕслиЛожь <> Неопределено Тогда //⚡
Структура.Вставить(Ключ, ЗначениеЕслиЛожь); //✏
КонецЕсли;
Возврат Структура; //↩
КонецФункции
//
// Создает структуру, объединив данные с заданными значениями по умолчанию. ✦
// Исходная структура ➤
// Структура значений по умолчанию ➤
// Structure — новая структура с примененными значениями по умолчанию ⬅
//
//
// config = A1sDS.Of("host", "localhost");
// defaults = A1sDS.Of("host", "127.0.0.1", "port", 8080);
// result = A1sDS.Defaults(config, defaults);
// // -> {host: "localhost", port: 8080}
//
//
Функция Defaults(SourceStruct, DefaultsStruct) Экспорт //⚙ Переименовано с OfDefaults
ib = "Применение значений по умолчанию"; //✍
Результат = Новый Структура(); //✏
// Сначала дефолты
Для Каждого Пара Из DefaultsStruct Цикл //⟳
Результат.Вставить(Пара.Ключ, Пара.Значение); //✏
КонецЦикла;
// Затем перезаписываем
Для Каждого Пара Из SourceStruct Цикл //⟳
Результат.Вставить(Пара.Ключ, Пара.Значение); //✏
КонецЦикла;
Возврат Результат; //↩
КонецФункции
// A1sDS.Merge(БазоваяСтруктура, Источник)
// Merges БазоваяСтруктура structure with overrides (for DTO pattern).
// @param БазоваяСтруктура - Structure - base structure with defaults
// @param Источник - Structure
// @return Structure - merged structure
&AtServer
Функция Merge(БазоваяСтруктура, Знач Источник) Экспорт
//ib = "Слияние с другой структурой"; //⚙
НоваяСтруктура = Copy(БазоваяСтруктура);
Если ТипЗнч(Источник) = Тип("Структура") ИЛИ ТипЗнч(Источник) = Тип("ФиксированнаяСтруктура") Тогда //⚡
Для Каждого Пара Из Источник Цикл //⟳
НоваяСтруктура.Вставить(Пара.Ключ, Пара.Значение); //✏
КонецЦикла;
КонецЕсли;
Возврат НоваяСтруктура; //↩
КонецФункции
#Region examples_A1sDS_Merge
// @example.1.A1sDS.Merge {
// Параметризованная фабричная функция (ПФФ) для DTO
//------------------------------------------------
//Function NewPostingDTO(Overrides = Undefined) Export
//
// Base = A1sDS.Of(
// "AccountDr", ChartOfAccounts.Main.EmptyRef(),
// "AccountCr", ChartOfAccounts.Main.EmptyRef(),
// "Amount", 0,
// "Description", ""
// );
//
// Return A1sDS.Merge(Base, Overrides);
//
//EndFunction
//
//// Использование
//Posting = NewPostingDTO(A1sDS.Of(
// "AccountDr", Account41,
// "AccountCr", Account62,
// "Amount", 1000
//));
//------------------------------------------------
// }
// @example.2.A1sDS.Merge {
// Конфигурация с дефолтами
//------------------------------------------------
//Function GetHTTPConfig(Overrides = Undefined) Export
//
// Defaults = A1sDS.Of(
// "BaseURL", "https://api.example.com",
// "Timeout", 30,
// "UseProxy", False
// );
//
// Return A1sDS.Merge(Defaults, Overrides);
//
//EndFunction
//
//Config1 = GetHTTPConfig(); // Все дефолты
//Config2 = GetHTTPConfig(A1sDS.Of("Timeout", 60)); // Переопределен Timeout
//------------------------------------------------
// }
#EndRegion
// A1sDS.MergeFixed(Base, Overrides)
// Merges base with overrides and returns frozen structure.
// @param Base - Structure - base structure
// @param Overrides - Structure, Undefined - overrides
// @return FixedStructure - frozen merged structure
&AtServer
Function MergeFixed(Base, Overrides = Undefined) Export
Return New ФиксированнаяСтруктура(Merge(Base, Overrides));
EndFunction
#Region examples_A1sDS_MergeFixed
// @example.1.A1sDS.MergeFixed {
// ПФФ с возвратом фиксированного DTO
//------------------------------------------------
//Function NewAPIRequestDTO(Overrides = Undefined) Export
//
// Base = A1sDS.Of(
// "Method", "GET",
// "Timeout", 30,
// "Headers", A1sDS.Of("Accept", "application/json")
// );
//
// // Возвращаем защищенный DTO
// Return A1sDS.MergeFixed(Base, Overrides);
//
//EndFunction
//
//// Использование
//Request = NewAPIRequestDTO(A1sDS.Of("Method", "POST"));
//// Request.Timeout = 60; // Ошибка - объект защищен!
//------------------------------------------------
// }
#EndRegion
//
// Объединяет до 8 структур, значения последующих перезаписывают предыдущие. ✦
// Первая структура ➤
// Вторая структура ➤
// Третья структура ➤
// Четвертая структура ➤
// Пятая структура ➤
// Шестая структура ➤
// Седьмая структура ➤
// Восьмая структура ➤
// Structure — объединенная структура ⬅
//
//
// // s1 = A1sDS.Of("a", 1, "b", 2);
// // s2 = A1sDS.Of("b", 3, "c", 4);
// // A1sDS.Concatenate(s1, s2) -> {a: 1, b: 3, c: 4}
//
//
Функция Concatenate(Структура1, Структура2 = Неопределено, Структура3 = Неопределено,
Структура4 = Неопределено, Структура5 = Неопределено, Структура6 = Неопределено,
Структура7 = Неопределено, Структура8 = Неопределено) Экспорт //⚙
ib = "Объединение структур"; //✍
НоваяСтруктура = Новый Структура(); //✏
// Создаем массив всех переданных структур для итерации
СтруктурыДляОбъединения = Новый Массив(); //✏
Если Структура1 <> Неопределено И ТипЗнч(Структура1) = Тип("Структура") Тогда СтруктурыДляОбъединения.Добавить(Структура1); КонецЕсли; //⚡
Если Структура2 <> Неопределено И ТипЗнч(Структура2) = Тип("Структура") Тогда СтруктурыДляОбъединения.Добавить(Структура2); КонецЕсли; //⚡
Если Структура3 <> Неопределено И ТипЗнч(Структура3) = Тип("Структура") Тогда СтруктурыДляОбъединения.Добавить(Структура3); КонецЕсли; //⚡
Если Структура4 <> Неопределено И ТипЗнч(Структура4) = Тип("Структура") Тогда СтруктурыДляОбъединения.Добавить(Структура4); КонецЕсли; //⚡
Если Структура5 <> Неопределено И ТипЗнч(Структура5) = Тип("Структура") Тогда СтруктурыДляОбъединения.Добавить(Структура5); КонецЕсли; //⚡
Если Структура6 <> Неопределено И ТипЗнч(Структура6) = Тип("Структура") Тогда СтруктурыДляОбъединения.Добавить(Структура6); КонецЕсли; //⚡
Если Структура7 <> Неопределено И ТипЗнч(Структура7) = Тип("Структура") Тогда СтруктурыДляОбъединения.Добавить(Структура7); КонецЕсли; //⚡
Если Структура8 <> Неопределено И ТипЗнч(Структура8) = Тип("Структура") Тогда СтруктурыДляОбъединения.Добавить(Структура8); КонецЕсли; //⚡
// Объединяем все структуры
Для Каждого ТекущаяСтруктура Из СтруктурыДляОбъединения Цикл //⟳
Для Каждого Пара Из ТекущаяСтруктура Цикл //⟳
НоваяСтруктура.Вставить(Пара.Ключ, Пара.Значение); //✏
КонецЦикла;
КонецЦикла;
Возврат НоваяСтруктура; //↩
КонецФункции
#КонецОбласти
#Область КопированиеИСброс
// A1sDS.Copy(Source)
// Creates a shallow copy of structure.
&AtServer
Function Copy(Source) Export
If Source = Undefined Then
Return New Structure;
EndIf;
If TypeOf(Source) <> Type("Structure") Then
Raise StrTemplate(
"A1sDS.Copy: Expected Structure, got %1",
TypeOf(Source)
);
EndIf;
Result = New Structure;
For Each KV In Source Do
Result.Insert(KV.Key, KV.Value);
EndDo;
Return Result;
EndFunction
//
// Глубокое копирование структуры (рекурсивно копирует вложенные структуры). ✦
// Структура для копирования ➤
// Structure — глубокая копия структуры ⬅
//
//
// // nested = A1sDS.Of("inner", A1sDS.Of("value", 42));
// // copy = A1sDS.DeepCopy(nested);
//
//
Функция DeepCopy(ИсходнаяСтруктура) Экспорт //⚙
ib = "Глубокое копирование структуры"; //✍
НоваяСтруктура = Новый Структура(); //✏
Для Каждого Пара Из ИсходнаяСтруктура Цикл //⟳
Значение = Пара.Значение; //✏
Если ТипЗнч(Значение) = Тип("Структура") Тогда //⚡
Значение = DeepCopy(Значение); //▶️
КонецЕсли;
НоваяСтруктура.Вставить(Пара.Ключ, Значение); //✏
КонецЦикла;
Возврат НоваяСтруктура; //↩
КонецФункции
//
//
// Заполняет значения в существующей структуре (In-Place).
// Мутирует переданный объект. Не создает копию.
//
//
// ПРИМЕЧАНИЕ ДЛЯ АРХИТЕКТОРА:
// Для статического кода предпочтительнее нативный "Новый Структура".
// Используйте эту функцию, когда:
// 1. Ключи приходят динамически (Массив).
// 2. Нужно заполнить все ключи одинаковым значением (избежать повторов аргументов).
//
// Структура для заполнения ➤
// Значение для заполнения ➤
// Exception — если TargetStruct не является структурой
//
//
// Струк = Новый Структура("a,b,c");
// A1sDS.Fill(Струк, 0); // Струк теперь {a:0, b:0, c:0}
//
// // Для конвейерных вызовов используй A1sDS.On():
// // A1sDS.On(Струк).Fill(0).Insert("Итог", 100);
//
//
Процедура Fill(TargetStruct, Value) Экспорт //⚙
// ✅ FAIL FAST: Аргумент обязателен и должен быть валидным
Если НЕ (ТипЗнч(TargetStruct) = Тип("Структура")) Тогда //⚡
ВызватьИсключение
НСтр("ru = 'A1sDS.Fill: Ожидается Структура, а передан %1'", ТипЗнч(TargetStruct)); //↯
КонецЕсли;
// ✅ MUTATION: Прямая запись в переданный объект.
// Ссылочное поведение 1С гарантирует, что изменения видны вызывающему коду.
Для Каждого КлючИЗначение Из TargetStruct Цикл //⟳
TargetStruct[КлючИЗначение.Ключ] = Value; //✏
КонецЦикла;
КонецПроцедуры
#Область СлужебноеИГлубокое
//
// Глубокий сброс структуры. Вложенные структуры создаются пустыми, примитивы — в дефолт. ✦
// Исходная структура ➤
// Structure — структура с обнуленными значениями ⬅
///
///
Функция ResetDeep(SourceStruct) Экспорт
Если ТипЗнч(SourceStruct) <> Тип("Структура") Тогда
Возврат Новый Структура;
КонецЕсли;
Возврат ResetDeepValue(SourceStruct);
КонецФункции
// Внутренняя рекурсия
Функция ResetDeepValue(Значение)
Если Значение = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
ТипЗначения = ТипЗнч(Значение);
Если ТипЗначения = Тип("Структура") Тогда
Результат = Новый Структура;
Для Каждого КлючИЗначение Из Значение Цикл
Результат.Вставить(
КлючИЗначение.Ключ,
ResetDeepValue(КлючИЗначение.Значение)
);
КонецЦикла;
Возврат Результат;
ИначеЕсли ТипЗначения = Тип("Массив") Тогда
// Сброс массива внутри структуры (пустой массив как дефолт)
// Если нужна полная очистка элементов внутри, используй логику A1sAR.ResetDeep
Возврат Новый Массив;
КонецЕсли;
// Примитивы
Попытка
Типы = Новый Массив;
Типы.Добавить(ТипЗначения);
ОписаниеТипа = Новый ОписаниеТипов(Типы);
Возврат ОписаниеТипа.ПривестиЗначение();
Исключение
Возврат Неопределено;
КонецПопытки;
КонецФункции
#КонецОбласти
//
// Преобразует структуру в фиксированную. ✦
// Исходная структура ➤
// FixedStructure — неизменяемая структура ⬅
//
//
Функция ToFixed(Знач Структура) Экспорт //⚙
ib = "Преобразование в фиксированную структуру"; //✍
Если Структура = Неопределено Тогда //⚡
Возврат Новый ФиксированнаяСтруктура(Новый Структура); //↩
КонецЕсли;
Возврат Новый ФиксированнаяСтруктура(Структура); //↩
КонецФункции
#КонецОбласти
#Область АнализИАгрегация
// Equals, IsEmpty, HasKey, GetKeys, GetValues
// -- АНАЛИЗ
//
// Проверяет наличие ключа в структуре. ✦
// Структура для проверки ➤
// Проверяемый ключ ➤
// Boolean — истина, если ключ существует ⬅
//
//
// // struct = A1sDS.Of("name", "John");
// // A1sDS.HasKey(struct, "name") -> Истина
// // A1sDS.HasKey(struct, "age") -> Ложь
//
//
Функция HasKey(Структура, Ключ) Экспорт //⚙
ib = "Проверка наличия ключа"; //✍
Возврат Структура.Свойство(Ключ) <> Неопределено; //↩
КонецФункции
//
// Проверяет, что структура содержит все перечисленные ключи. ✦
// Структура для проверки ➤
// Строка требуемых ключей через запятую ➤
// Boolean — Истина, если все ключи присутствуют ⬅
//
//
// // user = A1sDS.Of("name", "John", "age", 30);
// // A1sDS.HasKeys(user, "name,age") -> Истина
// // A1sDS.HasKeys(user, "name,email") -> Ложь
//
//
Функция HasKeys(Struct, KeysString) Экспорт //⚙
ib = "Проверка наличия набора ключей"; //✍
ТребуемыеКлючи = СтрРазделить(KeysString, ","); //✏
Для Каждого КлючСтрока Из ТребуемыеКлючи Цикл //⟳
Ключ = СокрЛП(КлючСтрока); //✏
Если НЕ ЗначениеЗаполнено(Ключ) ИЛИ НЕ Struct.Свойство(Ключ) Тогда //⚡
Возврат Ложь; //↩
КонецЕсли;
КонецЦикла;
Возврат Истина; //↩
КонецФункции
//
// Проверяет, пуста ли структура (нет свойств). ✦
// Структура для проверки ➤
// Boolean — истина, если структура пустая ⬅
//
//
// // A1sDS.IsEmpty(Новый Структура()) -> Истина
// // A1sDS.IsEmpty(A1sDS.Of("x", 1)) -> Ложь
//
//
Функция IsEmpty(Структура) Экспорт //⚙
ib = "Проверка пустоты структуры"; //✍
Для Каждого Пара Из Структура Цикл //⟳
Возврат Ложь; //↩
КонецЦикла;
Возврат Истина; //↩
КонецФункции
//
// Возвращает массив ключей структуры. ✦
// Исходная структура ➤
// Array — массив строк-ключей ⬅
//
//
// // struct = A1sDS.Of("name", "John", "age", 30);
// // A1sDS.GetKeys(struct) -> ["name", "age"]
//
//
Функция GetKeys(Структура) Экспорт //⚙
ib = "Получение массива ключей"; //✍
Ключи = Новый Массив(); //✏
Для Каждого Пара Из Структура Цикл //⟳
Ключи.Добавить(Пара.Ключ); //✏
КонецЦикла;
Возврат Ключи; //↩
КонецФункции
//
// Возвращает массив значений структуры. ✦
// Исходная структура ➤
// Array — массив значений ⬅
//
//
// // struct = A1sDS.Of("name", "John", "age", 30);
// // A1sDS.GetValues(struct) -> ["John", 30]
//
//
Функция GetValues(Структура) Экспорт //⚙
ib = "Получение массива значений"; //✍
Значения = Новый Массив(); //✏
Для Каждого Пара Из Структура Цикл //⟳
Значения.Добавить(Пара.Значение); //✏
КонецЦикла;
Возврат Значения; //↩
КонецФункции
// --- СРАВНЕНИЕ (Comparison) ---
//
// Сравнивает две структуры на полное совпадение (ключи и значения) с рекурсией. ✦
// Первая структура ➤
// Вторая структура ➤
// Boolean — Истина, если структуры идентичны по содержимому ⬅
//
//
// // s1 = A1sDS.Of("a", 1, "b", 2);
// // s2 = A1sDS.Of("b", 2, "a", 1);
// // A1sDS.Equals(s1, s2) -> Истина
//
//
Функция Equals(Struct1, Struct2) Экспорт //⚙
ib = "Глубокое сравнение двух структур"; //✍
// Быстрая проверка на количество ключей
Если Struct1.Количество() <> Struct2.Количество() Тогда //⚡
Возврат Ложь; //↩
КонецЕсли;
// Проверяем каждую пару из первой структуры
Для Каждого Пара Из Struct1 Цикл //⟳
Ключ = Пара.Ключ; //✏
Значение1 = Пара.Значение; //✏
Значение2 = Неопределено; //✏
// Если ключа нет во второй структуре
Если НЕ Struct2.Свойство(Ключ, Значение2) Тогда //⚡
Возврат Ложь; //↩
КонецЕсли;
// --- НОВОЕ: Рекурсивное сравнение ---
// Если оба значения - структуры, сравниваем их рекурсивно
Если ТипЗнч(Значение1) = Тип("Структура") И ТипЗнч(Значение2) = Тип("Структура") Тогда //⚡
Если НЕ Equals(Значение1, Значение2) Тогда //▶️
Возврат Ложь; //↩
КонецЕсли;
// Если типы разные, они не равны
ИначеЕсли ТипЗнч(Значение1) <> ТипЗнч(Значение2) Тогда //⚡
Возврат Ложь; //↩
// Иначе, это простые типы, сравниваем напрямую
ИначеЕсли Значение1 <> Значение2 Тогда //⚡
Возврат Ложь; //↩
КонецЕсли;
КонецЦикла;
Возврат Истина; //↩
КонецФункции
// --- АГРЕГАЦИЯ (Aggregation) ---
//
// Группирует массив структур по значению указанного ключа и агрегирует значения другого ключа. ✦
// Массив структур для группировки ➤
// Ключ, по которому группировать ➤
// Ключ, значения которого суммировать ➤
// Structure — структура с результатами группировки ⬅
//
//
// // sales = A1sAR.Of(
// // A1sDS.Of("product", "A", "amount", 10),
// // A1sDS.Of("product", "B", "amount", 5),
// // A1sDS.Of("product", "A", "amount", 7)
// // );
// // A1sDS.GroupBy(sales, "product", "amount") -> {A: 17, B: 5}
//
//
Функция GroupBy(ArrayOfStructs, GroupByKey, AggregateByKey) Экспорт //⚙
ib = "Группировка массива структур"; //✍
Результат = Новый Структура(); //✏
Для Каждого Элемент Из ArrayOfStructs Цикл //⟳
Если ТипЗнч(Элемент) <> Тип("Структура") Тогда //⚡
Продолжить; //✖
КонецЕсли;
Если Элемент.Свойство(GroupByKey) И Элемент.Свойство(AggregateByKey) Тогда //⚡
КлючГруппировки = Элемент[GroupByKey]; //✏
ЗначениеДляСуммы = Элемент[AggregateByKey]; //✏
ТекущееЗначение = 0; //✏
Результат.Свойство(Строка(КлючГруппировки), ТекущееЗначение); //✏
Результат.Вставить(Строка(КлючГруппировки), ТекущееЗначение + ЗначениеДляСуммы); //✏
КонецЕсли;
КонецЦикла;
Возврат Результат; //↩
КонецФункции
#КонецОбласти
#Область СетевыеОперации
// Intersection, Difference (с пометкой про ключи)
//
// Возвращает структуру с ключами, которые есть во всех переданных структурах (пересечение до 8 структур). ✦
// Первая структура ➤
// Вторая структура ➤
// Третья структура ➤
// Четвертая структура ➤
// Пятая структура ➤
// Шестая структура ➤
// Седьмая структура ➤
// Восьмая структура ➤
// Structure — пересечение структур ⬅
//
//
// // s1 = A1sDS.Of("a", 1, "b", 2); s2 = A1sDS.Of("b", 3, "c", 4);
// // A1sDS.Intersection(s1, s2) -> {b: 3}
//
//
Функция Intersection(Структура1, Структура2 = Неопределено, Структура3 = Неопределено,
Структура4 = Неопределено, Структура5 = Неопределено, Структура6 = Неопределено,
Структура7 = Неопределено, Структура8 = Неопределено) Экспорт //⚙
ib = "Пересечение структур"; //✍
// Создаем массив всех переданных структур
ВсеСтруктуры = Новый Массив(); //✏
Если Структура1 <> Неопределено И ТипЗнч(Структура1) = Тип("Структура") Тогда ВсеСтруктуры.Добавить(Структура1); КонецЕсли; //⚡
Если Структура2 <> Неопределено И ТипЗнч(Структура2) = Тип("Структура") Тогда ВсеСтруктуры.Добавить(Структура2); КонецЕсли; //⚡
Если Структура3 <> Неопределено И ТипЗнч(Структура3) = Тип("Структура") Тогда ВсеСтруктуры.Добавить(Структура3); КонецЕсли; //⚡
Если Структура4 <> Неопределено И ТипЗнч(Структура4) = Тип("Структура") Тогда ВсеСтруктуры.Добавить(Структура4); КонецЕсли; //⚡
Если Структура5 <> Неопределено И ТипЗнч(Структура5) = Тип("Структура") Тогда ВсеСтруктуры.Добавить(Структура5); КонецЕсли; //⚡
Если Структура6 <> Неопределено И ТипЗнч(Структура6) = Тип("Структура") Тогда ВсеСтруктуры.Добавить(Структура6); КонецЕсли; //⚡
Если Структура7 <> Неопределено И ТипЗнч(Структура7) = Тип("Структура") Тогда ВсеСтруктуры.Добавить(Структура7); КонецЕсли; //⚡
Если Структура8 <> Неопределено И ТипЗнч(Структура8) = Тип("Структура") Тогда ВсеСтруктуры.Добавить(Структура8); КонецЕсли; //⚡
Если ВсеСтруктуры.Количество() = 0 Тогда //⚡
Возврат Новый Структура(); //↩
КонецЕсли;
Если ВсеСтруктуры.Количество() = 1 Тогда //⚡
// Создаем копию первой структуры
Результат = Новый Структура(); //✏
Для Каждого Пара Из ВсеСтруктуры[0] Цикл //⟳
Результат.Вставить(Пара.Ключ, Пара.Значение); //✏
КонецЦикла;
Возврат Результат; //↩
КонецЕсли;
// Начинаем с ключей первой структуры
Результат = Новый Структура(); //✏
ПерваяСтруктура = ВсеСтруктуры[0]; //✏
// Проверяем каждый ключ первой структуры
Для Каждого Пара Из ПерваяСтруктура Цикл //⟳
КлючПрисутствуетВоВсех = Истина; //✏
// Проверяем присутствие ключа в остальных структурах
Для Индекс = 1 По ВсеСтруктуры.Количество() - 1 Цикл //⟳
Если НЕ ВсеСтруктуры[Индекс].Свойство(Пара.Ключ) Тогда //⚡
КлючПрисутствуетВоВсех = Ложь; //✏
Прервать; //✖
КонецЕсли;
КонецЦикла;
// Если ключ присутствует во всех структурах, берем значение из последней структуры
Если КлючПрисутствуетВоВсех Тогда //⚡
ЗначениеДляВставки = Пара.Значение; //✏
// Берем значение из последней структуры, которая содержит этот ключ
Индекс = ВсеСтруктуры.Количество() - 1; //✏
Пока Индекс >= 0 Цикл //⟳
Если ВсеСтруктуры[Индекс].Свойство(Пара.Ключ) Тогда //⚡
ЗначениеДляВставки = ВсеСтруктуры[Индекс][Пара.Ключ]; //✏
Прервать; //✖
КонецЕсли;
Индекс = Индекс - 1; //✏
КонецЦикла;
Результат.Вставить(Пара.Ключ, ЗначениеДляВставки); //✏
КонецЕсли;
КонецЦикла;
Возврат Результат; //↩
КонецФункции
//
// Возвращает структуру с ключами из первой структуры, которых нет в остальных структурах (разность до 8 структур). ✦
// Первая структура ➤
// Вторая структура ➤
// Третья структура ➤
// Четвертая структура ➤
// Пятая структура ➤
// Шестая структура ➤
// Седьмая структура ➤
// Восьмая структура ➤
// Structure — разность структур ⬅
//
//
// // s1 = A1sDS.Of("a", 1, "b", 2); s2 = A1sDS.Of("b", 3, "c", 4);
// // A1sDS.Difference(s1, s2) -> {a: 1}
//
//
Функция Difference(Структура1, Структура2 = Неопределено, Структура3 = Неопределено,
Структура4 = Неопределено, Структура5 = Неопределено, Структура6 = Неопределено,
Структура7 = Неопределено, Структура8 = Неопределено) Экспорт //⚙
ib = "Разность структур"; //✍
Если Структура1 = Неопределено ИЛИ ТипЗнч(Структура1) <> Тип("Структура") Тогда //⚡
Возврат Новый Структура(); //↩
КонецЕсли;
// Создаем массив структур для исключения
СтруктурыИсключения = Новый Массив(); //✏
Если Структура2 <> Неопределено И ТипЗнч(Структура2) = Тип("Структура") Тогда СтруктурыИсключения.Добавить(Структура2); КонецЕсли; //⚡
Если Структура3 <> Неопределено И ТипЗнч(Структура3) = Тип("Структура") Тогда СтруктурыИсключения.Добавить(Структура3); КонецЕсли; //⚡
Если Структура4 <> Неопределено И ТипЗнч(Структура4) = Тип("Структура") Тогда СтруктурыИсключения.Добавить(Структура4); КонецЕсли; //⚡
Если Структура5 <> Неопределено И ТипЗнч(Структура5) = Тип("Структура") Тогда СтруктурыИсключения.Добавить(Структура5); КонецЕсли; //⚡
Если Структура6 <> Неопределено И ТипЗнч(Структура6) = Тип("Структура") Тогда СтруктурыИсключения.Добавить(Структура6); КонецЕсли; //⚡
Если Структура7 <> Неопределено И ТипЗнч(Структура7) = Тип("Структура") Тогда СтруктурыИсключения.Добавить(Структура7); КонецЕсли; //⚡
Если Структура8 <> Неопределено И ТипЗнч(Структура8) = Тип("Структура") Тогда СтруктурыИсключения.Добавить(Структура8); КонецЕсли; //⚡
// Создаем соответствие всех ключей для исключения
КлючиИсключения = Новый Соответствие(); //✏
Для Каждого СтруктураИсключения Из СтруктурыИсключения Цикл //⟳
Для Каждого Пара Из СтруктураИсключения Цикл //⟳
КлючиИсключения.Вставить(Пара.Ключ, Истина); //✏
КонецЦикла;
КонецЦикла;
// Формируем результат
Результат = Новый Структура(); //✏
Для Каждого Пара Из Структура1 Цикл //⟳
Если КлючиИсключения[Пара.Ключ] = Неопределено Тогда //⚡
Результат.Вставить(Пара.Ключ, Пара.Значение); //✏
КонецЕсли;
КонецЦикла;
Возврат Результат; //↩
КонецФункции
#КонецОбласти
#Область СлужебноеИГлубокое
// ResetDeep, FlattenKeys, NestKeys
//
// Превращает вложенную структуру в плоскую, соединяя ключи через разделитель. ✦
// Вложенная структура ➤
// Разделитель для ключей (не может быть ".") ➤
// Structure — плоская структура ⬅
//
//
// // nested = A1sDS.Of("ui", A1sDS.Of("theme", A1sDS.Of("dark", True)));
// // A1sDS.FlattenKeys(nested) -> {"ui_theme_dark": True}
//
//
Функция FlattenKeys(NestedStruct, Separator = "_") Экспорт //⚙
ib = "Выпрямление ключей вложенной структуры"; //✍
Результат = Новый Структура(); //✏
_ВыпрямитьРекурсивно(NestedStruct, "", Результат, Separator); //▶️
Возврат Результат; //↩
КонецФункции
//
// Собирает плоскую структуру во вложенную, разделяя ключи по разделителю. ✦
// Плоская структура с составными ключами ➤
// Разделитель ключей ➤
// Structure — вложенная структура ⬅
//
//
// // flat = A1sDS.Of("user_profile_name", "John", "user_profile_address_city", "NY", "order_id", 456);
// // A1sDS.NestKeys(flat) -> {user: {profile: {name: "John", address: {city: "NY"}}}, order: {id: 456}}
//
//
Функция NestKeys(FlatStruct, Separator = "_") Экспорт //⚙
ib = "Сборка плоской структуры во вложенную"; //✍
Результат = Новый Структура(); //✏
// ИСПРАВЛЕННЫЙ ЦИКЛ
Для Каждого КлючИЗначение ИЗ FlatStruct Цикл //⟳
ПлоскийКлюч = КлючИЗначение.Ключ; //✏
Значение = КлючИЗначение.Значение; //✏
ЧастиПути = СтрРазделить(ПлоскийКлюч, Separator); //✏
// Отфильтровываем пустые части, которые могут возникнуть
// из-за ключей типа "_key" или "key__"
ВалидныеЧасти = Новый Массив; //✏
Для Каждого Часть Из ЧастиПути Цикл //⟳
Часть = СокрЛП(Часть); //✏
Если ЗначениеЗаполнено(Часть) Тогда //⚡
ВалидныеЧасти.Добавить(Часть); //✏
КонецЕсли;
КонецЦикла;
Если ВалидныеЧасти.Количество() = 0 Тогда //⚡
Продолжить; //✖
КонецЕсли;
// "Идем" по дереву структуры, создавая узлы по пути
ТекущийУзел = Результат; //✏
Для i = 0 По ВалидныеЧасти.Количество() - 2 Цикл //⟳
ИмяУзла = ВалидныеЧасти[i]; //✏
Если НЕ ТекущийУзел.Свойство(ИмяУзла) Тогда //⚡
ТекущийУзел.Вставить(ИмяУзла, Новый Структура); //✏
ИначеЕсли ТипЗнч(ТекущийУзел[ИмяУзла]) <> Тип("Структура") Тогда //⚡
// Конфликт: по пути уже лежит не-структура. Заменяем на структуру.
ТекущийУзел[ИмяУзла] = Новый Структура; //✏
КонецЕсли;
ТекущийУзел = ТекущийУзел[ИмяУзла]; //✏
КонецЦикла;
// Вставляем конечное значение
КонечноеИмяКлюча = ВалидныеЧасти[ВалидныеЧасти.Количество() - 1]; //✏
ТекущийУзел.Вставить(КонечноеИмяКлюча, Значение); //✏
КонецЦикла;
Возврат Результат; //↩
КонецФункции
// --- Служебная функция для рекурсивного выпрямления ---
Процедура _ВыпрямитьРекурсивно(ТекущаяСтруктура, Префикс, Результат, Separator) //⚙
Для Каждого Пара Из ТекущаяСтруктура Цикл //⟳
ТекущийКлюч = Пара.Ключ; //✏
ТекущееЗначение = Пара.Значение; //✏
НовыйПрефикс = ?(ЗначениеЗаполнено(Префикс), Префикс + Separator + ТекущийКлюч, ТекущийКлюч); //✏
Если ТипЗнч(ТекущееЗначение) = Тип("Структура") Тогда //⚡
_ВыпрямитьРекурсивно(ТекущееЗначение, НовыйПрефикс, Результат, Separator); //▶️
Иначе
Результат.Вставить(НовыйПрефикс, ТекущееЗначение); //✏
КонецЕсли;
КонецЦикла;
КонецПроцедуры
#КонецОбласти
#Область СериализацияИСанация
//
//
// Глубокая очистка и нормализация данных для JSON (Top-Tier Enterprise).
// Превращает 1C-специфичные типы в JSON-совместимые:
// - UUID/Ссылки -> String (через XMLString для чистого UID)
// - СписокЗначений -> Array
// - Соответствие -> Structure (Graceful Degrade для ключей)
// - ТаблицаЗначений -> Array of Objects (Graceful Degrade для колонок)
// - ДеревоЗначений -> Recursive Array with "Children"
// - ФиксированнаяСтруктура/Массив -> Обычные (разворачивает)
//
// Исходные данные ➤
// Arbitrary — "Чистые" данные, готовые для JSON ⬅
//
//
// Mixed = ПолучитьСмешанныеДанные();
// JSON = A1sDS.Sanitize(Mixed).ToJSON();
//
//
&AtServer
Функция Sanitize(Data) Экспорт
// 1. Базовые типы (Pass-Through)
// Не трогаем, чтобы не плодить лишние структуры
Если Data = Неопределено ИЛИ ТипЗнч(Data) = Тип("Строка")
ИЛИ ТипЗнч(Data) = Тип("Число")
ИЛИ ТипЗнч(Data) = Тип("Дата")
ИЛИ ТипЗнч(Data) = Тип("Булево") Тогда
Возврат Data;
КонецЕсли;
// 2. Структура -> Рекурсивный обход
Если ТипЗнч(Data) = Тип("Структура") Тогда
Результат = Новый Структура;
Для Каждого КлЗн Из Data Цикл
Результат.Вставить(КлЗн.Ключ, Sanitize(КлЗн.Значение));
КонецЦикла;
Возврат Результат;
КонецЕсли;
// 3. ФиксированнаяСтруктура -> Разворачиваем -> Рекурсия
Если ТипЗнч(Data) = Тип("ФиксированнаяСтруктура") Тогда
Возврат Sanitize(Новый Структура(Data));
КонецЕсли;
// 4. Массив -> Рекурсивный обход
Если ТипЗнч(Data) = Тип("Массив") Тогда
Результат = Новый Массив;
Для Каждого Элемент Из Data Цикл
Результат.Добавить(Sanitize(Элемент));
КонецЦикла;
Возврат Результат;
КонецЕсли;
// 5. ФиксированныйМассив -> Разворачиваем -> Рекурсия
Если ТипЗнч(Data) = Тип("ФиксированныйМассив") Тогда
Возврат Sanitize(Новый Массив(Data));
КонецЕсли;
// 6. СписокЗначений -> Массив
Если ТипЗнч(Data) = Тип("СписокЗначений") Тогда
Результат = Новый Массив;
Для Каждого Элемент Из Data Цикл
Результат.Добавить(Sanitize(Элемент.Значение));
КонецЦикла;
Возврат Результат;
КонецЕсли;
// 7. Соответствие -> Структура (Graceful Degrade)
// JSON не умеет в Map, только в Objects.
// ВАЖНО: 1C Структура запрещает ключи "10", "key-name".
// Используем Попытку, чтобы не упасть, а просто пропустить невалидный ключ.
Если ТипЗнч(Data) = Тип("Соответствие") Тогда
Результат = Новый Структура;
Для Каждого КлЗн Из Data Цикл
КлючСтрока = Строка(КлЗн.Ключ);
Попытка
Результат.Вставить(КлючСтрока, Sanitize(КлЗн.Значение));
Исключение
// Ключ невалиден для 1С Структуры (например, начинается с цифры "10").
// Graceful Degrade: Пропускаем.
КонецПопытки;
КонецЦикла;
Возврат Результат;
КонецЕсли;
// 8. УникальныйИдентификатор -> XMLString (Чистый формат)
Если ТипЗнч(Data) = Тип("УникальныйИдентификатор") Тогда
Возврат XMLString(Data);
КонецЕсли;
// 9. Любые Ссылки (Справочник, Документ...) -> XMLString
// Проверка через "НайтиПоТипу" надежнее проверки строки имени типа.
Если Метаданные.НайтиПоТипу(ТипЗнч(Data)) <> Неопределено Тогда
Возврат XMLString(Data);
КонецЕсли;
// 10. ТаблицаЗначений -> Массив структур (Graceful Degrade)
// Фронтенд ждет массив объектов. ВАЖНО: Имена колонок тоже могут быть невалидными!
Если ТипЗнч(Data) = Тип("ТаблицаЗначений") Тогда
Результат = Новый Массив;
Для Каждого СтрокаТЗ Из Data Цикл
НоваяСтрока = Новый Структура;
Для Каждого Колонка Из Data.Колонки Цикл
Ключ = Колонка.Имя;
Попытка
НоваяСтрока.Вставить(Ключ, Sanitize(СтрокаТЗ[Ключ]));
Исключение
// Колонка с именем "10" или "key-name" пропущена
КонецПопытки;
КонецЦикла;
Результат.Добавить(НоваяСтрока);
КонецЦикла;
Возврат Результат;
КонецЕсли;
// 11. ДеревоЗначений -> Массив структур с рекурсией
// Обрабатываем дерево, сохраняя вложенность через поле "Children"
Если ТипЗнч(Data) = Тип("ДеревоЗначений") Тогда
// Вызываем вспомогательную функцию для обхода уровней
Возврат SanitizeTreeRows(Data.Строки, Data.Колонки);
КонецЕсли;
// 12. Резерв: Всякий нечитаемый хлам -> String (Ласточка безопасности)
// Если это что-то редкое (БуферДвоичныхДанных, Картинка, ДокументHTML) — превращаем в строку.
Возврат Строка(Data); //↩
КонецФункции
//
//
// Вспомогательная функция для рекурсивного обхода ДереваЗначений.
// Превращает строки дерева в Структуры и вызывает себя для дочерних строк.
//
// Коллекция строк текущего уровня ➤
// Коллекция колонок дерева (схема) ➤
// Array — массив структур данных уровня ⬅
//
&AtServer
Функция SanitizeTreeRows(СтрокиДерева, Колонки) Экспорт
РезультатМассив = Новый Массив;
Для Каждого Строка Из СтрокиДерева Цикл
НоваяСтрока = Новый Структура;
// 1. Обрабатываем колонки текущей строки (как в ТаблицеЗначений)
Для Каждого Колонка Из Колонки Цикл
Ключ = Колонка.Имя;
Попытка
// Санитайзим значение ячейки (там может быть Ссылка!)
НоваяСтрока.Вставить(Ключ, Sanitize(Строка[Ключ]));
Исключение
// Плохое имя колонки — пропускаем
КонецПопытки;
КонецЦикла;
// 2. Рекурсивно обрабатываем детей, если они есть
Если Строка.Строки.Количество() > 0 Тогда
НоваяСтрока.Вставить("Children", SanitizeTreeRows(Строка.Строки, Колонки));
КонецЕсли;
РезультатМассив.Добавить(НоваяСтрока);
КонецЦикла;
Возврат РезультатМассив;
КонецФункции
/// ✦ ToJSON — Преобразовать в JSON-строку
/// ⚠ ВАЖНО: Данные должны содержать только простые типы (String, Number, Date, Bool, Structure).
/// Если есть UUID или Ссылки — используйте A1sDS.Sanitize().ToJSON()
Функция ToJSON(СтруктураВJSON) Экспорт
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, СтруктураВJSON);
Возврат ЗаписьJSON.Закрыть();
КонецФункции // ToJSON()
#КонецОбласти
#Region Tests
//
// Самотест модуля A1sDS с примерами использования. ✦
// Boolean — все тесты пройдены успешно ⬅
//
//
Функция SelfTest() Экспорт //⚙
ib = "Самотест A1sDS"; //✍
TestsPassed = 0; //✏
TestsTotal = 0; //✏
Сообщить("=== A1sDS.SelfTest() START ===");
// Тест 1: Of - создание структуры из пар ключ-значение
TestsTotal = TestsTotal + 1; //✏
TestStruct1 = Of("name", "John", "age", 30, "city", "NYC"); //✏
Если TestStruct1.Свойство("name") И TestStruct1.name = "John" И TestStruct1.age = 30 И TestStruct1.city = "NYC" Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Сообщить("✓ Тест 1 (Of создание): ПРОЙДЕН");
Иначе
Сообщить("✗ Тест 1 (Of создание): ПРОВАЛЕН");
КонецЕсли;
// Тест 2: Zip - создание из ключей и значений
TestsTotal = TestsTotal + 1; //✏
TestKeys = "x,y,z"; //✏
TestValues = Новый Массив(); //✏
TestValues.Добавить(10); //✏
TestValues.Добавить(20); //✏
TestValues.Добавить(30); //✏
TestStruct2 = Zip(TestKeys, TestValues); //✏
Если TestStruct2.x = 10 И TestStruct2.y = 20 И TestStruct2.z = 30 Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Сообщить("✓ Тест 2 (Zip): ПРОЙДЕН");
Иначе
Сообщить("✗ Тест 2 (Zip): ПРОВАЛЕН");
КонецЕсли;
// Тест 3: Pick - выборка ключей
TestsTotal = TestsTotal + 1; //✏
TestUser = Of("name", "John", "age", 30, "password", "secret", "email", "john@test.com"); //✏
TestPublic = Pick(TestUser, "name,age,email"); //✏
Если TestPublic.Свойство("name") И TestPublic.Свойство("age") И TestPublic.Свойство("email") И НЕ TestPublic.Свойство("password") Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Сообщить("✓ Тест 3 (Pick): ПРОЙДЕН");
Иначе
Сообщить("✗ Тест 3 (Pick): ПРОВАЛЕН");
КонецЕсли;
// Тест 4: Omit - исключение ключей
TestsTotal = TestsTotal + 1; //✏
TestSafe = Omit(TestUser, "password"); //✏
Если TestSafe.Свойство("name") И TestSafe.Свойство("age") И НЕ TestSafe.Свойство("password") Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Сообщить("✓ Тест 4 (Omit): ПРОЙДЕН");
Иначе
Сообщить("✗ Тест 4 (Omit): ПРОВАЛЕН");
КонецЕсли;
// Тест 5: Defaults - значения по умолчанию
TestsTotal = TestsTotal + 1; //✏
TestConfig = Of("host", "localhost"); //✏
TestDefaults = Of("host", "127.0.0.1", "port", 8080, "timeout", 30); //✏
TestResult = Defaults(TestConfig, TestDefaults); //✏
Если TestResult.host = "localhost" И TestResult.port = 8080 И TestResult.timeout = 30 Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Сообщить("✓ Тест 5 (Defaults): ПРОЙДЕН");
Иначе
Сообщить("✗ Тест 5 (Defaults): ПРОВАЛЕН");
КонецЕсли;
// Тест 6: Concatenate - объединение структур
TestsTotal = TestsTotal + 1; //✏
TestS1 = Of("a", 1, "b", 2); //✏
TestS2 = Of("b", 3, "c", 4); //✏
TestMerged = Concatenate(TestS1, TestS2); //✏
Если TestMerged.a = 1 И TestMerged.b = 3 И TestMerged.c = 4 Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Сообщить("✓ Тест 6 (Concatenate): ПРОЙДЕН");
Иначе
Сообщить("✗ Тест 6 (Concatenate): ПРОВАЛЕН");
КонецЕсли;
// Тест 7: HasKey - проверка наличия ключа
TestsTotal = TestsTotal + 1; //✏
TestStruct7 = Of("exists", "yes"); //✏
Если HasKey(TestStruct7, "exists") И НЕ HasKey(TestStruct7, "missing") Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Сообщить("✓ Тест 7 (HasKey): ПРОЙДЕН");
Иначе
Сообщить("✗ Тест 7 (HasKey): ПРОВАЛЕН");
КонецЕсли;
// Тест 8: GetKeys - получение ключей
TestsTotal = TestsTotal + 1; //✏
TestStruct8 = Of("first", 1, "second", 2); //✏
TestKeys8 = GetKeys(TestStruct8); //✏
Если TestKeys8.Количество() = 2 И (TestKeys8.Найти("first") <> Неопределено) И (TestKeys8.Найти("second") <> Неопределено) Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Сообщить("✓ Тест 8 (GetKeys): ПРОЙДЕН");
Иначе
Сообщить("✗ Тест 8 (GetKeys): ПРОВАЛЕН");
КонецЕсли;
// Тест 9: IsEmpty - проверка пустоты
TestsTotal = TestsTotal + 1; //✏
EmptyStruct = Новый Структура(); //✏
NonEmptyStruct = Of("test", "value"); //✏
Если IsEmpty(EmptyStruct) И НЕ IsEmpty(NonEmptyStruct) Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Сообщить("✓ Тест 9 (IsEmpty): ПРОЙДЕН");
Иначе
Сообщить("✗ Тест 9 (IsEmpty): ПРОВАЛЕН");
КонецЕсли;
// Тест 10: OfQueryString - разбор QueryString
TestsTotal = TestsTotal + 1; //✏
TestQS = OfQueryString("name=John&age=30&city=New%20York"); //✏
Если TestQS.name = "John" И TestQS.age = "30" И TestQS.city = "New York" Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Сообщить("✓ Тест 10 (OfQueryString): ПРОЙДЕН");
Иначе
Сообщить("✗ Тест 10 (OfQueryString): ПРОВАЛЕН");
КонецЕсли;
// ============================================
// Тесты новых функций (v1.2.0)
// ============================================
// Тест 11: OfKeys
TestsTotal= TestsTotal+ 1; //✏
Попытка
Результат1 = OfKeys("a,b,c", 0); //▶️
Результат2 = OfKeys(A1sAR.Of("x", "y"), ""); //▶️
Если Результат1.a = 0 И Результат1.b = 0 И Результат1.c = 0 И Результат2.x = "" И Результат2.y = "" Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Иначе
Сообщить("✗ Тест 11 (OfKeys): ПРОВАЛЕН"); //▶️
Успешно = Ложь; //✏
КонецЕсли;
Исключение
Сообщить("✗ Тест 11 (OfKeys): ИСКЛЮЧЕНИЕ - " + ОписаниеОшибки()); //▶️
Успешно = Ложь; //✏
КонецПопытки;
// Тест 12: OfSame
TestsTotal= TestsTotal+ 1; //✏
Попытка
Результат = OfKeys("active, verified, paid", Истина); //▶️
Если Результат.active = Истина И Результат.verified = Истина И Результат.paid = Истина Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Иначе
Сообщить("✗ Тест 12 (OfKeys): ПРОВАЛЕН"); //▶️
Успешно = Ложь; //✏
КонецЕсли;
Исключение
Сообщить("✗ Тест 12 (OfKeys): ИСКЛЮЧЕНИЕ - " + ОписаниеОшибки()); //▶️
Успешно = Ложь; //✏
КонецПопытки;
// Тест 13: Fill
TestsTotal= TestsTotal+ 1; //✏
Попытка
ТестоваяСтруктура = Of("x", 10, "y", 20); //✏
Fill(ТестоваяСтруктура, 0); //▶️
Если ТестоваяСтруктура.x = 0 И ТестоваяСтруктура.y = 0 Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Иначе
Сообщить("✗ Тест 13 (Fill): ПРОВАЛЕН"); //▶️
Успешно = Ложь; //✏
КонецЕсли;
Исключение
Сообщить("✗ Тест 13 (Fill): ИСКЛЮЧЕНИЕ - " + ОписаниеОшибки()); //▶️
Успешно = Ложь; //✏
КонецПопытки;
// Тест 14: Equals
TestsTotal= TestsTotal+ 1; //✏
Попытка
С1 = Of("a", 1, "b", 2); //✏
С2 = Of("b", 2, "a", 1); //✏
С3 = Of("a", 1, "b", 3); //✏
Если Equals(С1, С2) И НЕ Equals(С1, С3) Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Иначе
Сообщить("✗ Тест 14 (Equals): ПРОВАЛЕН"); //▶️
Успешно = Ложь; //✏
КонецЕсли;
Исключение
Сообщить("✗ Тест 14 (Equals): ИСКЛЮЧЕНИЕ - " + ОписаниеОшибки()); //▶️
Успешно = Ложь; //✏
КонецПопытки;
// Тест 15: GroupBy
TestsTotal= TestsTotal+ 1; //✏
Попытка
МассивПродаж = A1sAR.Of(Of("product", "A", "amount", 10), Of("product", "B", "amount", 5), Of("product", "A", "amount", 7)); //✏
Результат = GroupBy(МассивПродаж, "product", "amount"); //▶️
Если Результат["A"] = 17 И Результат["B"] = 5 Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Иначе
Сообщить("✗ Тест 15 (GroupBy): ПРОВАЛЕН"); //▶️
Успешно = Ложь; //✏
КонецЕсли;
Исключение
Сообщить("✗ Тест 15 (GroupBy): ИСКЛЮЧЕНИЕ - " + ОписаниеОшибки()); //▶️
Успешно = Ложь; //✏
КонецПопытки;
// Тест 16: HasKeys
TestsTotal= TestsTotal+ 1; //✏
Попытка
Пользователь = Of("name", "John", "age", 30); //✏
Если HasKeys(Пользователь, "name,age") И НЕ HasKeys(Пользователь, "name,email") Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Иначе
Сообщить("✗ Тест 16 (HasKeys): ПРОВАЛЕН"); //▶️
Успешно = Ложь; //✏
КонецЕсли;
Исключение
Сообщить("✗ Тест 16 (HasKeys): ИСКЛЮЧЕНИЕ - " + ОписаниеОшибки()); //▶️
Успешно = Ложь; //✏
КонецПопытки;
// Тест 17: FlattenKeys
TestsTotal= TestsTotal+ 1; //✏
Попытка
Вложенная = Of("ui", Of("theme", Of("dark", Истина))); //✏
Результат = FlattenKeys(Вложенная); //▶️
Если Результат.Свойство("ui_theme_dark") И Результат["ui_theme_dark"] = Истина Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Иначе
Сообщить("✗ Тест 17 (FlattenKeys): ПРОВАЛЕН"); //▶️
Успешно = Ложь; //✏
КонецЕсли;
Исключение
Сообщить("✗ Тест 17 (FlattenKeys): ИСКЛЮЧЕНИЕ - " + ОписаниеОшибки()); //▶️
Успешно = Ложь; //✏
КонецПопытки;
// Тест 18: NestKeys
TestsTotal= TestsTotal+ 1; //✏
Попытка
Плоская = Of("user_profile_name", "John", "user_profile_address_city", "NY", "order_id", 456, "simple_key", "value"); //✏
Ожидаемая = Of("user", Of("profile", Of("name", "John", "address", Of("city", "NY"))), "order", Of("id", 456), "simple_key", "value"); //✏
Результат = NestKeys(Плоская); //▶️
Если Equals(Результат, Ожидаемая) Тогда //⚡
TestsPassed = TestsPassed + 1; //✏
Иначе
Сообщить("✗ Тест 18 (NestKeys): ПРОВАЛЕН"); //▶️
Успешно = Ложь; //✏
КонецЕсли;
Исключение
Сообщить("✗ Тест 18 (NestKeys): ИСКЛЮЧЕНИЕ - " + ОписаниеОшибки()); //▶️
Успешно = Ложь; //✏
КонецПопытки;
// Итоги тестирования
Если TestsPassed = TestsTotal Тогда //⚡
Сообщить("=== A1sDS.SelfTest() SUCCESS: " + Строка(TestsPassed) + "/" + Строка(TestsTotal) + " ===");
Возврат Истина; //↩
Иначе
Сообщить("=== A1sDS.SelfTest() FAILED: " + Строка(TestsPassed) + "/" + Строка(TestsTotal) + " ===");
Возврат Ложь; //↩
КонецЕсли;
КонецФункции
#КонецОбласти
#Область Fluent
// On
/// ✦ On — Создать Fluent-обёртку для работы со структурой
/// ➤ ИсходнаяСтруктура (Структура, Неопределено) — Начальная структура или пусто для новой
/// ⬅ A1sDP_DS — Обработка с fluent-интерфейсом
///
/// Позволяет работать со структурой в fluent-стиле:
/// Результат = A1sDS.On()
/// .Set("Имя", "Иван")
/// .Set("Возраст", 30)
/// .Set("Город", "Москва")
/// .Value();
///
/// Пример 1 — Создание новой структуры:
/// Пользователь = A1sDS.On()
/// .Set("Имя", "Иван")
/// .Set("Email", "ivan@test.ru")
/// .Set("Роль", "admin")
/// .Value();
///
/// Пример 2 — Трансформация существующей:
/// БезСекретов = A1sDS.On(APIОтвет)
/// .Omit("token,secret,password")
/// .Rename(A1sDS.Of("firstName", "Имя", "lastName", "Фамилия"))
/// .Value();
///
/// Пример 3 — Конфигурация с дефолтами:
/// Конфиг = A1sDS.On(ПользовательскиеНастройки)
/// .Defaults(A1sDS.Of("Порт", 8080, "Таймаут", 30, "SSL", Ложь))
/// .Value();
///
/// Пример 4 — Комплексная цепочка:
/// Результат = A1sDS.On()
/// .Set("Статус", "Активен")
/// .SetIf("Премиум", Истина, ПодпискаОплачена)
/// .Merge(ДанныеПрофиля)
/// .Merge(НастройкиПользователя)
/// .Omit("ВнутренниеДанные")
/// .Pick("Имя,Email,Статус,Премиум")
/// .Value();
///
/// Пример 5 — Получение фиксированной структуры:
/// Константы = A1sDS.On()
/// .Set("PI", 3.14159)
/// .Set("E", 2.71828)
/// .ToFixed();
///
/// Доступные методы-трансформаторы (возвращают ЭтотОбъект):
/// .Set(Ключ, Значение) — установить значение
/// .SetIf(Ключ, Значение, Усл) — установить по условию
/// .SetMany(Структура) — установить несколько значений
/// .Remove(Ключ) — удалить ключ
/// .Pick("Ключ1,Ключ2") — оставить только указанные
/// .Omit("Ключ1,Ключ2") — исключить указанные
/// .Merge(Структура) — слить (перезаписывает)
/// .Defaults(Структура) — применить дефолты (не перезаписывает)
/// .Rename(Карта) — переименовать ключи
/// .DeepCopy() — глубокое копирование
/// .Clear() — очистить
/// .Transform(Ключ, "выражение") — преобразовать значение
///
/// Доступные методы-терминаторы (возвращают значение):
/// .Value() — получить структуру
/// .ToFixed() — получить фиксированную структуру
/// .Get(Ключ) — получить значение по ключу
/// .Keys() — получить массив ключей
/// .Values() — получить массив значений
/// .HasKey(Ключ) — проверить наличие ключа
/// .IsEmpty() — проверить пустоту
/// .Count() — получить количество ключей
/// .ToString() — преобразовать в строку
/// .ToJSON() — преобразовать в JSON
///
&AtServer
Функция On(Знач ИсходнаяСтруктура = Неопределено) Экспорт //⚙
ib = "Создание fluent-обёртки для структуры"; //✍
Обработка = Обработки.A1sDP_DS.Создать(); //✏
Обработка.Init(ИсходнаяСтруктура); //▶️
Возврат Обработка; //↩
КонецФункции
// ============================================
// ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ (для документации)
// ============================================
#Область ПримерыFluent
// @example.1.A1sDS.On — Базовое создание структуры {
//------------------------------------------------
// // БЫЛО (традиционный подход)
// Пользователь = Новый Структура;
// Пользователь.Вставить("Имя", "Иван");
// Пользователь.Вставить("Возраст", 30);
// Пользователь.Вставить("Город", "Москва");
// Пользователь.Вставить("Активен", Истина);
//
// // СТАЛО (fluent-стиль)
// Пользователь = A1sDS.On()
// .Set("Имя", "Иван")
// .Set("Возраст", 30)
// .Set("Город", "Москва")
// .Set("Активен", Истина)
// .Value();
//------------------------------------------------
// }
// @example.2.A1sDS.On — Фильтрация данных API {
//------------------------------------------------
// // Получили ответ от API с лишними полями
// APIОтвет = ПолучитьДанныеОтСервиса();
//
// // Убираем секретные данные и переименовываем
// Публичные = A1sDS.On(APIОтвет)
// .Omit("token,secret,internal_id,password_hash")
// .Rename(A1sDS.Of(
// "first_name", "Имя",
// "last_name", "Фамилия",
// "email_address", "Email"
// ))
// .Value();
//------------------------------------------------
// }
// @example.3.A1sDS.On — Конфигурация с умолчаниями {
//------------------------------------------------
// // Пользователь передал только часть настроек
// ПользовательскиеНастройки = A1sDS.Of("Хост", "prod-server");
//
// // Применяем дефолты
// ИтоговаяКонфигурация = A1sDS.On(ПользовательскиеНастройки)
// .Defaults(A1sDS.Of(
// "Хост", "localhost",
// "Порт", 8080,
// "Таймаут", 30,
// "SSL", Ложь,
// "ПулСоединений", 10
// ))
// .Value();
//
// // Результат: {Хост: "prod-server", Порт: 8080, Таймаут: 30, SSL: Ложь, ПулСоединений: 10}
//------------------------------------------------
// }
// @example.4.A1sDS.On — Условное добавление полей {
//------------------------------------------------
// ЕстьПодписка = ПроверитьПодписку(Пользователь);
// ЕстьФото = ЗначениеЗаполнено(Пользователь.Фото);
//
// Профиль = A1sDS.On()
// .Set("ID", Пользователь.ID)
// .Set("Имя", Пользователь.Имя)
// .SetIf("Премиум", Истина, ЕстьПодписка)
// .SetIf("Фото", Пользователь.Фото, ЕстьФото)
// .SetIf("ДатаОкончанияПодписки", Пользователь.ДатаПодписки, ЕстьПодписка)
// .Value();
//------------------------------------------------
// }
// @example.5.A1sDS.On — Слияние нескольких источников {
//------------------------------------------------
// // Данные из разных источников
// БазовыеДанные = ПолучитьИзБД(ID);
// КэшДанные = ПолучитьИзКэша(ID);
// СессионныеДанные = ПолучитьИзСессии();
//
// // Объединяем с приоритетом (последний перезаписывает)
// ПолныеДанные = A1sDS.On(БазовыеДанные)
// .Merge(КэшДанные)
// .Merge(СессионныеДанные)
// .Set("ДатаЗагрузки", ТекущаяДата())
// .Value();
//------------------------------------------------
// }
// @example.6.A1sDS.On — Создание иммутабельных констант {
//------------------------------------------------
// // Фиксированная структура — нельзя изменить после создания
// МатематическиеКонстанты = A1sDS.On()
// .Set("PI", 3.14159265358979)
// .Set("E", 2.71828182845905)
// .Set("PHI", 1.61803398874989)
// .ToFixed();
//
// // МатематическиеКонстанты.PI = 3; // ОШИБКА! Изменение запрещено
//------------------------------------------------
// }
// @example.7.A1sDS.On — Работа с API запросами {
//------------------------------------------------
// Функция СформироватьЗапросAPI(Метод, Ресурс, Данные = Неопределено)
//
// Запрос = A1sDS.On()
// .Set("method", Метод)
// .Set("url", БазовыйURL + Ресурс)
// .Set("headers", A1sDS.Of(
// "Content-Type", "application/json",
// "Authorization", "Bearer " + Токен
// ))
// .SetIf("body", Данные, Данные <> Неопределено)
// .Set("timeout", 30)
// .Value();
//
// Возврат Запрос;
//
// КонецФункции
//------------------------------------------------
// }
// @example.8.A1sDS.On — Сравнение с A1sDS.Of {
//------------------------------------------------
// // A1sDS.Of — для простых случаев (до 20 пар)
// Простая = A1sDS.Of("X", 10, "Y", 20, "Z", 30);
//
// // A1sDS.On — когда нужна логика/трансформации
// Сложная = A1sDS.On()
// .Set("X", 10)
// .Set("Y", 20)
// .SetIf("Z", 30, УсловиеZ)
// .Defaults(Умолчания)
// .Omit("Временные")
// .Value();
//
// // A1sDS.On(существующая) — для трансформации
// Изменённая = A1sDS.On(Существующая)
// .Pick("НужныеПоля")
// .Rename(КартаПереименования)
// .Value();
//------------------------------------------------
// }
#КонецОбласти
#КонецОбласти