everyone I'm trying to get the best way to compare two arrays and add a new element with value 0 if there's any difference between them. First I got this 2 arrays:
var niv = ["FB","Bach","FPSE","FPPE","AA"];
var values = [["H", "FB", 3286.25, 509.75],["H", "Bach", 4587.5, 522.5],
["H", "FPPE", 160, 2624],["H", "AA", 334, 7454],
["M", "FB", 3803.75, 1743.25],["M", "Bach", 7856.5, 709.5],
["M", "FPPE", 799, 3785],["M", "AA", 1055, 11718,2],["N", "FB", 45, 0.0, 0.0]];
This is a poor example of what Im trying to do:
for (var i = 0; i < data.length; i++) {
for (var j = 0; j < values.length; j++) {
if (data[i][0] == values[j][0] &&
(values[i][1] == "FB" || values[i][1] == "Bach" ||
values[i][1] == "FPSE" || values[i][1] == "FPPE" || values[i][1] == "AA")) {
data[i].push(values[j][2],values[j][3])
} else {
data[i].push(0,0)
}
}
}
All I want to get is this:
[["H", "FB", 3286.25, 509.75],["H", "Bach", 4587.5, 522.5],
["H", "FPSE", 0.0, 0.0],["H", "FPPE", 160, 2624],
["H", "AA", 334, 7454],["M", "FB", 3803.75, 1743.25],["M", "Bach", 7856.5, 709.5],
["M", "FPSE", 0.0, 0.0],["M", "FPPE", 799, 3785],
["M", "AA", 1055, 11718,2],["N", "FB", 45, 0.0],["N", "Bach", 0.0, 0.0],
["N", "FPSE", 0.0, 0.0],["N", "FPPE", 0.0, 0.0],["N", "AA", 0.0, 0.0]];
I hope I'm not late.
I think you should divide the work into several sections so that it is not so difficult to reach the goal.
Starting from the two arrays with the information: niv and values
var niv = ["FB","Bach","FPSE","FPPE","AA"];
var values = [["H", "FB", 3286.25, 509.75],["H", "Bach", 4587.5, 522.5],
["H", "FPPE", 160, 2624],["H", "AA", 334, 7454],
["M", "FB", 3803.75, 1743.25],["M", "Bach", 7856.5, 709.5],
["M", "FPPE", 799, 3785],["M", "AA", 1055, 11718,2],["N", "FB", 45, 0.0, 0.0]];
I propose to divide the array values into 3 arrays, one for H, another for M and another for N.
var man = [];
var wom = [];
var unk = [];
for (var i = 0; i < values.length; i++){
if (values[i][0] == "H") man.push(values[i]);
if (values[i][0] == "M") wom.push(values[i]);
if (values[i][0] == "N") unk.push(values[i]);
}
Then it is a question of going through the arrays and checking whether or not the values correspond to the array niv. To verify that, I propose that you make a function that receives an array and a string with the word to search and returns true or false if it finds it or not.
Knowing if it is or not, you can add a new element in the array. With the splice method the element will be added in the position where it should be. With this you would have 3 complete and ordered arrays.
for (var i = 0; i < niv.length; i++){
if(!checkArray(man,niv[i])){
man.splice(i,0,["H",niv[i],0,0]);
}
}
for (var i = 0; i < niv.length; i++){
if(!checkArray(wom,niv[i])){
wom.splice(i,0,["M",niv[i],0,0]);
}
}
for (var i = 0; i < niv.length; i++){
if(!checkArray(unk,niv[i])){
unk.splice(i,0,["N",niv[i],0,0]);
}
}
function checkArray (array, niv){
for (var i = 0; i < array.length; i++){
if(array[i][1] === niv){
return true;
}
}
return false;
}
I hope I've helped
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!