Angular JS ビット演算はfilterで効率よく処理しよう

AngularJSでWebシステム開発 Angular JS
AngularJSでWebシステム開発

ビット演算って何?

コンピュータは0と1の信号で、2は理解できません。0と1しか理解できないなら、桁を増やせばいいじゃない。ということでコンピュータは膨大な桁を処理しています。だからコンピュータは2を表現する時、10(イチゼロ)と表現します。桁が上がってしまうんですね。
同じ仕組で
3=11
4=100
5=101
6=110
7=111
8=1000
9=1001
10=1010
という具合です。これを2進数といいます。コンピュータリテラシーの基本のキホンですね。

ビット演算を使うメリットと使いみち

ビット演算っていうのは、この2進数の値で計算をする、割とコンピュータよりな計算方式です。ビット演算はうまく使うと便利です。
例えば、ユーザ毎に機能の有効無効を判断する必要があったとします。スイッチは8個としましょう。
機能1・機能2〜機能8

あるユーザが、機能1〜機能8のうち、どの機能が有効でどの機能が無効か判断する時、一番簡単に思いつくのは、おそらくこんな処理でしょう

var enableFunc1 = true;
var enableFunc2 = false;
var enableFunc3 = true;
//略

これでも正解です。しかしいちいち変数を8個用意するのが大変です。配列やオブジェクトでやってもいいんだけど、まぁいずれにせよその分の箱を用意するのが大変です。
とくにユーザの機能が有効か無効かの状態を、MySQLに保存するとなると、それだけのフィールドを用意する必要があります。

enableFunc1フィールドつくって・・・1enableFunc2フィールド作って・・・
使えるかどうかの判断はクエリを書いて、えぇと
select enableFunc1 , enableFunc2 ,enableFunc3 from user where userID = ・・・

結構たいへんです。こんなときに使えるのがビット演算です。
例えば、userStatus という変数1個に、1〜8種類のスイッチのオンオフ状態を保存できます。
仮に、userStatus = 5だとしましょう。
5は、2進数で101です。先頭のゼロを省略せずに書くと
0000 0101 です。

このとき、1のついてるところはスイッチが有効で、0のついてるところはスイッチが無効と考えると、変数1個で8種類のスイッチのオンオフ状態を判断できるようになります。

あとは、任意の桁のビットを取り出せれば、スイッチが有効か無効か判断できます。取り出すときは、2の倍数の値で、&のビット演算すればいいんです。

1桁目:1 (2進数 :0000 0001)
2桁目:2 (2進数 :0000 0010)
3桁目:4 (2進数 :0000 0100)
4桁目:8 (2進数 :0000 1000)
5桁目:16 (2進数 :0001 0000)
6桁目:32 (2進数 :0010 0000)
7桁目:64 (2進数 :0100 0000)
8桁目:128 (2進数 :1000 0000)

おしゃれに16進数で表記したり、シフト演算でスマートに書くこともできますが、まぁそれは置いといて、先の例に戻りましょう。userStatus=5です。
5は、0000 0101でしたね。
1桁目のビットを取り出すには、1桁目のマスクを使います。つまり1でマスクします。
&は、両方真の時真なので
0000 0101 ( 5です)
0000 0001 ( 1です)
0000 0001 ( 結果)
ということになります。結果が0じゃないので、if文で書くとこれは真として評価されますね

if(5 & 1 ){
 //スイッチ1番が有効な処理が書ける
}

あとは同じ理屈なんです。よって

if(5 & 1){
//スイッチ1が有効
}
if(5 & 2){
//スイッチ2が有効
}
if(5 & 4){
//スイッチ3が有効
}
//略

うまく説明できてないなぁって、ブログ書きながら思ってます。ビット演算についてもっとわかりやすいサイトが沢山あるので、そちらを参照してみてください。

Angular JSでビット演算のフィルターを作る

ビット演算を使った、Angualr JSのサンプルコードです。この例では、1この変数bitが、1〜8のスイッチの状態を判断します。
無効になっているスイッチは、画面に表示しません
まずはHTMLのソース

<div class="btn-group">
  <label class="btn btn-success" ng-model="radioModel"  uib-btn-radio="'1'"   ng-show="bit | mask:1"  >スイッチ1</label>
  <label class="btn btn-success" ng-model="radioModel"  uib-btn-radio="'2'"   ng-show="bit | mask:2"  >スイッチ2</label>
  <label class="btn btn-success" ng-model="radioModel"  uib-btn-radio="'3'"   ng-show="bit | mask:4"  >スイッチ3</label>
  <label class="btn btn-success" ng-model="radioModel"  uib-btn-radio="'4'"   ng-show="bit | mask:8"  >スイッチ4</label>
  <label class="btn btn-success" ng-model="radioModel"  uib-btn-radio="'5'"   ng-show="bit | mask:16" >スイッチ5</label>
  <label class="btn btn-success" ng-model="radioModel"  uib-btn-radio="'6'"   ng-show="bit | mask:32" >スイッチ6</label>
  <label class="btn btn-success" ng-model="radioModel"  uib-btn-radio="'7'"   ng-show="bit | mask:64" >スイッチ7</label>
  <label class="btn btn-success" ng-model="radioModel"  uib-btn-radio="'8'"   ng-show="bit | mask:128">スイッチ8</label>
</div>

ng-showでフィルターを使います。変数bitの値に応じて、スイッチ1〜8を表示したり非表示にしたりします。
続いてJavaScript

.controller('sampleCtrl' , function($scope){
    $scope.radioModel = '1';
    $scope.bit = 5;
//略

スイッチ1〜8の状態を表す変数bitには、整数の5がセットされました。
(通常この値は、サーバなりどこなりに保存されているものです)
最後に、フィルターのソース


  .filter('mask' , function(){
    return function(data , key){
      if(data & key){
        return 1;
      }else{
        return 0;
      }
    }
  })

ソース自体はシンプルです。

補足すると、dataは$scope.bitの値です。つまりここでは常に5が入ってます。
keyは、それぞれのスイッチのng-sohwから渡されてきます。1,2,4,8,16,32,64,128のどれかが入ってます。
&で計算した結果、0なら当該ビットは0(つまり当該スイッチは無効)ということですので、0を返します。
0以外の場合はビットが立っているので、つまり当該スイッチは有効と判断できます。よって1を返します。