Related
Problem 1
I have this variables
var myArr = ["A", "B", "C", "D", "E", "F", "G", "H", "I"];
var nOptions = 10;
and I'm trying to get all unique possible combination with 10 options.
Results example:
var result1 = ["A", "A", "A", "A", "A", "A", "A", "A", "A", "A"];
var result2 = ["A", "A", "A", "A", "A", "A", "A", "A", "A", "B"];
var result3 = ["A", "A", "A", "A", "A", "A", "A", "A", "B", "A"];
var result4 = ["A", "A", "A", "A", "A", "A", "A", "B", "A", "A"];
----------------------------------------------------------------
var result5 = ["A", "A", "A", "A", "A", "A", "A", "A", "B", "B"];
var result6 = ["A", "A", "A", "A", "A", "A", "A", "B", "A", "B"];
var result7 = ["A", "A", "A", "A", "A", "A", "B", "A", "A", "B"];
----------------------------------------------------------------
var result8 = ["A", "A", "A", "A", "A", "A", "A", "A", "C", "B"];
var result9 = ["A", "A", "A", "A", "A", "A", "A", "C", "A", "B"];
var result10 = ["A", "A", "A", "A", "A", "A", "C", "A", "A", "B"];
----------------------------------------------------------------
var result11 = ["A", "A", "A", "A", "A", "A", "A", "C", "B", "B"];
var result12 = ["A", "A", "A", "A", "A", "A", "C", "A", "B", "B"];
var result13 = ["A", "A", "A", "A", "A", "C", "A", "A", "B", "B"];
----------------------------------------------------------------
var resultn = ["I", "I", "I", "I", "I", "I", "I", "I", "I", "I"];
Is there any way to print all that results in JavaScript?
Is there a math formula so I can know the number of all combinations without making all the loops?
Problem 2
Finally I want to make a filter so I can have all possible combinations with the new filters.
Example:
var minA = 0;
var maxA = 3;
var minB = 2;
var maxB = 4;
var minC = 0;
var maxC = 3;
-------------
var minI = 3;
var maxI = 5;
Example valid results with filter:
var result1 = ["B", "B", "B", "B", "I", "I", "I", "I", "I", "A"];
var result2 = ["B", "B", "B", "B", "I", "I", "I", "I", "I", "C"];
var result3 = ["B", "B", "B", "B", "I", "I", "I", "I", "A", "A"];
Is there any way to print all filter results in JavaScript directly or I have to loop throw all results and make counters for each letter and then if all counters are valid with the filter options print the new results?
Is there a math formula so I can know the number of all combinations with filters without making all the loops?
Thanks to everybody who can help in making the code. I will update the code so everybody can use it for future projects.
CODE Problem 1
var myArr = ["A", "B", "C", "D", "E", "F", "G", "H", "I"];
var nOptions = 10;
myCounter = 0;
allResults = [];
for (let a = 0; a < 9; a++) {
for (let b = 0; b < 9; b++) {
for (let c = 0; c < 9; c++) {
for (let d = 0; d < 9; d++) {
for (let e = 0; e < 9; e++) {
for (let f = 0; f < 9; f++) {
for (let g = 0; g < 9; g++) {
for (let h = 0; h < 9; h++) {
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
allResults.push([myArr[a], myArr[b], myArr[c], myArr[d], myArr[e], myArr[f], myArr[g], myArr[h], myArr[i], myArr[j]]);
myCounter++;
}
//break;
}
//break;
}
//break;
}
//break;
}
//break;
}
//break;
}
//break;
}
//break;
}
//break;
}
Better CODE Problem 1
I made a better code so now nOptions can be a reasonable number (min 0, max 26) but a number too big will have too much possible combinations and the pc couldn't process all that data. A maxium reasonable number would be nOptions = 6. That means 9^6 = 531.441 results.
var myArr = ["A", "B", "C", "D", "E", "F", "G", "H", "I"];
var nOptions = 2;
var myCounter = 0;
var allResults = [];
var myLetters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"];
codeLine='';
for (let i = 0; i < nOptions; i++) {
codeLine += 'for (let ' + myLetters[i] + ' = 0; ' + myLetters[i] + ' < 9; ' + myLetters[i] + '++) {';
}
codeLine += 'allResults.push([';
for (let i = 0; i < nOptions; i++) {
codeLine += 'myArr[' + myLetters[i] + ']';
if (i !== nOptions - 1) {
codeLine += ', ';
}
}
codeLine += ']);';
codeLine += 'myCounter++;';
//codeLine += 'console.log(myCounter);';
for (let i = 0; i < nOptions; i++) {
codeLine += '}';
}
//console.log(codeLine);
eval(codeLine);
console.table(allResults);
If you think of this recursively, it becomes fairly simple to write the code that generates these combinations. Simply take each letter in turn as the first letter, then recursively calculate all the words one character shorter for the remainder and combine them. We bottom out with a result holding just one empty array when the count is 0. Here's a simple version of the code:
const makeWords = (cs, n) =>
n == 0
? [[]]
: cs .flatMap (c => makeWords (cs, n - 1) .map (w => [c, ...w]))
const letters = ['A', 'B', 'C', 'D']
console .log (makeWords (letters, 3))
.as-console-wrapper {max-height: 100% !important; top: 0}
As others have pointed out the formula for the count of such words is simply (number of letters) ^ (word length). So for my example of three-letter words on a four-character alphabet, there are 4 ^ 3, or 64 results. For ten-letter words on a nine-character alphabet, there are 9 ^ 10 or 3486784401 results. I hope you're not planning on printing them all, or even logging them all to the console!
You can convert between numbers and letters by their digit. Here you have 9 letters you care about, so using base 9 we have: 0=a, 1=b, ... 8=i.
Then use toString to convert all the integers from 0 to 9^11-1 to base 9, with leading 0s. Finally, convert the digits back to the appropriate letters.
0 -> 0000000000 -> aaaaaaaaaa
1 -> 0000000001 -> aaaaaaaaab
...
64340123 -> 0064340123 -> aagedeabcd
...
8888888888 -> 8888888888 -> iiiiiiiiii
Solution to problem 1#
var myArr = ["A", "B", "C", "D", "E", "F", "G", "H", "I"];
var nOptions = 2;
var myCounter = 0;
var allResults = [];
var myLetters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"];
codeLine='';
for (let i = 0; i < nOptions; i++) {
codeLine += 'for (let ' + myLetters[i] + ' = 0; ' + myLetters[i] + ' < 9; ' + myLetters[i] + '++) {';
}
codeLine += 'allResults.push([';
for (let i = 0; i < nOptions; i++) {
codeLine += 'myArr[' + myLetters[i] + ']';
if (i !== nOptions - 1) {
codeLine += ', ';
}
}
codeLine += ']);';
codeLine += 'myCounter++;';
//codeLine += 'console.log(myCounter);';
for (let i = 0; i < nOptions; i++) {
codeLine += '}';
}
//console.log(codeLine);
eval(codeLine);
console.table(allResults);
How do I display every element in an Array with each other element once, but not itself?
For example:
var myArray:any = "a b c d"
And then display:
a,b
a,c
a,d
b,a
b,c
b,d
etc.
A for in for works fine.
var myArray = "a b c d".split(' ');
for (let first of myArray) {
for (let second of myArray) {
if (first !== second) {
console.log(`${first},${second}`)
}
}
}
Try
function f (arr) {
arr.forEach(function(e) {
arr.forEach(function(e2) {
if (e === e2) return;
var str = e + "," + e2;
// print str
});
});
}
f("a b c d".split(' '));
You could also use dankogai/js-combinatorics like this:
cmb = Combinatorics.combination(['a','b','c','d'], 2);
while(a = cmb.next()) console.log(a);
// ["a", "b"]
// ["a", "c"]
// ["a", "d"]
// ["b", "c"]
// ["b", "d"]
// ["c", "d"]
"a b c d".split(' ').map((el, idx, arr)=>{
let elIdx = arr.indexOf(el);
let rest = arr.slice(0,elIdx).concat(arr.slice(elIdx+1)).map((l)=> console.log(`${el},${l}`) );
});
for this you can do imbrication of map fonctions, but you do have to work with arrays
here an exemple:
const myArray = ["a", "b", "c", "d", "e", "f", "g"]
// forEach accept one first param x current value
// second param is optional xi index of value
// third param is optional too and it refer the array itself
myArray.forEach( (x, xi, myArr) => {
myArr.forEach( (y, yi) => {
if(xi !== yi) { console.log(`${x}, ${y}`); }
});
});
I have two arrays
var arrayA = ["a", "a", "b", "b", "a", "c"];
var arrayB = [10, 20, 3, 2, 20, 5];
As you can see, arrayA[0], arrayA[1], arrayA[4] have an same elements (arrayA[2], arrayA[3] same too).
So based on above example, I want arrayB[0], arrayB[1], arrayB[4] will be summed up, and arrayB[2], arrayB[3] too.
expectation output
arrayA = ["a", "b", "c"];
arrayB = [50, 5, 5];
It's possible to sum the arrayB elements if arrayA have same elements based arrayA index? and is there an Lodash/Underscore function to do this?
You could use an object for the indices and maintain the values.
var arrayA = ["a", "a", "b", "b", "a", "c"],
arrayB = [10, 20, 3, 2, 20, 5],
indices = Object.create(null),
groupedA = [],
groupedB = [];
arrayA.forEach(function (a, i) {
if (!(a in indices)) {
groupedA.push(a);
indices[a] = groupedB.push(0) - 1;
}
groupedB[indices[a]] += arrayB[i];
});
console.log(groupedA);
console.log(groupedB);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Version which mutates the original arrays.
var arrayA = ["a", "a", "b", "b", "a", "c"],
arrayB = [10, 20, 3, 2, 20, 5],
indices = Object.create(null),
i = 0;
while (i < arrayA.length) {
if (!(arrayA[i] in indices)) {
indices[arrayA[i]] = i++;
continue;
}
arrayB[indices[arrayA[i]]] += arrayB.splice(i, 1)[0];
arrayA.splice(i, 1);
}
console.log(arrayA);
console.log(arrayB);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here's a solution using lodash:
[arrayA, arrayB] = _(arrayA)
.zip(arrayB)
.groupBy(0)
.mapValues( grp => _.sumBy(grp,1))
.thru(obj => [_.keys(obj), _.values(obj)])
.value();
zip will associate each element in arrayA with the corresponding element in arrayB e.g. [ ['a', 10], ['a', 20], ...]
We then groupBy the value in position 0 giving an object something like:
{
a: ['a', 10], ['a', 20], ['a', 20'],
b: ['b', 3] ...,
c: ...
}
The values of each key are then mapped to the sum of the values in position 1 before finally returning the keys and the values in separate arrays.
You can reduce both arrays into an ES6 Map, and then spread the keys for arrayA, and the values for arrayB:
const arrayA = ["a", "a", "b", "b", "a", "c"];
const arrayB = [10, 20, 3, 2, 20, 5];
const map = arrayA.reduce((m, c, i) => m.set(c, (m.get(c) || 0) + arrayB[i]), new Map());
const arrA = [...map.keys()];
const arrB = [...map.values()];
console.log(arrA);
console.log(arrB);
With an intermediary "result" object:
var arrayA = ["a", "a", "b", "b", "a", "c"];
var arrayB = [10, 20, 3, 2, 20, 5];
var result = {};
for (var i = 0, max = arrayA.length; i < max; i++) {
if (!result[arrayA[i]]) {
result[arrayA[i]] = 0;
}
result[arrayA[i]] += arrayB[i];
}
var keys = Object.keys(result);
arrayA = [];
arrayB = [];
for (var i = 0, max = keys.length; i < max; i++) {
arrayA.push(keys[i]);
arrayB.push(result[keys[i]]);
}
You can compute sums of all elements in arrayB that corresponds to each element in arrayA, store these sums in an object and use Object.values to get an array of the sums.
var arrayA = ["a", "a", "b", "b", "a", "c"];
var arrayB = [10, 20, 3, 2, 20, 5];
var sum = {};
arrayA.forEach((l, index) => {
sum[l] = (sum[l] || 0) + arrayB[index];
});
var res = Object.values(sum);
console.log(res);
And it can be done even shorter with array.prototype.reduce:
var arrayA = ["a", "a", "b", "b", "a", "c"];
var arrayB = [10, 20, 3, 2, 20, 5];
var res = Object.values(arrayA.reduce((m, l, index) => {
m[l] = (m[l] || 0) + arrayB[index];
return m;
}, {}));
console.log(res);
let _ = require('underscore');
var arrayA = ["a", "a", "b", "b", "a", "c"];
var arrayB = [10, 20, 3, 2, 20, 5];
let res = {};
_.each(arrayA, (item, key) => {
if (! res[item]) {
res[item] = arrayB[key];
} else {
res[item] = res[item] + arrayB[key];
}
});
arrayA = [];
arrayB = [];
_.each(res,(value,key) => {
arrayA.push(key);
arrayB.push(value);
});
console.log(arrayA);
console.log(arrayB);
First fill the dict and then fill de arrays with the keys and values
let arrayA = ["a", "a", "b", "b", "a", "c"];
let arrayB = [10, 20, 3, 2, 20, 5];
let result = {};
for (let i=0; i < arrayA.length; i++ ){
let valueB = 0;
if (arrayB.length > i) {
valueB = arrayB[i];
}
if (result.hasOwnProperty(arrayA[i])) {
result[arrayA[i]] += valueB;
}
else {
result[arrayA[i]] = valueB;
}
}
let resultA = [];
let resultB = [];
for (let k in result) {
resultA.push(k);
resultB.push(result[k]);
}
console.log(resultA);
console.log(resultB);
Use Array#reduce method.
var arrayA = ["a", "a", "b", "b", "a", "c"];
var arrayB = [10, 20, 3, 2, 20, 5];
// reference to keep the index
var ref = {},
// array for keeping first result
res1 = [];
var res2 = arrayA
// iterate over the first array
.reduce(function(arr, v, i) {
// check index presenet in the refernece object
if (!(v in ref)) {
// if not then define the index and insert 0 in the array(defining the new index)
arr[ref[v] = arr.length] = 0;
// push value into the array( for unique value )
res1.push(v);
}
// update the element at the index
arr[ref[v]] += arrayB[i];
// return the array reference
return arr;
// initialize initial value as an empty array to keep result
}, [])
console.log(res1, res2);
If I have a function:
function sliceArrayIntoGroups(arr, size) {
var slicedArray = arr.slice(0, size);
return slicedArray;
}
I am looking to take an array and slice it into an array of arrays.. how would I go about doing so?
So if I had this:
sliceArrayIntoGroups(["a", "b", "c", "d"], 2);
The result should be:
[["a","b"],["c","d"]]
But I don't know how to save the second part of the original array after slicing it.
Any help is appreciated.
The solution using regular while loop and custom step parameter:
function sliceArrayIntoGroups(arr, size) {
var step = 0, sliceArr = [], len = arr.length;
while (step < len) {
sliceArr.push(arr.slice(step, step += size));
}
return sliceArr;
}
console.log(sliceArrayIntoGroups(["a", "b", "c", "d"], 2));
console.log(sliceArrayIntoGroups(["a", "b", "c", "d", "e", "f"], 2));
console.log(sliceArrayIntoGroups(["a", "b", "c", "d", "e", "f"], 3));
step option points to an offset of each extraction(slicing)
This ought to do it. It's a simple recursive function that slices n elements from the beginning of the array and calls itself with the remaining elements.
function sliceArrayIntoGroups(arr, size) {
if (arr.length === 0) { return arr; }
return [ arr.slice(0, size), ...sliceArrayIntoGroups(arr.slice(size), size) ];
}
console.log(sliceArrayIntoGroups([1,2,3,4,5], 2));
console.log(sliceArrayIntoGroups([1,2,3,4,5], 3));
console.log(sliceArrayIntoGroups([1,2,3,4,5], 10));
try this, it will slice origin array to 2 pieces, then concat to 1 array
function sliceArrayIntoGroups(arr, size) {
if (size >= arr.length || size <= 0) { return arr; }
return [arr.slice(0, size), arr.slice(size)];
}
console.log(sliceArrayIntoGroups(["a", "b", "c", "d"], 2));
Try this:
function sliceArrayIntoGroups(arr, size) {
var result = [];
while (arr.length > 0) {
result.push(arr.splice(0, size));
}
return result;
}
console.log(sliceArrayIntoGroups(["a", "b", "c", "d", "e", "f"], 3));
console.log(sliceArrayIntoGroups(["a", "b", "c", "d"], 2));
function sliceArrayIntoGroups(arr, size) {
var result = [];
while (arr.length > 0) {
result.push(arr.splice(0, size));
}
return result;
}
This will divide array into pieces where each piece will be the size of size variable, so
sliceArrayIntoGroups(["a", "b", "c", "d", "e", "f"], 3);
will output
[["a", "b", "c"], ["d", "e", "f"]]
Javascript slice() method returns the selected elements in an array, as a new array object. So using for loop create smallArray and push them to arrGroup array.
function sliceArrayIntoGroups(arr, size) {
let arrGroup =[];
for (let i=0; i<arr.length; i+=size) {
let smallArray = arr.slice(i,i+size);//creating smaller array of required size using slice
arrGroup.push(smallArray);
}
return arrGroup;
}
Reduce:
var x = [1,2,3,4,5,6,7,8,9];
var chunk = function(arr,n) {
var temp;
return arr.reduce(function(carry,item,index) {
//if we're at a chunk point: index%n == 0
if(!(index%n)) {
//if temp currently holds items, push it onto carry
if(temp && temp.length) { carry.push(temp); }
//reset temp to an empty array
temp = [];
}
//push the current item onto temp
temp.push(item);
//if this is the last item in the array, push temp onto carry
index == arr.length-1 && carry.push(temp);
return carry;
},[]);
};
chunk(x,5);
I have two arrays:
var array1 = ["A", "B", "C"];
var array2 = ["1", "2", "3"];
How can I set another array to contain every combination of the above, so that:
var combos = ["A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3"];
Or if you'd like to create combinations with an arbitrary number of arrays of arbitrary sizes...(I'm sure you can do this recursively, but since this isn't a job interview, I'm instead using an iterative "odometer" for this...it increments a "number" with each digit a "base-n" digit based on the length of each array)...for example...
combineArrays([ ["A","B","C"],
["+", "-", "*", "/"],
["1","2"] ] )
...returns...
[
"A+1","A+2","A-1", "A-2",
"A*1", "A*2", "A/1", "A/2",
"B+1","B+2","B-1", "B-2",
"B*1", "B*2", "B/1", "B/2",
"C+1","C+2","C-1", "C-2",
"C*1", "C*2", "C/1", "C/2"
]
...each of these corresponding to an "odometer" value that
picks an index from each array...
[0,0,0], [0,0,1], [0,1,0], [0,1,1]
[0,2,0], [0,2,1], [0,3,0], [0,3,1]
[1,0,0], [1,0,1], [1,1,0], [1,1,1]
[1,2,0], [1,2,1], [1,3,0], [1,3,1]
[2,0,0], [2,0,1], [2,1,0], [2,1,1]
[2,2,0], [2,2,1], [2,3,0], [2,3,1]
The "odometer" method allows you to easily generate
the type of output you want, not just the concatenated strings
like we have here. Besides that, by avoiding recursion
we avoid the possibility of -- dare I say it? -- a stack overflow...
function combineArrays( array_of_arrays ){
// First, handle some degenerate cases...
if( ! array_of_arrays ){
// Or maybe we should toss an exception...?
return [];
}
if( ! Array.isArray( array_of_arrays ) ){
// Or maybe we should toss an exception...?
return [];
}
if( array_of_arrays.length == 0 ){
return [];
}
for( let i = 0 ; i < array_of_arrays.length; i++ ){
if( ! Array.isArray(array_of_arrays[i]) || array_of_arrays[i].length == 0 ){
// If any of the arrays in array_of_arrays are not arrays or zero-length, return an empty array...
return [];
}
}
// Done with degenerate cases...
// Start "odometer" with a 0 for each array in array_of_arrays.
let odometer = new Array( array_of_arrays.length );
odometer.fill( 0 );
let output = [];
let newCombination = formCombination( odometer, array_of_arrays );
output.push( newCombination );
while ( odometer_increment( odometer, array_of_arrays ) ){
newCombination = formCombination( odometer, array_of_arrays );
output.push( newCombination );
}
return output;
}/* combineArrays() */
// Translate "odometer" to combinations from array_of_arrays
function formCombination( odometer, array_of_arrays ){
// In Imperative Programmingese (i.e., English):
// let s_output = "";
// for( let i=0; i < odometer.length; i++ ){
// s_output += "" + array_of_arrays[i][odometer[i]];
// }
// return s_output;
// In Functional Programmingese (Henny Youngman one-liner):
return odometer.reduce(
function(accumulator, odometer_value, odometer_index){
return "" + accumulator + array_of_arrays[odometer_index][odometer_value];
},
""
);
}/* formCombination() */
function odometer_increment( odometer, array_of_arrays ){
// Basically, work you way from the rightmost digit of the "odometer"...
// if you're able to increment without cycling that digit back to zero,
// you're all done, otherwise, cycle that digit to zero and go one digit to the
// left, and begin again until you're able to increment a digit
// without cycling it...simple, huh...?
for( let i_odometer_digit = odometer.length-1; i_odometer_digit >=0; i_odometer_digit-- ){
let maxee = array_of_arrays[i_odometer_digit].length - 1;
if( odometer[i_odometer_digit] + 1 <= maxee ){
// increment, and you're done...
odometer[i_odometer_digit]++;
return true;
}
else{
if( i_odometer_digit - 1 < 0 ){
// No more digits left to increment, end of the line...
return false;
}
else{
// Can't increment this digit, cycle it to zero and continue
// the loop to go over to the next digit...
odometer[i_odometer_digit]=0;
continue;
}
}
}/* for( let odometer_digit = odometer.length-1; odometer_digit >=0; odometer_digit-- ) */
}/* odometer_increment() */
Just in case anyone is looking for Array.map solution
var array1=["A","B","C"];
var array2=["1","2","3","4"];
console.log(array1.flatMap(d => array2.map(v => d + v)))
Seeing a lot of for loops in all of the answers...
Here's a recursive solution I came up with that will find all combinations of N number of arrays by taking 1 element from each array:
const array1=["A","B","C"]
const array2=["1","2","3"]
const array3=["red","blue","green"]
const combine = ([head, ...[headTail, ...tailTail]]) => {
if (!headTail) return head
const combined = headTail.reduce((acc, x) => {
return acc.concat(head.map(h => `${h}${x}`))
}, [])
return combine([combined, ...tailTail])
}
console.log('With your example arrays:', combine([array1, array2]))
console.log('With N arrays:', combine([array1, array2, array3]))
//-----------UPDATE BELOW FOR COMMENT---------
// With objects
const array4=[{letter: "A"}, {letter: "B"}, {letter: "C"}]
const array5=[{number: 1}, {number: 2}, {number: 3}]
const array6=[{color: "RED"}, {color: "BLUE"}, {color: "GREEN"}]
const combineObjects = ([head, ...[headTail, ...tailTail]]) => {
if (!headTail) return head
const combined = headTail.reduce((acc, x) => {
return acc.concat(head.map(h => ({...h, ...x})))
}, [])
return combineObjects([combined, ...tailTail])
}
console.log('With arrays of objects:', combineObjects([array4, array5, array6]))
A loop of this form
combos = [] //or combos = new Array(2);
for(var i = 0; i < array1.length; i++)
{
for(var j = 0; j < array2.length; j++)
{
//you would access the element of the array as array1[i] and array2[j]
//create and array with as many elements as the number of arrays you are to combine
//add them in
//you could have as many dimensions as you need
combos.push(array1[i] + array2[j])
}
}
Assuming you're using a recent web browser with support for Array.forEach:
var combos = [];
array1.forEach(function(a1){
array2.forEach(function(a2){
combos.push(a1 + a2);
});
});
If you don't have forEach, it is an easy enough exercise to rewrite this without it. As others have proven before, there's also some performance advantages to doing without... (Though I contend that not long from now, the common JavaScript runtimes will optimize away any current advantages to doing this otherwise.)
Solution enhancement for #Nitish Narang's answer.
Use reduce in combo with flatMap to support N arrays combination.
const combo = [
["A", "B", "C"],
["1", "2", "3", "4"]
];
console.log(combo.reduce((a, b) => a.flatMap(x => b.map(y => x + y)), ['']))
Here is functional programming ES6 solution:
var array1=["A","B","C"];
var array2=["1","2","3"];
var result = array1.reduce( (a, v) =>
[...a, ...array2.map(x=>v+x)],
[]);
/*---------OR--------------*/
var result1 = array1.reduce( (a, v, i) =>
a.concat(array2.map( w => v + w )),
[]);
/*-------------OR(without arrow function)---------------*/
var result2 = array1.reduce(function(a, v, i) {
a = a.concat(array2.map(function(w){
return v + w
}));
return a;
},[]
);
console.log(result);
console.log(result1);
console.log(result2)
Part II: After my complicated iterative "odometer" solution of July 2018, here's a simpler recursive version of combineArraysRecursively()...
function combineArraysRecursively( array_of_arrays ){
// First, handle some degenerate cases...
if( ! array_of_arrays ){
// Or maybe we should toss an exception...?
return [];
}
if( ! Array.isArray( array_of_arrays ) ){
// Or maybe we should toss an exception...?
return [];
}
if( array_of_arrays.length == 0 ){
return [];
}
for( let i = 0 ; i < array_of_arrays.length; i++ ){
if( ! Array.isArray(array_of_arrays[i]) || array_of_arrays[i].length == 0 ){
// If any of the arrays in array_of_arrays are not arrays or are zero-length array, return an empty array...
return [];
}
}
// Done with degenerate cases...
let outputs = [];
function permute(arrayOfArrays, whichArray=0, output=""){
arrayOfArrays[whichArray].forEach((array_element)=>{
if( whichArray == array_of_arrays.length - 1 ){
// Base case...
outputs.push( output + array_element );
}
else{
// Recursive case...
permute(arrayOfArrays, whichArray+1, output + array_element );
}
});/* forEach() */
}
permute(array_of_arrays);
return outputs;
}/* function combineArraysRecursively() */
const array1 = ["A","B","C"];
const array2 = ["+", "-", "*", "/"];
const array3 = ["1","2"];
console.log("combineArraysRecursively(array1, array2, array3) = ", combineArraysRecursively([array1, array2, array3]) );
Here is another take. Just one function and no recursion.
function allCombinations(arrays) {
const numberOfCombinations = arrays.reduce(
(res, array) => res * array.length,
1
)
const result = Array(numberOfCombinations)
.fill(0)
.map(() => [])
let repeatEachElement
for (let i = 0; i < arrays.length; i++) {
const array = arrays[i]
repeatEachElement = repeatEachElement ?
repeatEachElement / array.length :
numberOfCombinations / array.length
const everyElementRepeatedLength = repeatEachElement * array.length
for (let j = 0; j < numberOfCombinations; j++) {
const index = Math.floor(
(j % everyElementRepeatedLength) / repeatEachElement
)
result[j][i] = array[index]
}
}
return result
}
const result = allCombinations([
['a', 'b', 'c', 'd'],
[1, 2, 3],
[true, false],
])
console.log(result.join('\n'))
Arbitrary number of arrays, arbitrary number of elements.
Sort of using number base theory I guess - the j-th array changes to the next element every time the number of combinations of the j-1 arrays has been exhausted. Calling these arrays 'vectors' here.
let vectorsInstance = [
[1, 2],
[6, 7, 9],
[10, 11],
[1, 5, 8, 17]]
function getCombos(vectors) {
function countComb(vectors) {
let numComb = 1
for (vector of vectors) {
numComb *= vector.length
}
return numComb
}
let allComb = countComb(vectors)
let combos = []
for (let i = 0; i < allComb; i++) {
let thisCombo = []
for (j = 0; j < vectors.length; j++) {
let vector = vectors[j]
let prevComb = countComb(vectors.slice(0, j))
thisCombo.push(vector[Math.floor(i / prevComb) % vector.length])
}
combos.push(thisCombo)
}
return combos
}
console.log(getCombos(vectorsInstance))
While there's already plenty of good answers to get every combination, which is of course the original question, I'd just like to add a solution for pagination. Whenever there's permutations involved, there's the risk of extremely large numbers. Let's say, for whatever reason, we wanted to build an interface where a user could still browse through pages of practically unlimited permutations, e.g. show permutations 750-760 out of one gazillion.
We could do so using an odometer similar to the one in John's solution. Instead of only incrementing our way through the odometer, we also calculate its initial value, similar to how you'd convert for example seconds into a hh:mm:ss clock.
function getPermutations(arrays, startIndex = 0, endIndex) {
if (
!Array.isArray(arrays) ||
arrays.length === 0 ||
arrays.some(array => !Array.isArray(array))
) {
return { start: 0, end: 0, total: 0, permutations: [] };
}
const permutations = [];
const arrayCount = arrays.length;
const arrayLengths = arrays.map(a => a.length);
const maxIndex = arrayLengths.reduce(
(product, arrayLength) => product * arrayLength,
1,
);
if (typeof endIndex !== 'number' || endIndex > maxIndex) {
endIndex = maxIndex;
}
const odometer = Array.from({ length: arrayCount }).fill(0);
for (let i = startIndex; i < endIndex; i++) {
let _i = i; // _i is modified and assigned to odometer indexes
for (let odometerIndex = arrayCount - 1; odometerIndex >= 0; odometerIndex--) {
odometer[odometerIndex] = _i % arrayLengths[odometerIndex];
if (odometer[odometerIndex] > 0 && i > startIndex) {
// Higher order values in existing odometer are still valid
// if we're not hitting 0, since there's been no overflow.
// However, startIndex always needs to follow through the loop
// to assign initial odometer.
break;
}
// Prepare _i for next odometer index by truncating rightmost digit
_i = Math.floor(_i / arrayLengths[odometerIndex]);
}
permutations.push(
odometer.map(
(odometerValue, odometerIndex) => arrays[odometerIndex][odometerValue],
),
);
}
return {
start: startIndex,
end: endIndex,
total: maxIndex,
permutations,
};
}
So for the original question, we'd do
getPermutations([['A', 'B', 'C'], ['1', '2', '3']]);
-->
{
"start": 0,
"end": 9,
"total": 9,
"permutations": [
["A", "1"],
["A", "2"],
["A", "3"],
["B", "1"],
["B", "2"],
["B", "3"],
["C", "1"],
["C", "2"],
["C", "3"]
]
}
but we could also do
getPermutations([['A', 'B', 'C'], ['1', '2', '3']], 2, 5);
-->
{
"start": 2,
"end": 5,
"total": 9,
"permutations": [
["A", "3"],
["B", "1"],
["B", "2"]
]
}
And more importantly, we could do
getPermutations(
[
new Array(1000).fill(0),
new Array(1000).fill(1),
new Array(1000).fill(2),
new Array(1000).fill(3),
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'],
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'],
['X', 'Y', 'Z'],
['1', '2', '3', '4', '5', '6']
],
750,
760
);
-->
{
"start": 750,
"end": 760,
"total": 1800000000000000,
"permutations": [
[0, 1, 2, 3, "e", "B", "Z", "1"],
[0, 1, 2, 3, "e", "B", "Z", "2"],
[0, 1, 2, 3, "e", "B", "Z", "3"],
[0, 1, 2, 3, "e", "B", "Z", "4"],
[0, 1, 2, 3, "e", "B", "Z", "5"],
[0, 1, 2, 3, "e", "B", "Z", "6"],
[0, 1, 2, 3, "e", "C", "X", "1"],
[0, 1, 2, 3, "e", "C", "X", "2"],
[0, 1, 2, 3, "e", "C", "X", "3"],
[0, 1, 2, 3, "e", "C", "X", "4"]
]
}
without the computer hanging.
Here's a short recursive one that takes N arrays.
function permuteArrays(first, next, ...rest) {
if (rest.length) next = permuteArrays(next, ...rest);
return first.flatMap(a => next.map(b => [a, b].flat()));
}
Or with reduce (slight enhancement of Penny Liu's):
function multiply(a, b) {
return a.flatMap(c => b.map(d => [c, d].flat()));
}
[['a', 'b', 'c'], ['+', '-'], [1, 2, 3]].reduce(multiply);
Runnable example:
function permuteArrays(first, next, ...rest) {
if (rest.length) next = permuteArrays(next, ...rest);
return first.flatMap(a => next.map(b => [a, b].flat()));
}
const squish = arr => arr.join('');
console.log(
permuteArrays(['A', 'B', 'C'], ['+', '-', '×', '÷'], [1, 2]).map(squish),
permuteArrays(['a', 'b', 'c'], [1, 2, 3]).map(squish),
permuteArrays([['a', 'foo'], 'b'], [1, 2]).map(squish),
permuteArrays(['a', 'b', 'c'], [1, 2, 3], ['foo', 'bar', 'baz']).map(squish),
)
I had a similar requirement, but I needed get all combinations of the keys of an object so that I could split it into multiple objects. For example, I needed to convert the following;
{ key1: [value1, value2], key2: [value3, value4] }
into the following 4 objects
{ key1: value1, key2: value3 }
{ key1: value1, key2: value4 }
{ key1: value2, key2: value3 }
{ key1: value2, key2: value4 }
I solved this with an entry function splitToMultipleKeys and a recursive function spreadKeys;
function spreadKeys(master, objects) {
const masterKeys = Object.keys(master);
const nextKey = masterKeys.pop();
const nextValue = master[nextKey];
const newObjects = [];
for (const value of nextValue) {
for (const ob of objects) {
const newObject = Object.assign({ [nextKey]: value }, ob);
newObjects.push(newObject);
}
}
if (masterKeys.length === 0) {
return newObjects;
}
const masterClone = Object.assign({}, master);
delete masterClone[nextKey];
return spreadKeys(masterClone, newObjects);
}
export function splitToMultipleKeys(key) {
const objects = [{}];
return spreadKeys(key, objects);
}
one more:
const buildCombinations = (allGroups: string[][]) => {
const indexInArray = new Array(allGroups.length);
indexInArray.fill(0);
let arrayIndex = 0;
const resultArray: string[] = [];
while (allGroups[arrayIndex]) {
let str = "";
allGroups.forEach((g, index) => {
str += g[indexInArray[index]];
});
resultArray.push(str);
// if not last item in array already, switch index to next item in array
if (indexInArray[arrayIndex] < allGroups[arrayIndex].length - 1) {
indexInArray[arrayIndex] += 1;
} else {
// set item index for the next array
indexInArray[arrayIndex] = 0;
arrayIndex += 1;
// exclude arrays with 1 element
while (allGroups[arrayIndex] && allGroups[arrayIndex].length === 1) {
arrayIndex += 1;
}
indexInArray[arrayIndex] = 1;
}
}
return resultArray;
};
One example:
const testArrays = [["a","b"],["c"],["d","e","f"]]
const result = buildCombinations(testArrays)
// -> ["acd","bcd","ace","acf"]
My version of the solution by John D. Aynedjian, which I rewrote for my own understanding.
console.log(getPermutations([["A","B","C"],["1","2","3"]]));
function getPermutations(arrayOfArrays)
{
let permutations=[];
let remainder,permutation;
let permutationCount=1;
let placeValue=1;
let placeValues=new Array(arrayOfArrays.length);
for(let i=arrayOfArrays.length-1;i>=0;i--)
{
placeValues[i]=placeValue;
placeValue*=arrayOfArrays[i].length;
}
permutationCount=placeValue;
for(let i=0;i<permutationCount;i++)
{
remainder=i;
permutation=[];
for(let j=0;j<arrayOfArrays.length;j++)
{
permutation[j]=arrayOfArrays[j][Math.floor(remainder/placeValues[j])];
remainder=remainder%placeValues[j];
}
permutations.push(permutation.reduce((prev,curr)=>prev+curr,"")); }
return permutations;
}
First express arrays as array of arrays:
arrayOfArrays=[["A","B","C"],["a","b","c","d"],["1","2"]];
Next work out the number of permuations in the solution by multiplying the number of elements in each array by each other:
//["A","B","C"].length*["a","b","c","d"].length*["1","2"].length //24 permuations
Then give each array a place value, starting with the last:
//["1","2"] place value 1
//["a","b","c","d"] place value 2 (each one of these letters has 2 possibilities to the right i.e. 1 and 2)
//["A","B","C"] place value 8 (each one of these letters has 8 possibilities to the right i.e. a1,a2,b1,b2,c1,c2,d1,d2
placeValues=[8,2,1]
This allows each element to be represented by a single digit:
arrayOfArrays[0][2]+arrayOfArrays[1][3]+arrayOfArrays[2][0] //"Cc1"
...would be:
2*placeValues[2]+3*placesValues[1]+0*placeValues[2] //2*8+3*2+0*1=22
We actually need to do the reverse of this so convert numbers 0 to the number of permutations to an index of each array using quotients and remainders of the permutation number.
Like so:
//0 = [0,0,0], 1 = [0,0,1], 2 = [0,1,0], 3 = [0,1,1]
for(let i=0;i<permutationCount;i++)
{
remainder=i;
permutation=[];
for(let j=0;j<arrayOfArrays.length;j++)
{
permutation[j]=arrayOfArrays[j][Math.floor(remainder/placeValues[j])];
remainder=remainder%placeValues[j];
}
permutations.push(permutation.join(""));
}
The last bit turns the permutation into a string, as requested.
Make a loop like this
->
let numbers = [1,2,3,4,5];
let letters = ["A","B","C","D","E"];
let combos = [];
for(let i = 0; i < numbers.length; i++) {
combos.push(letters[i] + numbers[i]);
};
But you should make the array of “numbers” and “letters” at the same length thats it!