web-dev-qa-db-ja.com

JavaScriptの複数の配列のデカルト積

JavaScriptで複数の配列のデカルト積をどのように実装しますか?

例として、

cartesian([1, 2], [10, 20], [100, 200, 300]) 

帰るべき

[
  [1, 10, 100],
  [1, 10, 200],
  [1, 10, 300],
  [2, 10, 100],
  [2, 10, 200]
  ...
]
85
viebel

reduceflattenを使用した(mutable variable!なしの)問題の機能的な解決策を次に示します。 underscore.js

function cartesianProductOf() {
    return _.reduce(arguments, function(a, b) {
        return _.flatten(_.map(a, function(x) {
            return _.map(b, function(y) {
                return x.concat([y]);
            });
        }), true);
    }, [ [] ]);
}

// [[1,3,"a"],[1,3,"b"],[1,4,"a"],[1,4,"b"],[2,3,"a"],[2,3,"b"],[2,4,"a"],[2,4,"b"]]
console.log(cartesianProductOf([1, 2], [3, 4], ['a']));  
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore.js"></script>

備考:このソリューションは http://cwestblog.com/2011/05/02/cartesian-product-of-multiple-arrays/ から着想を得たものです

87
viebel

2017アップデート:バニラJSでの2行の回答

ここでの答えはすべて過度に複雑であり、それらのほとんどは20行以上のコードを必要とします。

この例では、Vanilla JavaScriptの2行のみを使用し、ダッシュ、アンダースコア、その他のライブラリは使用しません。

let f = (a, b) => [].concat(...a.map(a => b.map(b => [].concat(a, b))));
let cartesian = (a, b, ...c) => b ? cartesian(f(a, b), ...c) : a;

更新:

これは上記と同じですが、厳密に Airbnb JavaScriptスタイルガイド - eslint-config-airbnb-baseESLint を使用して検証されるように改善されました:

const f = (a, b) => [].concat(...a.map(d => b.map(e => [].concat(d, e))));
const cartesian = (a, b, ...c) => (b ? cartesian(f(a, b), ...c) : a);

ZuBBに感謝します。元のコードのリンターの問題について教えてくれました。

これはあなたの質問からの正確な例です:

let output = cartesian([1,2],[10,20],[100,200,300]);

出力

これはそのコマンドの出力です:

[ [ 1, 10, 100 ],
  [ 1, 10, 200 ],
  [ 1, 10, 300 ],
  [ 1, 20, 100 ],
  [ 1, 20, 200 ],
  [ 1, 20, 300 ],
  [ 2, 10, 100 ],
  [ 2, 10, 200 ],
  [ 2, 10, 300 ],
  [ 2, 20, 100 ],
  [ 2, 20, 200 ],
  [ 2, 20, 300 ] ]

デモ

次のデモをご覧ください:

構文

ここで使用した構文は新しいものではありません。私の例では、スプレッド演算子と残りのパラメーターを使用します。これは、2015年6月に公開され、ES6またはES2015として知られる以前に開発されたECMA-262標準の第6版で定義されたJavaScriptの機能です。見る:

このようなコードは非常に単純なので、使用しないのは罪です。ネイティブにサポートしていない古いプラットフォームの場合は、常にBabelまたは他のツールを使用して古い構文に変換できます。実際、Babelによって変換された私の例は、ここにあるほとんどの例よりも短くて単純ですが、そうではありません本当に重要なのは、トランスピレーションの出力はあなたが理解したり維持したりする必要のあるものではないからです。それは私が興味深いと思った事実です。

結論

2行のVanilla JavaScriptで簡単に作業を完了することができる場合、保守が困難な数百行のコードを記述する必要はなく、このような単純なことのためにライブラリ全体を使用する必要もありません。ご覧のように、言語の最新の機能を使用することは本当に有益であり、最新の機能をネイティブにサポートしていない古風なプラットフォームをサポートする必要がある場合は、常にBabelまたは他のツールを使用して新しい構文を古い構文に変換できます。

1995年のようにコーディングしないでください

