简体   繁体   中英

Cartesian product without duplicates

I am using a cartesian product function that given [1], [1,2,3], [1,2,3] returns 9 combinations:

[ [ 1, 1, 1 ],
  [ 1, 2, 1 ],
  [ 1, 3, 1 ],
  [ 1, 1, 2 ],
  [ 1, 2, 2 ],
  [ 1, 3, 2 ],
  [ 1, 1, 3 ],
  [ 1, 2, 3 ],
  [ 1, 3, 3 ] ]

But I need to remove those with the same items regardless of the order, so [ 1, 3, 1 ] and [ 1, 1, 3 ] are the same to me. The result should contain 6 items:

[ [ 1, 1, 1 ],
  [ 1, 2, 1 ],
  [ 1, 3, 1 ],
  [ 1, 2, 2 ],
  [ 1, 3, 2 ],
  [ 1, 3, 3 ] ]

I can write a function that compares all possible pairs with _.xor , but for larger numbers it will probably be very inefficient. Is there a good way in Javascript to do this? An efficient way to compare all possible pairs or an algorithm for cartesian product without duplicates?

sort each array of the cartesian product

[ 1, 2, 1 ] -> [1 , 1 , 2]
[ 1, 1, 2 ] -> [1 , 1 , 2]

then gather these sorted arrays into a set, that will remove the duplicates.

Of course, you can do that while constructing the cartesian product rather than afterward.

JavaScript has Set and Map , however they compare objects and arrays by reference rather than by value, so you cannot take advantage of it directly. The idea is to use a key function which sorts and json encodes the items before putting it in a set.

pure ES5:

 function product(sets) { if (sets.length > 0) { var head = sets[0]; var tail = product(sets.slice(1)); var result = []; head.forEach(function(x) { tail.forEach(function(xs) { var item = xs.slice(0); item.unshift(x); result.push(item); }); }); return result; } else { return [[]]; } } function myKeyFn(item) { return JSON.stringify(item.slice(0).sort()); } function uniqBy(items, keyFn) { var hasOwn = Object.prototype.hasOwnProperty, keyset = {}; return items.filter(function(item) { var key = keyFn(item); if (hasOwn.call(keyset, key)) { return false; } else { keyset[key] = 1; return true; } }); } function uniqProduct(sets) { return uniqBy(product(sets), myKeyFn); } function log(x) { console.log(x); var pre = document.createElement('pre'); pre.appendChild(document.createTextNode(x)); document.body.appendChild(pre); } log(uniqProduct([[1],[1,2,3],[1,2,3]]).map(JSON.stringify).join("\\n")); 
 <pre></pre> 

lodash + modern JavaScript :

// Note: This doesn't compile on current babel.io/repl due to a bug

function product(sets) {
  if (sets.length > 0) {
    const [x, ...xs] = sets;
    const products = product(xs);
    return _.flatMap(x, head => products.map(tail => [head, ...tail]));
  } else {
    return [[]];
  }
}

function uniqProduct(sets) {
  return _.uniqBy(product(sets), x => JSON.stringify(x.slice(0).sort()));
}

console.log(uniqProduct([[1],[1,2,3],[1,2,3]]).map(JSON.stringify).join("\n"));

JavaScript has set data structure.

So store your results in a set where each element of the set is a collection of pairs of numbers from the original sets along with the number of times that number occurs.

So your result would look something like this:

[ 
  {1:3},
  {1:2, 2: 1},
  { 1:2, 3:1},
  { 1:1, 2:2},
  { 1:1, 2:1, 3:1},
  { 1:1, 3:2 }  ]

This way, you won't be able to add the object a second time to the set.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM