Побитовые сжатые Логические значения - Переключение Определенных
Предыстория
Мне нужно хранить несколько значений true/false в базе данных, и вместо того, чтобы создавать много столбцов, я вместо этого использую один столбец int
, в котором я храню все логические значения, сжатые с помощью побитовых операторов.
Пример:
Product 1
сертифицирован как:
Lead Free - Yes
Gluton Free - No
Free Range - Yes
Organic - Yes
Поэтому в БД, в столбце certs
, я бы сжал 1
, 0
, 1
, 1
в 13
(1 + 4 + 8).
Я написал функции быстрой оболочки для сжатия и извлеките эту информацию (измените из int
в массив boolean
и обратно).
Проблема
Я не уверен в лучшем способе быстрого добавления и удаления значений из этого. Скажем, я хочу обновить продукт, чтобы он больше не был свободным. Я могу взять сжатый int, минус 4
. Это работает. За исключением того, что, если это уже не был Свободный выгул? Если я выполняю массовые операции, мне нужно, чтобы моя функция удаляла только 4
из продуктов, которые в настоящее время сертифицированы в свободном ассортименте, иначе номер будет перепутан вверх.
Я понял это с помощью длинного заявления if
, но это не элегантно. Это работает:
// $certs_current is the compressed int from the DB.
// $certs_new is a new integer. 4 would mean toggle "Free Range" to true. -4 would mean toggle it to false.
if ( $certs_current & abs($certs_new) ) {
if ( $certs_new < 0 ) {
$certs_current += $certs_new;
}
} else {
if ( $certs_new > 0 ) {
$certs_current += $certs_new;
}
}
Это много if
утверждений. Я поиграл с оператором |
, но он работает только для добавления положительных чисел. Передайте ему -4
, и он сломается. И я не могу понять оператора nor
.
Есть ли лучший способ сделать это?
Редактировать:
Другими словами, есть ли оператор, где: Если я дам ему 13
и -4
, он будет дай мне 9
. Но если я дам ему 9
и -4
, он даст 9
? Или даже просто 13
и 4
.
Редактировать 2
Добавив в |
свою рабочую логику, я уменьшил сложность и все еще работаю:
if ( $certs_new > 0 ) {
$certs_current = ( $certs_current | $certs_new );
} else {
if ( $certs_current & abs($certs_new) ) {
$certs_current += $certs_new;
}
}
В основном это говорит о том, что если новое число положительное, то используйте оператор |
, чтобы добавить его, если его необходимо добавить. Если он отрицательный, то мы сначала проверяем, нужно ли его удалять, и если да, то удаляем его. Это то место, где я думаю, мы сможем исправиться.
|
заменяет оператор if
, потому что он добавляет 4
только в том случае, если 4
еще не переключено на true (я понял, что это странный способ его описания).
Но с отрицательным числом у меня все еще есть вложенный оператор if
, который я хочу удалить, если это возможно.
2 answers
У вас есть сжатое значение, так почему бы вам не расширить его перед выполнением своих операций?
$certs_temp = $certs_current
$is_organic = $certs_temp >= 8 && $certs_temp -= 8
$is_freerange = $certs_temp >= 4 && $certs_temp -= 4
$is_glutenfree = $certs_temp >= 2 && $certs_temp -= 2
$is_leadfree = $certs_temp >= 1 && $certs_temp -= 1
, А затем повторно сжать с измененными значениями (если действительно что-то изменилось).
Я думаю, что вы напрашиваетесь на неприятности, если попытаетесь работать непосредственно с сохраненным целым числом, которое на самом деле представляет собой 4 отдельных двоичных флага. Либо представляйте каждый флаг отдельно в БД, либо, если он должен храниться в виде целого числа, декодируйте его перед применением изменений или логики.
Ответ
Я понял это. Чтобы переключить логическое значение на false, нам нужно:
Реверсируйте первичный сжатый int из БД, используя
~
(13
в примере)Добавьте абсолютное значение нового int
4
(не-4
), используя|
.Повторите полный int еще раз, используя
~
.
Итак, окончательный код, содержащий только одно утверждение if
:
if ( $certs_new > 0 ) {
$certs = ( $certs | $certs_new );
} else {
$certs = ~( ~$certs | abs($certs_new) );
}
// 13 and 4 .... 13
// 13 and 2 .... 15
// 13 and -4 .... 9
// 13 and -2 .... 13