DACエンジニアブログ:アドテクゑびす界

DACのエンジニアやマーケター、アナリストが執筆するアドテクの技術系ブログです。

【小ネタ】JSで要素の組み合わせを列挙する

下記のような、キーの数や要素数が可変のデータの組み合わせを、列挙するjavascriptコードです。 pythonにはitertoolなどの順列・組み合わせ計算を行う定番ライブラリがあるようです。

やっている事ですが、イメージとしては組み合わせを数列に置き換えています。 1桁目が2進数、3桁目が3進数、3桁目が4進数の不規則な進法で表現されていると捉えて、 n[1..234=24]までループでひたすら列挙します。

[code lang="js"] var data = { "1.ドリンク": ["チャイ", "ラッシー"], "2.副菜": ["サモサ", "ティッカマサラ", "サブジ"], "3.主菜": ["バターチキン", "キーマ", "ダール", "マトン"] }

// 実行毎に結果の出力順序が一意になるようにソート var keys = Object.keys(data).sort();

// 組み合わせの総数を事前に計算 var total = 1; for (var key in data) { total *= data[key].length; }

var q, // 商, ループ中の配列で表現できない数 r, // 余り, ループ中の配列で表現する数 result = []; // 組み合わせを格納する配列

// 組み合わせの総数回ループして、n番目にどの組み合わせが来るかどうかを求める for (var n=0; n < total; n++) { result[n] = {}; q = n;

// dataのキー毎にループして、どの要素を使うか決定 for (i=0; i<keys.length; i++) { key = keys[i];

// ループ対象の配列の要素数で割った余り = その配列で表現できる数
r = q % data[key].length;

// ループ対象の配列の要素数で割った商 = その配列で表現できない数
q = Math.floor(q / data[key].length);

result[n][key] = data[key][r];

} }

// 出力 for (var i=0; i < result.length; i++) { console.log(JSON.stringify(result[i])); }

/ 0:{"1.ドリンク":"チャイ","2.副菜":"サモサ","3.主菜":"バターチキン"} 1:{"1.ドリンク":"ラッシー","2.副菜":"サモサ","3.主菜":"バターチキン"} 2:{"1.ドリンク":"チャイ","2.副菜":"ティッカマサラ","3.主菜":"バターチキン"} 3:{"1.ドリンク":"ラッシー","2.副菜":"ティッカマサラ","3.主菜":"バターチキン"} 4:{"1.ドリンク":"チャイ","2.副菜":"サブジ","3.主菜":"バターチキン"} 5:{"1.ドリンク":"ラッシー","2.副菜":"サブジ","3.主菜":"バターチキン"} 6:{"1.ドリンク":"チャイ","2.副菜":"サモサ","3.主菜":"キーマ"} 7:{"1.ドリンク":"ラッシー","2.副菜":"サモサ","3.主菜":"キーマ"} 8:{"1.ドリンク":"チャイ","2.副菜":"ティッカマサラ","3.主菜":"キーマ"} 9:{"1.ドリンク":"ラッシー","2.副菜":"ティッカマサラ","3.主菜":"キーマ"} 10:{"1.ドリンク":"チャイ","2.副菜":"サブジ","3.主菜":"キーマ"} 11:{"1.ドリンク":"ラッシー","2.副菜":"サブジ","3.主菜":"キーマ"} 12:{"1.ドリンク":"チャイ","2.副菜":"サモサ","3.主菜":"ダール"} 13:{"1.ドリンク":"ラッシー","2.副菜":"サモサ","3.主菜":"ダール"} 14:{"1.ドリンク":"チャイ","2.副菜":"ティッカマサラ","3.主菜":"ダール"} 15:{"1.ドリンク":"ラッシー","2.副菜":"ティッカマサラ","3.主菜":"ダール"} 16:{"1.ドリンク":"チャイ","2.副菜":"サブジ","3.主菜":"ダール"} 17:{"1.ドリンク":"ラッシー","2.副菜":"サブジ","3.主菜":"ダール"} 18:{"1.ドリンク":"チャイ","2.副菜":"サモサ","3.主菜":"マトン"} 19:{"1.ドリンク":"ラッシー","2.副菜":"サモサ","3.主菜":"マトン"} 20:{"1.ドリンク":"チャイ","2.副菜":"ティッカマサラ","3.主菜":"マトン"} 21:{"1.ドリンク":"ラッシー","2.副菜":"ティッカマサラ","3.主菜":"マトン"} 22:{"1.ドリンク":"チャイ","2.副菜":"サブジ","3.主菜":"マトン"} 23:{"1.ドリンク":"ラッシー","2.副菜":"サブジ","3.主菜":"マトン"} / [/code]

例のデータで言えば2 * 3 * 4通りの組み合わせを求めているので、 例えば15番目の組み合わせをを求める場合、「ドリンク」で表現できる要素の数=2で割ると 商が7、余りが1、…となり、 商の7 = 「副菜」以降で表現しなければならない数 余りの1 = 「ドリンク」で表現する数 よって「ドリンク」は配列の添字[1]の要素「ラッシー」を使うと決定できます。

同様にして、商の7を「副菜」で表現できる3で割ると 商が2、余りが1、…となり、 「副菜」は配列の添字[1]の要素「ティッカマサラ」を使います。

最後の主菜では、2 / 4 商は勿論0、余りは[2]となり、 「主菜の」は配列の添字2の要素の「ダール」に決定します。