Есть коллекция с типом Соответствие. Нужно программно узнать, есть ли в ней некоторый ключ. В структуре, например, есть специальный метод "Свойство()", а как это лучше сделать?
Я вижу следующие способы:
1. Перебрать все ключи в цикле - мне не нравится, т.к. в коллекции может быть много элементов
2. Получить количество элементов в коллекции, добавить нужный ключ, после добавления проверить, изменилось ли количество элементов - такой способ кривой, да и требует внесения изменений
3. Обычный способ "Если Коллекция[Ключ] = Неопределено Тогда ..." - также не проходит, т.к. в коллекции может быть элемент, с таким ключом, и значением равным Неопределено
Если при обращении к элементу коллекции с помощью оператора [], указан несуществующий ключ, будет вызвано исключение.
В то же время метод Получить, в параметре которого указан несуществующий ключ, возвращает значение Неопределено.
В первом случае сделает исключение, а не вернет Неопределено - может это решение Вашей проблемы?
(1) Навскидку: получить значение по ключу. Если оно = Неопределено, запомнить количество ключей методом Количество() и вставить этот же ключ со значением Неопределено в соответствие. Сравнить кол-во с запомненным. Если не совпадает, ключа не было в оригинале. Удалить этот ключ.
(4) Вы правы. Единственный выход - это попытаться избегать значений Неопределено, либо предварительно обработать соответствие, заменив значения Неопределено на NULL, например.
(10) избегать значения Неопределено - не всегда возможно. У меня например коллекция приходит в виде JSON, который может быть многоуровневым. Если в JSONе значение равно NULL, то 1С его преобразует к Неопределено
ПроверочнаяСтруктура = Новый Структура("Ключ", "ЗНАЧЕНИЕ_КОТОРОЕ_НИКОГДА_НЕ_ВСТРЕТИТСЯ");
ЗаполнитьЗначенияСвойств(ПроверочнаяСтруктура, Коллекция);
Если ПроверочнаяСтруктура.Ключ = "ЗНАЧЕНИЕ_КОТОРОЕ_НИКОГДА_НЕ_ВСТРЕТИТСЯ" Тогда
// не было такого ключа
КонецЕсли;
Придумал еще один способ:
1. Получить значение по ключу
2. Если значение = Неопределено, то
- запомнить количество,
- удалить значение
- проверить сколько элементов осталось
- если уменьшилось, то добавить его на место и вернуть истина
Коллекция =Новый Соответствие();
Коллекция.Вставить("МойКлюч",НЕОПРЕДЕЛЕНО);
Если Коллекция.Получить("МойКлюч")=Неопределено тогда
// мой код
КонецЕсли;
в этом случае мой код не выполнится, а ключ в коллекции есть
Функция СоответствиеСодержитКлюч(Соответствие, Ключ)
Если НЕ Соответствие[Ключ] = Неопределено Тогда
Возврат Истина;
КонецЕсли;
КоличествоЭлементов = Соответствие.Количество();
Соответствие.Удалить(Ключ);
Если КоличествоЭлементов = Соответствие.Количество() Тогда
Возврат Ложь;
КонецЕсли;
Соответствие.Вставить(Ключ);
Возврат Истина;
КонецФункции
(17) А с чего общество решило что Соотвествие[Ключ] это не цикл перебора коллекции ? И если вдруг это так, то для того, что бы избежать цикла перебора в коде 1С. запускают его в предприятии а потом еще и вставляют и удаляют значения. Это вот точно быстрее и оптимальнее?
в коллекции потенциально может быть и 100 и 1000 элементов, мне не подходит такой вариант
повторное использование тоже не прокатит:
мне сначала нужно найти один ключ - я перебираю всю коллекцию, запоминаю,
потом мне нужно найти второй ключ - я снова перебираю всю коллекцию, запоминаю,
и таких проверок может быть много. Нет выигрыша от повторного использования
(30) Сделайте замеры) мне кажется что перебор все же довольно быстрая операция для соответствия.
А ключи какого типа - Строка? А то может преобразовать, например в строку json или еще что и искать наименование в строке...
(30) решение в (17) очень красивое. Эту красоту нужно просто увидеть.
Запись в соответствие по определению быстрее поиска. Исходные данные при выходе из обработки остаются прежними.
Что еще нужно?
(32) Исходя из чего вы решили что запись в соотвествие быстрее поиска? На чем основана уверенность что запись не ищет элемент по ключу что бы заполнить ему значение? Или если не ищет, а просто вставляет новый элемент, то это не потенциальная утечка памяти?
(51) просто помимо 1С необходимо знать и другие ЯП и понимать механику их работы.
Соответствие это аналог map в других языках. И обращение по ключи использует функцию вычисления хэш. Из-за этого и скорость записи мало отличается по скорости записи в массив по индексу (добавляется вычисление хэш), а не использует поиск по самому ключу.
PS. кстати, именно из-за использования хэш и дает возможность использовать в качестве ключа любой тип данных, так как все равно по нему будет вычисляться хэш, который и будет использоваться.
(51) и еще. Что подразумевать под поиском в соответствии.
Гарантированный поиск в соответствии возможен только полным перебором, пока не найдется ключ или до конца коллекции.
Так как прямого получение значение Неопределено, не гарантирует, что такого ключа нет. Это может быть значение по существующему ключу. Вот и получается, что поиск по коллекции(с перебором самой коллекции) значительно медленнее самой записи.
(55) Если залезть под капот, то по ключу мы вычисляем хэш и дальше так же перебором уже по хэшам, не так ли?
//я понимаю что дело в количестве условных перекладываний объектов из сущности в сущность и оптимизации функций сравнения разных и что переложить в хэш и сравнить последовательность до первого разного байта это статистически быстрее чем подбирать функции сравнения сложных объектов или массива очень длинных строк, различающихся в последнем символе.
(57) Тогда лезем под капот капота. пусть хэш это у нас индекс. Мы получили ключ входным параметром. преобразовали в хэш и дальше у нас задача понять есть ли такой хэш в нашей коллекции, условно. Задача упростилась т.к. привели ключи одному типу.
ПС. в итоге мы имеем задачу оптимизации количества процессорных команд, и постулат такой что преобразование хранения данных с ключем-хэшем статитситчески быстрее. С этим я спорить не буду.
В А получили Неопределено.
Ключ существует?
Получение данных из соответствия и поиск в соответствии это два принципиально разных действия.
Получение данных из соответствия так же довольно быстрый, так как так же получение по хэш. Это почти тоже самое, что и получение данных из массива по индексу. Но есть принципиальное отличие. Если обратиться к массиву с несуществующим индексом, то получим исключение. Если обратиться к соответствию с несуществующим ключом получим Неопределено. Но где гарантия, что полученное Неопределено это отсутствие ключа, а не такое значение по существующему ключу? Вот тут и кроится дилемма, так как соответствие не может дать такую гарантию и единственно гарантированный поиск в соответствии - полный перебор самой коллекции.
ИскомыйКлюч = "Мой Ключ";
КлючьНайден = Ложь;
Для каждого Зн из Соответствия цикл
Если Зн.Ключ = ИскомыйКлюч Тогда
КлючьНайден = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
(33), (35)
видимо, вы путаете с запись в оперативную память с записью в базу данных))
что такое запись в коллекцию - это почти то же самое, что и присвоение переменной. В случае обхода в цикле будет присвоений столько же, сколько элементов в коллекции (в худшем случае) - я имею в виду конструкцию "для каждого КлючИЗначение из Коллекция цикл". Плюс будет проверка на равенство.
В случае же поиска через Получить(), он будет выполняться более эффективными алгоритмами
Поэтому, если рассуждать логически, то способ 17 должен быть быстрее. Но как было справедливо замечено, нужно провести тест. Может позже я его сделаю
я хочу сказать, что если исключить ключ Неопределено, то метода Получить() вполне достаточно.
технологии все больше уходят в сферу вероятностей или как говорят математики необходимых и достаточных условий когда просто глупо тратить ресурсы на то, что пренебрежительно мало и по логике точно ничего не содержит, а если содержит, то его создал идиот и ценность этого творения ничтожна.
(43) напрасно. Нужно просто уметь готовить (с).
Иногда само значение элемента коллекции не важно. Важно само наличие этого элемента, как триггера.
Иногда приходится отказываться от соответствия в пользу структуры, только из-за поиска ключа.
Это недокументированное поведение компилятора что СО[1] без "=" справа возвращает объект "неопределено", а с "=" справа возвращает объект КиЗ принадлежащий Со?
Переписывать компилятор под объект соответствие ради этого отдельного финта очень спорное дело, но других идей так то и нет нет.
Это недокументированное поведение компилятора что СО[1] без "=" справа возвращает объект "неопределено", а с "=" справа возвращает объект КиЗ принадлежащий Со?
А что тут не понятно?
Это равнозначно:
СО = Новый Соответствие;
А = СО[1];
СО[1] = А;
А получит значение Неопределено, так как Ключа нет и по умолчанию вернется Неопределено.
Далее присваиваем в соответствие по ключу значение Неопределено. Теперь в соответствии есть ключ и значение.
Если еще раз получить:
А = СО[1];
то вернется все то же Неопределено, только это уже будет не значение по умолчанию, а реальное значение по ключу.
И отличить это можно только полным перебором всего соответствия, до нахождения ключа, или до конца.
(61)
Т.е. компилятор переписан под соответствие, выходит что так.
Т.е. действие оператора получения элемента коллекции "[]" на объект Соответствие отличается от действия суммы операторов "[] =" на этот же объект Соответсвие.
В первом случае возврат Неопределено, во воторм случае преобразуем "[] =" в "Новый КиЗ.Значение = "
Не уверен что это хорошая практика перепиливать компилятор под объекты, но придется просто запомнить.
Однако и та околонулевая частота применения этого финта говорит о том что пилить в компилятор какие то такие исключения под конкретный объект очень спорная практика.
(62) что-то не туда вас занесло.
Все просто.
Есть lvalue и есть rvalue.
Что скажите про такой код:
СО = Новый Соответствие;
СО[1] = СО(1);
Для уточнения, СО(1) это вызов функции, а не получение значения соответствия по ключу.
Но так наглядно.
Чтобы присвоить значение lvalue, сначала вычисляется rvalue. А это результат функции. Т.е. сначала вызывается функция, получается результат этой функции, и только после этого присваивается полученное значение левой переменной.
Так и при таком коде:
СО = Новый Соответствие;
СО[1] = СО[1];
Сначала получается значение rvalue. Потом это значение присваивается lvalue.
Никаких специальных ухищрений компилятора не делается. Все отрабатывает штатно.
(63) Коллега, давайте станем на место компилятора.
Видим оператор "[]" который действует на операнд "СО" со вторым операндом [ВторойОперанд]
Действие оператора [] на коллекцию это обычно получение элемента коллекции.
Соотвествие это тоже коллекция, по крайней мере оператор "Для Каждого" на нее действует.
Т.е. по общей логике действие оператора [] на эту коллекцию ожидается как получение элемента коллекции по некоему ключу.
Но если с Метаданные[НеизвестнаяФигня] вызовет скорее исключение, то в случае с
Соответствие[НеизвестнаяФигня] - явно оператор для соответствия переопределен, т.е. соответствие уже не совсем коллекция.
Это половина истории.
Потому что связка операторов "[] = "-уже совсем заводит в тупик, т.к. к какой еще коллекции применима такая последовательность операторов? Потому что левый операнд от "[] =" - КлючИЗначение.Значение.
Компилятор для соотвествия заменяет " СО[Ключ] = значение " на Соответствие.Вставить(Ключ,Значение), что не является ожидаемым действием связки операторов [] = для никакой больше коллекции, насколько я помню. По крайней мере для массива мы получим исключение и т.д.
ПС. Это я к тому, что компилятор выглядит переписанным под объект Соответствие, что на мой взгляд - плохой стиль. А переписание еще и пары операторов так на мой опять же субъективный взгляд уже крайне плохой стиль, как бы изящно это не выглядело в коде. Потому что эта изящность - это исключение которое надо будет помнить и которое в отсутствии жесткой типизации 1С выглядит вдвойне спорно, т.е. СО мы можем и переопределить в концепции 1С, а с ним и переопределится действие всех операторов на этот объект.
Видим оператор "[]" который действует на операнд "СО" со вторым операндом [ВторойОперанд]
Где нашли "ВторойОперанд"? Это вообще не операнды.
Т.е. по общей логике действие оператора [] на эту коллекцию ожидается как получение элемента коллекции по некоему ключу.
Считаете, что все получения по ключу из разных типов коллекций отрабатываются одним алгоритмом? Серьезно?
Читайте СП. Для соответствия, если указанный ключ отсутствует.возвращается Неопределено.
Тут нет никаких сложностей.
Все уже озвучено выше.
(65) Да для меня не вопрос если действие операторов на разные объекты разное, в конечном итоге действие оператора "+" в выражении 3+4 действует совсем иначе чем "три" + "четыре".
Но это изначально стремный путь зависимости операторов от операндов. Хотя бы в том плане что мы не можем:
Вроде бы все эти "универсальные коллекции значений" - коллекции и явно наследуют от некой протоколлекции, ан нет. Они жестко зашиты на уровне компилятора и только это уже говорит о том что хрен нам, а не собтвенные объекты.
На мой взгляд это не сложность, это хуже, это ошибка.
Потому что встретив в коде Коллекция[Ключ] = Значение - при отсутствии жесткой типизации вы не знаете как эта конструкция себя поведет. Прог ДОЛЖЕН помнить что где то глубоко в его коде корректно его код (о котором он забыл три года назад) работает только с соответствием, или с массивом.
А этот код может со временем опуститься в глубокое закрытое пятью верхними обертками легаси.
(66) с добрым утром. Соответствие это не уникальная коллекция присущая 1С.
Аналогичные коллекции есть и в других ЯП. С аналогичным поведением, возвращающим null, если нет ключа.
И снова путаете понятие операндов.
(17)
Понимаю, что тема мягко говоря не актуальная )
Но тоже заморочился с подобной проверкой и вот что получилось:
ЕстьКлюч = соотв[Ключ] <> неопределено;
Если НЕ ЕстьКлюч Тогда
//на этом этапе ещё не ясно, имеется ли ключ, или только его значение "неопределено"
//поэтому запомним количество ключей в коллекции
КоличествоКлючейКоллекции = соотв.Количество();
//явно вставим, тут ничего страшного в перезаписи нет, так как исходное значение и так "неопределено"
соотв[Ключ] = неопределено;
//если количество ключей изменилось, значит ключа не было
ЕстьКлюч = КоличествоКлючейКоллекции = соотв.Количество();
Если НЕ ЕстьКлюч Тогда
//подчистим
соотв.Удалить(Ключ);
КонецЕсли;
КонецЕсли;
Показать
Для фиксированных соответствий достаточно следующего кода:
Попытка
ЕстьКлюч = фсоотв[Ключ] = истина или истина;
Исключение
ЕстьКлюч = ложь;
КонецПопытки;
так как - цитата из СП:
Если при обращении к элементу коллекции с помощью оператора [], указан несуществующий ключ, будет вызвано исключение.
В то же время метод Получить, в параметре которого указан несуществующий ключ, возвращает значение Неопределено.
(46) У Вас какой-то свой СП. Или Вы видите между строк. Не вижу такой инфы, что "Если при обращении к элементу коллекции с помощью оператора [], указан несуществующий ключ, будет вызвано исключение."
(47) это похоже у вас персональный СП, отличный от оригинала.
ФиксированноеСоответствие (FixedMap)
Элементы коллекции:
КлючИЗначение
Для объекта доступен обход коллекции посредством оператора Для каждого … Из … Цикл. При обходе выбираются элементы соответствия.
Возможно обращение к значению элемента посредством оператора [...]. В качестве аргумента передается значение ключа элемента.
Методы:
Количество (Count)
Получить (Get)
Конструкторы:
Из соответствия
Описание:
Представляет собой неизменяемую коллекцию пар КлючИЗначение. В качестве ключа может выступать любое значение. Рекомендуется, чтобы в качестве ключа выступало значение примитивного типа или другого типа, значение которого может только присваиваться, но не может менять свое содержимое.
Если при обращении к элементу коллекции с помощью оператора [], указан несуществующий ключ, будет вызвано исключение.
В то же время метод Получить, в параметре которого указан несуществующий ключ, возвращает значение Неопределено.
Показать
Чтобы не было недопонимания откуда указываю про ФиксированноеСоответствие, перечитайте (46):
Для фиксированных соответствий достаточно следующего кода:
Тема старая, но актуальная.
А всего то нужно изменить подход к использованию Соответствия.
Если в Соответствии в качестве Значения всегда использовать Структуру тогда:
Параметр=Параметры["Путь"];
Если НЕ ТипЗнч(Параметр) = Тип("Структура") Тогда
Сообщить ("Нет ключа Путь");
КонецЕсли;
(72)
А чего "не"?
Если не ты логику использования Соответствия задумывал, то конечно "не". В этом случае бояться циклов не стоит. Или костылей с количеством(). В конце концов скопировать в фиксированную структуру, только для того, чтобы через "попытку исключение" проверить существование ключа. А если ты сам задумал логику использования Соответствия, то все ок.
Предлагаю такой вариант решения: завернуть значение ключа в структуру. Т.е. для сохранения значения использовать не само значение, а структуру вида: Новый Структура("Значение", МоеЗначение). Тогда, при извлечении значения, если ключ существует, то значение можно получить из структуры, а если нет - то будет Неопределено. Пример:
Словарь = Новый Соответствие;
Словарь.Вставить("МойКлюч", Новый Структура("Значение", МоеЗначение));
Если Словарь.Получить("МойКлюч") = Неопределено Тогда
Сообщить("Ключа нет");
Иначе
Сообщить("Значение ключа: " + Словарь["МойКлюч"].Значение);
КонецЕсли;