JavaScriptは進化しますが、それには理由があります。 TC39は新しい機能を追加することで言語設計の素晴らしい仕事をし、ブラウザーベンダーはそれらの機能を実装する素晴らしい仕事をします。

ブラウザの特定の機能のネイティブサポートの現在の状態を確認するには、次を参照してください。

Nodeバージョンのサポートを確認するには、以下を参照してください。

ネイティブにサポートしていないプラットフォームで最新の構文を使用するには、Babelを使用します。

79
rsp

ライブラリを使用せずに、プレーンJavascriptで@viebelのコードを修正したバージョンを次に示します。

function cartesianProduct(arr) {
    return arr.reduce(function(a,b){
        return a.map(function(x){
            return b.map(function(y){
                return x.concat(y);
            })
        }).reduce(function(a,b){ return a.concat(b) },[])
    }, [[]])
}

var a = cartesianProduct([[1, 2,3], [4, 5,6], [7, 8], [9,10]]);
console.log(JSON.stringify(a));
38
Danny

コミュニティはこれを些細で、または簡単に参照実装を見つけることができると考えているようです、簡単な検査で、私はできなかったか、多分それはあなたの幸運な方法で車輪を再発明するか、教室のようなプログラミング問題を解決するのが好きです:

function cartProd(paramArray) {

  function addTo(curr, args) {

    var i, copy, 
        rest = args.slice(1),
        last = !rest.length,
        result = [];

    for (i = 0; i < args[0].length; i++) {

      copy = curr.slice();
      copy.Push(args[0][i]);

      if (last) {
        result.Push(copy);

      } else {
        result = result.concat(addTo(copy, rest));
      }
    }

    return result;
  }


  return addTo([], Array.prototype.slice.call(arguments));
}


>> console.log(cartProd([1,2], [10,20], [100,200,300]));
>> [
     [1, 10, 100], [1, 10, 200], [1, 10, 300], [1, 20, 100], 
     [1, 20, 200], [1, 20, 300], [2, 10, 100], [2, 10, 200], 
     [2, 10, 300], [2, 20, 100], [2, 20, 200], [2, 20, 300]
   ]

比較的効率的な完全なリファレンス実装...:-D

効率性について:ifをループから外し、2つの独立したループを使用することで、技術的には一定であり、分岐予測などのすべての混乱を助けることができますが、その点はjavascriptでの論争の種です

誰でも、楽しむ-ck

27
ckozl

以下に、空想的ではない、簡単な再帰的ソリューションを示します。

function cartesianProduct(a) { // a = array of array
    var i, j, l, m, a1, o = [];
    if (!a || a.length == 0) return a;

    a1 = a.splice(0, 1)[0]; // the first array of a
    a = cartesianProduct(a);
    for (i = 0, l = a1.length; i < l; i++) {
        if (a && a.length) for (j = 0, m = a.length; j < m; j++)
            o.Push([a1[i]].concat(a[j]));
        else
            o.Push([a1[i]]);
    }
    return o;
}

console.log(cartesianProduct([[1,2], [10,20], [100,200,300]]));
// [[1,10,100],[1,10,200],[1,10,300],[1,20,100],[1,20,200],[1,20,300],[2,10,100],[2,10,200],[2,10,300],[2,20,100],[2,20,200],[2,20,300]]
19
sebnukem

次の効率的な generator function は、指定されたすべての iterables のデカルト積を返します。

// Generate cartesian product of given iterables:
function* cartesian(head, ...tail) {
  const remainder = tail.length > 0 ? cartesian(...tail) : [[]];
  for (let r of remainder) for (let h of head) yield [h, ...r];
}

// Example:
console.log(...cartesian([1, 2], [10, 20], [100, 200, 300]));

配列、文字列、セット、および 反復可能プロトコル を実装する他のすべてのオブジェクトを受け入れます。

n-aryデカルト積 の仕様に従って

  • [] 1つ以上の指定された反復可能オブジェクトが空の場合。 []または''
  • [[a]]単一の値aを含む単一の反復可能オブジェクトが指定された場合。

