Corredor

ウェブ、プログラミングの勉強メモ。

Key と Value を入れ替えた Map を取得する方法:Java と JavaScript 編

Key と Value を入れ替えた Map (連想配列) を作る方法を調べた。

Java も JavaScript も、PHP でいうところの array_flip() みたいな標準メソッドは存在しなかったので、簡単なやり方を説明する。

TL; DR

  • Java : Commons-Collections with GenericsBidiMap#inverseBidiMap()
  • JavaScript : Underscore.js_.invert()

Java : BidiMap が一番楽

Apache Commons の中の Commons-Collection というライブラリが、コレクションの便利メソッドをたくさん持っている。しかし本家のライブラリはジェネリクスに対応していないので、フォークしてジェネリクスに対応した Commons-Collections with Generics を使ってみる。

以下から collections-generic-4.01.zip をダウンロードし、collections-generic-4.01.jar を Jar ライブラリとして導入する。

そしたら以下のように使う。

// 普通にマップを作る
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("にんじん", 150);
map.put("レタス"  , 200);
map.put("キャベツ", 500);
map.put("キャベツ", 200);
// key が衝突した時は後勝ちになるので、"キャベツ" の value は 200 になる

// BidiMap : Bidirectional Map (双方向マップ) を使い、変数 map を変換する
BidiMap<String, Integer> bidiMap = new DualHashBidiMap<String, Integer>(map);
bidiMap.getKey(200);     // "キャベツ"
bidiMap.get("レタス");   // null
bidiMap.get("キャベツ"); // 200
// #getKey() というメソッドで、value から key を取得できるようになる
// その代わり、重複する value は key と同じように後勝ちになるため、
// 先に宣言された key "レタス" は削除され、同じ value 200 を持つ key "キャベツ" だけが格納されている

// BidiMap#inverseBidiMap() を使うと、key と value を入れ替えた BidiMap が生成できる
BidiMap<Integer, String> flipMap = bidiMap.inverseBidiMap();
flipMap.get(200);           // "キャベツ"
flipMap.getKey("レタス");   // null
flipMap.getKey("キャベツ"); // 200
// 先程、変数 bidiMap を操作した時と、#get() と #getKey() が逆になっている = key と value が入れ替わっている

BidiMap っていうのに普通の Map を取り込むと、双方向マップ、つまり Key と Value が1対1であることを保証する Map が出来上がる。Key と Value を入れ替えた Map に変換するには、Value 側にも重複があってはいけないわけだ。

BidiMap 自体は普通に Map として使えるので、コンストラクタに引数を渡さず、新規で作っても良い。この場合の BidiMap#put() は、value の重複を検知して後勝ちするように自動的に管理してくれる。

BidiMap<String, Integer> bidiMap = new DualHashBidiMap<String, Integer>();
bidiMap.put("にんじん", 150);
bidiMap.put("ほうれんそう", 150);

bidiMap.get("にんじん"); // null
bidiMap.getKey(150);     // "ほうれんそう"
// "にんじん" は、後に put された "ほうれんそう" と同じ value を持っていたので、Map から remove されていた。

JavaScript : Underscore.js が便利

以下のページに、Legacy JavaScript、ES5、jQuery での実装方法が載っている。

Legacy JavaScript だと以下の記事のコードが分かりやすかったので、整形しつつ転載。

/**
 * example: arrayFlip( { a: 1, b: 1, c: 2 } )
 * returns: { 1: "b", 2: "c" }
 */
function arrayFlip(trans) {
  var key;
  var tmpArr = {};
  for(key in trans) {
    if(trans.hasOwnProperty(key)) {
      tmpArr[trans[key]] = key;
    }
  }
  return tmpArr;
}

だが、一番手っ取り早いのは、便利メソッドが沢山つまったライブラリ、Underscore.js を導入することだろう。

上記公式サイトから underscore.js もしくは underscore-min.js (圧縮版) をダウンロードして script 要素で読み込むようにしたり、npm でインストールして require したりしても OK。

そうすると、以下のような関数が使えるようになる。

// 連想配列を用意
var trans = { A: "aaa", B: "bbb", C: "ccc" };

// Underscore.js の _.invert() メソッドで key と value を入れ替えた連想配列を取得する
var result = _.invert(trans);
// result : { aaa: "A", bbb: "B", ccc: "C" };