//////////////////////////////////////////////////////////////////////////////// // Модуль: 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(); //------------------------------------------------ // } #КонецОбласти #КонецОбласти