他のすべてのケースは、次のテストケースで示されるとおり、期待どおりに処理されます。

// Generate cartesian product of given iterables:
function* cartesian(head, ...tail) {
  const remainder = tail.length > 0 ? cartesian(...tail) : [[]];
  for (let r of remainder) for (let h of head) yield [h, ...r];
}

// Test cases:
console.log([...cartesian([])]);              // []
console.log([...cartesian([1])]);             // [[1]]
console.log([...cartesian([1, 2])]);          // [[1], [2]]

console.log([...cartesian([1], [])]);         // []
console.log([...cartesian([1, 2], [])]);      // []

console.log([...cartesian([1], [2])]);        // [[1, 2]]
console.log([...cartesian([1], [2], [3])]);   // [[1, 2, 3]]
console.log([...cartesian([1, 2], [3, 4])]);  // [[1, 3], [2, 3], [1, 4], [2, 4]]

console.log([...cartesian('')]);              // []
console.log([...cartesian('ab', 'c')]);       // [['a','c'], ['b', 'c']]
console.log([...cartesian([1, 2], 'ab')]);    // [[1, 'a'], [2, 'a'], [1, 'b'], [2, 'b']]

console.log([...cartesian(new Set())]);       // []
console.log([...cartesian(new Set([1]))]);    // [[1]]
console.log([...cartesian(new Set([1, 1]))]); // [[1]]
19
le_m

ES6ジェネレーターで一般的なバックトラッキングを使用して、

function cartesianProduct(...arrays) {
  let current = new Array(arrays.length);
  return (function* backtracking(index) {
    if(index == arrays.length) yield current.slice();
    else for(let num of arrays[index]) {
      current[index] = num;
      yield* backtracking(index+1);
    }
  })(0);
}
for (let item of cartesianProduct([1,2],[10,20],[100,200,300])) {
  console.log('[' + item.join(', ') + ']');
}
div.as-console-wrapper { max-height: 100%; }

以下に、古いブラウザと互換性のある同様のバージョンがあります。

function cartesianProduct(arrays) {
  var result = [],
      current = new Array(arrays.length);
  (function backtracking(index) {
    if(index == arrays.length) return result.Push(current.slice());
    for(var i=0; i<arrays[index].length; ++i) {
      current[index] = arrays[index][i];
      backtracking(index+1);
    }
  })(0);
  return result;
}
cartesianProduct([[1,2],[10,20],[100,200,300]]).forEach(function(item) {
  console.log('[' + item.join(', ') + ']');
});
div.as-console-wrapper { max-height: 100%; }
9
Oriol

ECMAScript 2015 generator function を使用する再帰的な方法を次に示します。したがって、一度にすべてのタプルを作成する必要はありません。

function* cartesian() {
    let arrays = arguments;
    function* doCartesian(i, prod) {
        if (i == arrays.length) {
            yield prod;
        } else {
            for (let j = 0; j < arrays[i].length; j++) {
                yield* doCartesian(i + 1, prod.concat([arrays[i][j]]));
            }
        }
    }
    yield* doCartesian(0, []);
}

console.log(JSON.stringify(Array.from(cartesian([1,2],[10,20],[100,200,300]))));
console.log(JSON.stringify(Array.from(cartesian([[1],[2]],[10,20],[100,200,300]))));
9
heenenee

これは 矢印関数 を使用した純粋なES6ソリューションです

function cartesianProduct(arr) {
  return arr.reduce((a, b) =>
    a.map(x => b.map(y => x.concat(y)))
    .reduce((a, b) => a.concat(b), []), [[]]);
}

var arr = [[1, 2], [10, 20], [100, 200, 300]];
console.log(JSON.stringify(cartesianProduct(arr)));
7

Lodashを使用したコーヒースクリプトバージョン:

_ = require("lodash")
cartesianProduct = ->
    return _.reduceRight(arguments, (a,b) ->
        _.flatten(_.map(a,(x) -> _.map b, (y) -> x.concat(y)), true)
    , [ [] ])
