Bitwise 多項目單選應用

大概就是最近要用整理一下,一個情境,使用者要不要發送 XXX 的 Mail,類似站內通知交易通知等等,而這種東西非常非常囉唆,通常要存多個 column,但一個 column 卻只有一個值,還是只有 boolean…

其實 Linux 之類的一直都有應用這類的東西,類似這邊所說的(內容太細了不引用)

http://linux.vbird.org/linux_basic/0210filepermission.php

所以開到最寬鬆的權限會是 777,3 個欄位 3 個值,而不是開 9 個欄位(當然你也可以塞成一大個,不過人類很難判別就是…)

所以以下,剛寫的範例 code

class Bitwise
  CEHCK_ITEM = [:is_send_mail,:is_girl,:is_happy,:is_child]
  CHECK_LEN  = CEHCK_ITEM.length # "1111".to_i(2) #=> 4095 int 最大單位
  CHECK_DEFAULT = 0

  def initialize
    @bitwise = CHECK_DEFAULT
  end

  def get_check_items
    ans = []
    CHECK_LEN.times do |offset|
      ans << CEHCK_ITEM[offset] if @bitwise >> offset & 1 == 1
    end
   return ans
  end

  def get_check_item(item)
    return @bitwise >> select_item(item) & 1 == 1
  end

  def set_check_item(item , is_checked)
    index = select_item(item)

    #check item status first , check use xor(^)
    if is_checked ^ (@bitwise >> index & 1 == 1)
      # xor to swap bit
      @bitwise ^= 1 << index
    end
    return true
  end

  private
  def select_item(item)
    index = CEHCK_ITEM.index(item)
    #無此項目,直接發 error
    raise StandardError.new("CHECK_ITEM does not include #{item}") unless index
    return index
  end
end

應用

Bitwise::CEHCK_ITEM
#=> [:is_send_mail, :is_girl, :is_happy, :is_child]
t = Bitwise.new
t.set_check_item(:is_girl , true)
t.set_check_item(:is_child , true)
t.set_check_item(:is_send_mail , true)
t.get_check_items
# => [:is_send_mail, :is_girl, :is_child]
t.set_check_item(:is_send_mail , false)
t.get_check_items
# => [:is_girl, :is_child]
t.get_check_item(:is_girl)
# => true
t.get_check_item(:is_send_mail)
# => false

大概就這樣而已,這份可以寫入 model 內或寫成 module include 進去,存只要存 @bitwise 就好,或是把它換成你的欄位名稱

再來就是一定要注意位數,假設如果有 32 個項目

("1" * 32).to_i(2)
#=> 4294967295

而 Rails 的 mysql 的預設 int 欄位是有正負號的,length 是 4 個 byte,對照表在這邊

http://dev.mysql.com/doc/refman/5.7/en/integer-types.html

所以最高存到 2147483647,原值 4294967295 會超過這個數字哩,所以看是要切欄位還是開成 8 byte 才是( 4 之後就跳 8 了 ),大概就這樣而已

這題很考布林的位元操作就是了,如果你看得懂上面所有的 code,你的布林操作就應該沒問題才是,裡面有個特殊點是 ^ (xor) 這個符號同時用在一般 true / false 和布林數值操作上,沒有 ^^,不像 & , && , | , ||,這是比較會混淆的地方才是,以上

2個讚