6
dsummersl

インデントで読みやすくするための単一行アプローチ。

result = data.reduce(
    (a, b) => a.reduce(
        (r, v) => r.concat(b.map(w => [].concat(v, w))),
        []
    )
);

必要なデカルトアイテムの配列を持つ単一の配列を取ります。

var data = [[1, 2], [10, 20], [100, 200, 300]],
    result = data.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []));

console.log(result.map(a => a.join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
5
Nina Scholz

これはタグ付けされたfunctional-programmingですので、 Listモナド

この単項リストの1つの用途は、非決定的な計算を表すことです。 Listは、アルゴリズムのすべての実行パスの結果を保持できます...

まあ、それはcartesianに適合するperfectのように聞こえます。 JavaScriptはArrayを提供し、モナドバインディング関数はArray.prototype.flatMapので、それらを使用してみましょう-

const cartesian = (...all) =>
{ const loop = (t, a, ...more) =>
    a === undefined
      ? [ t ]
      : a .flatMap (x => loop ([ ...t, x ], ...more))
  return loop ([], ...all)
}

console .log (cartesian ([1,2], [10,20], [100,200,300]))

上記のloopの代わりに、tをカリー化されたパラメーターとして追加できます-

const makeCartesian = (t = []) => (a, ...more) =>
  a === undefined
    ? [ t ]
    : a .flatMap (x => makeCartesian ([ ...t, x ]) (...more))

const cartesian =
  makeCartesian ()

console .log (cartesian ([1,2], [10,20], [100,200,300]))
4
user633183

入力配列のいずれかに配列項目が含まれている場合、このトピックの回答のいくつかは失敗します。あなたはそれをよりよくチェックします。

とにかく、アンダースコアは不要です。これは純粋なJS ES6で機能するはずです。

このコードは、reduceおよびネストされたマップを使用して、2つの配列のデカルト積を取得しますが、2番目の配列は、配列が1つ少ない同じ関数の再帰呼び出しから取得されます。したがって.. a[0].cartesian(...a.slice(1))

Array.prototype.cartesian = function(...a){
  return a.length ? this.reduce((p,c) => (p.Push(...a[0].cartesian(...a.slice(1)).map(e => a.length > 1 ? [c,...e] : [c,e])),p),[])
                  : this;
};

var arr = ['a', 'b', 'c'],
    brr = [1,2,3],
    crr = [[9],[8],[7]];
console.log(JSON.stringify(arr.cartesian(brr,crr))); 
3
Redu

私の特定の設定では、「旧式の」アプローチは、より新しい機能に基づく方法よりも効率的であるように見えました。以下は、他の誰かにとっても有用であると判明した場合のコードです(@rspおよび@sebnukemによってこのスレッドに投稿された他のソリューションとの小さな比較を含む)。

アイデアは次のとおりです。 N配列の外積、それぞれ_a_1,...,a_N_コンポーネントを持つ_m_i_を構築しているとしましょう。これらの配列の外積には_M=m_1*m_2*...*m_N_要素があり、それぞれが正の整数であり、i- thコンポーネントが上から厳密に制限されている_N-_ dimensionalベクトルでそれらを識別することができます_m_i_によって。たとえば、ベクトル_(0, 0, ..., 0)_は、各配列の最初の要素を取得する特定の組み合わせに対応し、_(m_1-1, m_2-1, ..., m_N-1)_は、各配列の最後の要素を取得する組み合わせで識別されます。したがって、すべてのMの組み合わせを構築するために、以下の関数はそのようなすべてのベクトルを連続的に構築し、それぞれに対して入力配列の要素の対応する組み合わせを識別します。

_function cartesianProduct(){
    const N = arguments.length;

    var arr_lengths = Array(N);
    var digits = Array(N);
    var num_tot = 1;
    for(var i = 0; i < N; ++i){
        const len = arguments[i].length;
        if(!len){
            num_tot = 0;
            break;
        }
        digits[i] = 0;
        num_tot *= (arr_lengths[i] = len);
    }

    var ret = Array(num_tot);
    for(var num = 0; num < num_tot; ++num){

        var item = Array(N);
        for(var j = 0; j < N; ++j){ item[j] = arguments[j][digits[j]]; }
        ret[num] = item;

        for(var idx = 0; idx < N; ++idx){
            if(digits[idx] == arr_lengths[idx]-1){
                digits[idx] = 0;
            }else{
                digits[idx] += 1;
                break;
            }
        }
    }
    return ret;
}
//------------------------------------------------------------------------------
let _f = (a, b) => [].concat(...a.map(a => b.map(b => [].concat(a, b))));
let cartesianProduct_rsp = (a, b, ...c) => b ? cartesianProduct_rsp(_f(a, b), ...c) : a;
//------------------------------------------------------------------------------
function cartesianProduct_sebnukem(a) {
    var i, j, l, m, a1, o = [];
    if (!a || a.length == 0) return a;

    a1 = a.splice(0, 1)[0];
    a = cartesianProduct_sebnukem(a);
    for (i = 0, l = a1.length; i < l; i++) {
        if (a && a.length) for (j = 0, m = a.length; j < m; j++)
            o.Push([a1[i]].concat(a[j]));
        else
            o.Push([a1[i]]);
    }
    return o;
}
//------------------------------------------------------------------------------
const L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const args = [L, L, L, L, L, L];

let fns = {
    'cartesianProduct': function(args){ return cartesianProduct(...args); },
    'cartesianProduct_rsp': function(args){ return cartesianProduct_rsp(...args); },
    'cartesianProduct_sebnukem': function(args){ return cartesianProduct_sebnukem(args); }
};

Object.keys(fns).forEach(fname => {
    console.time(fname);
    const ret = fns[fname](args);
    console.timeEnd(fname);
});
_

_node v6.12.2_を使用すると、次のタイミングが得られます。

_cartesianProduct: 427.378ms
cartesianProduct_rsp: 1710.829ms
cartesianProduct_sebnukem: 593.351ms
_
2
ewcz

TypeScriptが必要な人(@Dannyの答えを再実装)

/**
 * Calculates "Cartesian Product" sets.
 * @example
 *   cartesianProduct([[1,2], [4,8], [16,32]])
 *   Returns:
 *   [
 *     [1, 4, 16],
 *     [1, 4, 32],
 *     [1, 8, 16],
 *     [1, 8, 32],
 *     [2, 4, 16],
 *     [2, 4, 32],
 *     [2, 8, 16],
 *     [2, 8, 32]
 *   ]
 * @see https://stackoverflow.com/a/36234242/1955709
 * @see https://en.wikipedia.org/wiki/Cartesian_product
 * @param arr {T[][]}
 * @returns {T[][]}
 */
function cartesianProduct<T> (arr: T[][]): T[][] {
  return arr.reduce((a, b) => {
    return a.map(x => {
      return b.map(y => {
        return x.concat(y)
      })
    }).reduce((c, d) => c.concat(d), [])
  }, [[]] as T[][])
}
1
artnikpro

プレーンJavascriptの@viebelのコードの単純な修正バージョン:

function cartesianProduct(...arrays) {
  return arrays.reduce((a, b) => {
    return [].concat(...a.map(x => {
      const next = Array.isArray(x) ? x : [x];
      return [].concat(b.map(y => next.concat(...[y])));
    }));
  });
}

const product = cartesianProduct([1, 2], [10, 20], [100, 200, 300]);

console.log(product);
/*
[ [ 1, 10, 100 ],
  [ 1, 10, 200 ],
  [ 1, 10, 300 ],
  [ 1, 20, 100 ],
  [ 1, 20, 200 ],
  [ 1, 20, 300 ],
  [ 2, 10, 100 ],
  [ 2, 10, 200 ],
  [ 2, 10, 300 ],
  [ 2, 20, 100 ],
  [ 2, 20, 200 ],
  [ 2, 20, 300 ] ];
*/
1

選択のために、配列のreduceを使用した実際の単純な実装:

const array1 = ["day", "month", "year", "time"];
const array2 = ["from", "to"];
const process = (one, two) => [one, two].join(" ");

const product = array1.reduce((result, one) => result.concat(array2.map(two => process(one, two))), []);
1
Simple.Js

ほんの数行で最新のJavaScript。 Lodashのような外部ライブラリや依存関係はありません。

function cartesian(...arrays) {
  return arrays.reduce((a, b) => a.flatMap(x => b.map(y => x.concat([y]))), [ [] ]);
}

console.log(
  cartesian([1, 2], [10, 20], [100, 200, 300])
    .map(arr => JSON.stringify(arr))
    .join('\n')
);
1
Miles Elam

製品を実際に結果セットに追加する前に、製品をフィルタリングおよび変更する機能を追加する非再帰的アプローチ。 .forEachではなく.mapの使用に注意してください。一部のブラウザーでは、.mapの実行速度が速くなります。

function crossproduct(arrays,rowtest,rowaction) {
      // Calculate the number of elements needed in the result
      var result_elems = 1, row_size = arrays.length;
      arrays.map(function(array) {
            result_elems *= array.length;
      });
      var temp = new Array(result_elems), result = [];

      // Go through each array and add the appropriate element to each element of the temp
      var scale_factor = result_elems;
      arrays.map(function(array)
      {
        var set_elems = array.length;
        scale_factor /= set_elems;
        for(var i=result_elems-1;i>=0;i--) {
            temp[i] = (temp[i] ? temp[i] : []);
            var pos = i / scale_factor % set_elems;
            // deal with floating point results for indexes, this took a little experimenting
            if(pos < 1 || pos % 1 <= .5) {
                pos = Math.floor(pos);
            } else {
                pos = Math.min(array.length-1,Math.ceil(pos));
            }
            temp[i].Push(array[pos]);
            if(temp[i].length===row_size) {
                var pass = (rowtest ? rowtest(temp[i]) : true);
                if(pass) {
                    if(rowaction) {
                        result.Push(rowaction(temp[i]));
                    } else {
                        result.Push(temp[i]);
                    }
                }
            }
        }
      });
      return result;
    }
1
AnyWhichWay

reduce 2D配列にすることができます。アキュムレータ配列で flatMap を使用して、各ループで_acc.length x curr.length_の組み合わせの数を取得します。 cは最初の反復の数値であり、その後の配列であるため、[].concat(c, n)が使用されます。

_const data = [ [1, 2], [10, 20], [100, 200, 300] ];

const output = data.reduce((acc, curr, i) =>
  acc.flatMap(c => curr.map(n => [].concat(c, n)))
)

console.log(JSON.stringify(output))_

(これは Nina Scholzの答え に基づいています)

0
adiga

各組み合わせを処理するために関数を渡すことができるソリューションを誰も投稿していないことに気づいたので、ここに私のソリューションがあります:

const _ = require('lodash')

function combinations(arr, f, xArr = []) {
    return arr.length>1 
    ? _.flatMap(arr[0], x => combinations(arr.slice(1), f, xArr.concat(x)))
    : arr[0].map(x => f(...xArr.concat(x)))
}

// use case
const greetings = ["Hello", "Goodbye"]
const places = ["World", "Planet"]
const punctuationMarks = ["!", "?"]
combinations([greetings,places,punctuationMarks], (greeting, place, punctuationMark) => `${greeting} ${place}${punctuationMark}`)
  .forEach(row => console.log(row))

出力:

Hello World!
Hello World?
Hello Planet!
Hello Planet?
Goodbye World!
Goodbye World?
Goodbye Planet!
Goodbye Planet?
0
Lezorte

入力として配列の配列を使用するプレーンJSブルートフォースアプローチ。

var cartesian = function(arrays) {
    var product = [];
    var precals = [];
    var length = arrays.reduce(function(acc, curr) {
        return acc * curr.length
    }, 1);
    for (var i = 0; i < arrays.length; i++) {
        var array = arrays[i];
        var mod = array.length;
        var div = i > 0 ? precals[i - 1].div * precals[i - 1].mod : 1;
        precals.Push({
            div: div,
            mod: mod
        });
    }
    for (var j = 0; j < length; j++) {
        var item = [];
        for (var i = 0; i < arrays.length; i++) {
            var array = arrays[i];
            var precal = precals[i];
            var k = (~~(j / precal.div)) % precal.mod;
            item.Push(array[k]);
        }
        product.Push(item);
    }
    return product;
};

cartesian([
    [1],
    [2, 3]
]);

cartesian([
    [1],
    [2, 3],
    [4, 5, 6]
]);
0
Samuel Ventura

さらに別の実装。最短でも空想でもないが、高速:

function cartesianProduct() {
    var arr = [].slice.call(arguments),
        intLength = arr.length,
        arrHelper = [1],
        arrToReturn = [];

    for (var i = arr.length - 1; i >= 0; i--) {
        arrHelper.unshift(arrHelper[0] * arr[i].length);
    }

    for (var i = 0, l = arrHelper[0]; i < l; i++) {
        arrToReturn.Push([]);
        for (var j = 0; j < intLength; j++) {
            arrToReturn[i].Push(arr[j][(i / arrHelper[j + 1] | 0) % arr[j].length]);
        }
    }

    return arrToReturn;
}
0
flare256

ライブラリは必要ありません! :)

ただし、矢印関数が必要で、おそらくそれほど効率的ではありません。 :/

const flatten = (xs) => 
    xs.flat(Infinity)

const binaryCartesianProduct = (xs, ys) =>
    xs.map((xi) => ys.map((yi) => [xi, yi])).flat()

const cartesianProduct = (...xss) =>
    xss.reduce(binaryCartesianProduct, [[]]).map(flatten)
      
console.log(cartesianProduct([1,2,3], [1,2,3], [1,2,3]))
0
Chris Vouga

シンプルな「心と視覚に優しい」ソリューション。

enter image description here


// t = [i, length]

const moveThreadForwardAt = (t, tCursor) => {
  if (tCursor < 0)
    return true; // reached end of first array

  const newIndex = (t[tCursor][0] + 1) % t[tCursor][1];
  t[tCursor][0] = newIndex;

  if (newIndex == 0)
    return moveThreadForwardAt(t, tCursor - 1);

  return false;
}

const cartesianMult = (...args) => {
  let result = [];
  const t = Array.from(Array(args.length)).map((x, i) => [0, args[i].length]);
  let reachedEndOfFirstArray = false;

  while (false == reachedEndOfFirstArray) {
    result.Push(t.map((v, i) => args[i][v[0]]));

    reachedEndOfFirstArray = moveThreadForwardAt(t, args.length - 1);
  }

  return result;
}

// cartesianMult(
//   ['a1', 'b1', 'c1'],
//   ['a2', 'b2'],
//   ['a3', 'b3', 'c3'],
//   ['a4', 'b4']
// );

console.log(cartesianMult(
  ['a1'],
  ['a2', 'b2'],
  ['a3', 'b3']
));
0
zero.zero.seven

記録のために

ここに私のバージョンがあります。最も単純なjavascriptイテレータ "for()"を使用して作成したため、すべてのケースで互換性があり、最高のパフォーマンスが得られます。

function cartesian(arrays){
    var quant = 1, counters = [], retArr = [];

    // Counts total possibilities and build the counters Array;
    for(var i=0;i<arrays.length;i++){
        counters[i] = 0;
        quant *= arrays[i].length;
    }

    // iterate all possibilities
    for(var i=0,nRow;i<quant;i++){
        nRow = [];
        for(var j=0;j<counters.length;j++){
            if(counters[j] < arrays[j].length){
                nRow.Push(arrays[j][counters[j]]);
            } else { // in case there is no such an element it restarts the current counter
                counters[j] = 0;
                nRow.Push(arrays[j][counters[j]]);
            }
            counters[j]++;
        }
        retArr.Push(nRow);
    }
    return retArr;
}

宜しくお願いします。

0
LeandroP