How can i get an available combination to replace the repetition? - javascript

How can i combine the arrays?
[
['1','1','1','2','1'],
['3','3','4','4','4'],
['6','6','7','8','7'],
]
with those arrays there are many combinations, some of the are these:
1,3,6
1,3,6 -> this not, because its duplicate
1,4,7
2,4,8
1,4,7 -> this not, because its duplicate
how can we replace the wrong combination? one solution will be:
1,3,6
1,3,7 -> fixed
1,4,6
1,4,8
2,4,7 -> fixed
rules:
in the first column, 1 should appear 4 times
in the first column, 2 should appear 1 times
in the second column, 3 should appear 2 times
in the second column, 4 should appear 3 times
in the third column, 6 should appear 2 times
in the third column, 7 should appear 2 times
in the third column, 8 should appear 1 times
my attempt is: i would like to generate many combinations, but its not safe the number of appears
function available(array) {
let currentIndex = array.length,
randomIndex;
while (currentIndex != 0) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
[array[currentIndex], array[randomIndex]] = [
array[randomIndex], array[currentIndex]
];
}
return array;
}
const arrays = [
['1', '1', '1', '2', '1'],
['3', '3', '4', '4', '4'],
['6', '6', '7', '8', '7'],
]
for (const current of arrays) {
console.log(available(current))
}

Ignore that the individual strings contain digits. This doesn't matter. At that level, all that matters is that they're strings. (We wouldn't care about that either, just that they're some kind of comparable symbol, but we can take advantage of the fact that they're stings in validation.)
Integers, in general, don't have to have any particular representation. Just because, say, 3 is shaped like that doesn't say anything about the concept of 3. It could be represented in any way so long as it's unique relative to others of its type. For example, we could represent it as [1,1,1].
For each row, we need to iterate through the permutations of that row. Heap's Algorithm allows us to do that in a predictable way without breaking the rules for how many of each kind of item goes in each row.
If we think of that iteration as a kind of counting (the first permutation is 1, the next is 2, etc.), we can count through the permutations of each row as though they were digits in a strange kind of integer. In this way, we can iterate over all combinations of the permutations of the rows.
All that's left is validating each result against duplicate columns. We do this by joining along columns, and adding the results to a set. So long as the set ends up with the same length as the rows, we know there were no duplicates.
solve() only returns the first solution encountered, if any. However, it would be pretty simple to adjust it to return all solutions.
We use .every as a kind of .forEach with the additional ability to end the loop early and to signal whether or not that happened.
function* is known as a generator function. They return a generator object, a thing that wraps the function and lets you return values (via yield) and then later come back and resume the function from that point.
// Heap's Algorithm modified to skip swaps of the same value
// Allows us to iterate over permutations of a row
function* iterHeapsAlgo(arr) {
const A = Array.from(arr); // shallow copy
const len = A.length;
const c = new Array(len).fill(0);
let i = 0;
yield A;
while(i < len) {
if(c[i] < i) {
let j = i&1 ? c[i] : 0;
if(A[i] != A[j]) {
[A[j], A[i]] = [A[i], A[j]];
yield A;
}
c[i]++;
i = 0;
} else {
c[i] = 0;
i++;
}
}
};
// Iterate over all combinations of all rows
// Typical counting algorithm with shortcut to take advantage of exposed state
function* iterCount(data) {
const state = data.map(v => iterHeapsAlgo(v));
const current = state.map(v => v.next().value);
yield current;
while(true) {
const isEnd = state.every((v,i) => {
let n = v.next();
if(n.done) {
state[i] = iterHeapsAlgo(data[i]);
current[i] = state[i].next().value;
return true;
}
});
if(isEnd) return;
yield current;
}
}
const validate = data => {
const set = new Set();
return data[0].every((_,i) => {
set.add(data.reduce((s,v) => `${s}\uffff${v[i]}`, ''));
return set.size-1 == i;
});
};
const solve = start => {
for(const current of iterCount(start)) {
if(validate(current)) {
return current.map(v => Array.from(v)); // depth 2 copy
}
}
return null;
};
console.log(solve([
['1','1','1','2','1'],
['3','3','4','4','4'],
['6','6','7','8','7'],
]) || 'No solution found');
.as-console-wrapper { top: 0; max-height: 100% !important; }
EDIT: Adjustments based on comments. Thanks #Mulan and #ScottSauyet

Maybe I'm missing something, but it seems like you're looking for the product of the arrays with only unique combinations of elements -
const unique = a =>
Array.from(new Set(a))
const product = t =>
t.length == 0
? [[]]
: product(t.slice(1)).flatMap(p => unique(t[0]).map(v => [v, ...p]))
const myinput = [
['1','1','1','2','1'],
['3','3','4','4','4'],
['6','6','7','8','7'],
]
for (const combination of product(myinput))
console.log(String(combination))
1,3,6
2,3,6
1,4,6
2,4,6
1,3,7
2,3,7
1,4,7
2,4,7
1,3,8
2,3,8
1,4,8
2,4,8
By inverting the loops, we can generate the unique products in lexicographical order -
const bind = (f, x) => f(x)
const unique = a =>
Array.from(new Set(a))
const product = t =>
t.length == 0
? [[]]
: bind
( r => unique(t[0]).flatMap(v => r.map(p => [v, ...p]))
, product(t.slice(1))
)
const myinput = [
['1','1','1','2','1'],
['3','3','4','4','4'],
['6','6','7','8','7'],
]
for (const combination of product(myinput))
console.log(String(combination))
1,3,6
1,3,7
1,3,8
1,4,6
1,4,7
1,4,8
2,3,6
2,3,7
2,3,8
2,4,6
2,4,7
2,4,8
Finally we can generate the products lazily by using a generator -
const unique = a =>
Array.from(new Set(a))
function* product(t) {
if (t.length == 0)
yield []
else
for (const p of product(t.slice(1)))
for (const v of unique(t[0]))
yield [v, ...p]
}
const myinput = [
['1','1','1','2','1'],
['3','3','4','4','4'],
['6','6','7','8','7'],
]
for (const combination of product(myinput))
console.log(String(combination))
1,3,6
2,3,6
1,4,6
2,4,6
1,3,7
2,3,7
1,4,7
2,4,7
1,3,8
2,3,8
1,4,8
2,4,8
If you are looking for permutations, see this related Q&A.

Related

JS function to compare two arrays of numbers that include duplicates

I'm building a mastermind game. In this game the player needs to guess a secret random number. To make this work I need to have a function that can compare the two arrays and check if there is a match in number and position and/or a match only in the number but not the position.
The problem: This function works well for the most part when there are not repeated numbers in the arrays, but gives me a wrong output when there are repeated numbers.
example:
Random -- arr1 = ['5', '5', '3', '4']
Guess -- arr 2 = ['5', '1', '0', '0,]
expected result
{match: true, exactMatches: 1, matchesByValue: 0}
I'm getting this wrong result:
{match: true, exactMatches: 1, matchesByValue: 1}
Important: matchesByValue means correct number in wrong position. It shouldn't consider numbers that have been already counted as exactMatches.
function compareGuessVsRandom(arr1, arr2) {
// convert the arrays to sets
const set1 = new Set(arr1);
const set2 = new Set(arr2);
let exactMatches = 0;
let matchesByValue = 0;
// check if each value in the first array has the same value and position in the second array
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] == arr2[i]) {
exactMatches++;
} else if (set1.has(arr2[i])) {
matchesByValue++;
}
}
// if all checks pass, the arrays are a match
const result = {
match: true,
exactMatches: exactMatches,
matchesByValue: matchesByValue,
};
console.log(result);
return result;
}
function compareGuessVsRandom (arr1, arr2) {
// The list of all the indicies at which there is an exact match
const exactMatchesList = []
// The amount of matche by value
let matchesByValue = 0
for (let i = 0; i < arr1.length; i++)
if (arr1[i] === arr2[i])
exactMatchesList.push(i)
// Init sets with arrays without the exactly-matched values
const set1 = new Set(arr1.filter((_, i) => !exactMatchesList.includes(i)))
const set2 = new Set(arr2.filter((_, i) => !exactMatchesList.includes(i)))
// If set2 contains an element of set1, increment matchesByValue
for (const e of set1)
matchesByValue += set2.has(e)
// Get the amount of exact matches
const exactMatches = exactMatchesList.length
const result = {
match: exactMatches !== 0 || matchesByValue !== 0,
exactMatches,
matchesByValue
}
return result;
}
const random = [ '5', '5', '3', '4' ]
const guess = [ '5', '1', '0', '0' ]
console.log(compareGuessVsRandom(random, guess))
The has method returns true if set1 contains the specified element. So regardless of whether an exact match has occurred or not, it will increment 'matchesByValue' even for a match at the index with the exact match.
For example, in a scenario where arr2 has [5,5,0,0]. The first '5'. will increment 'exactMatches' but the second one will also increment 'matchesByValue' by matching it with index 0 of set1.
You can eliminate duplicate matches by comparing indexes of the two types of matches.
This function solved the problem with duplicates. Thanks everyone!
//helper function to count how many values two arrays have in common
const countCommonValuesOfArrays = (arr1, arr2) =>
arr1.reduce((a, c) => a + arr2.includes(c), 0);
export function compareGuessVsRandom(guesses, random) {
//check for duplicates
let perfectMatch = [];
let equalValues = [];
let unmatchedGuesses = [];
let unmatchedRandom = [];
for (let i = 0; i < guesses.length; i++) {
if (guesses[i] === random[i]) {
perfectMatch++;
} else {
unmatchedGuesses.push(guesses[i]);
unmatchedRandom.push(random[i]);
}
}
equalValues = countCommonValuesOfArrays(unmatchedGuesses, unmatchedRandom);
return {
gameMatch: perfectMatch === random.length,
perfectMatch: perfectMatch,
equalValues: equalValues,
};
}

JavaScript Trying to Print The Number of Times a Letter Appears In String But It Prints More than Once

In the code below, I am trying to check how many times a letter in a string appears. Problem with the code below is that it prints each letter more than once. It needs to collect all the same letters and show the number of times it occurs in the string and display it once.
const string = 'mississippi'
const letters = [...string]
let currentLetter = ''
let letterOccurance = []
for(let i = 0; i < letters.length; i++){
let letterFrequency = letters.filter((letter)=>{
return letter === letters[i]
})
letterOccurance.push([`${letters[i]}`,letterFrequency.length])
}
console.log(letterOccurance)
That's too much code just to get the number of times a letter appears in a string. Try the following code:
const string = 'mississippi';
let frequency = {};
for (let letter of string) {
if (frequency[letter]) {
frequency[letter]++;
} else {
frequency[letter] = 1;
}
}
console.log(frequency);
You're always pushing the letter to the array, whether it already exists there or not:
letterOccurance.push([`${letters[i]}`,letterFrequency.length])
You could check if it exists first:
if (!letterOccurance.find(l => l[0] === letters[i])) {
letterOccurance.push([`${letters[i]}`,letterFrequency.length])
}
Or even skip it entirely if you've already seen it, since the first time you find any letter you already know its complete count:
for(let i = 0; i < letters.length; i++){
if (letterOccurance.find(l => l[0] === letters[i])) {
continue;
}
// the rest of the loop...
}
There's honestly a variety of ways you could step back and re-approach the problem. But for the question about why letters are repeating, that's simply because each iteration of the loop unconditionally appends the letter to the resulting array.
How about writing a more generic item-counting function and then layering countLetters as a simple partial application of the identity function?
const countBy = (fn) => ([...xs]) =>
xs .reduce ((a, x) => {const k = fn (x); a [k] = (a[k] || 0) + 1; return a}, {})
const countLetters = countBy (x => x)
console .log (countLetters ('missisippi'))
countBy is fairly generic. You pass it a function to convert your values to strings, and pass your array of items to the function it returns. Strings are array-like enough that this just works for our simple countLetters. But we could use it for other counts as well, such as:
countBy (x => x .grade) ([{id: 1, grade: 'A'}, {id: 2, grade: 'B'}, {id: 3, grade: 'A'}])
//=> {"A": 2, "B": 1}
Here's a solution using a Set to get the individual letters and String.split() to count.
const countChars = str => Object.fromEntries(
[...new Set(str)]
.map(c => [c, str.split(c).length-1])
)
console.log(countChars('mississippi'));
Using reduce to build the object
const countChars = str => [...str].reduce(
(a, c) => (a[c] ? a[c]++ : a[c]=1, a),
{}
)
console.log(countChars('mississippi'));
var result =

Finding all permutations of array elements as concatenated strings

I am trying to write a JavaScript function that returns all combinations of the elements of an array of unknown length. The argument to be passed to the function should be an array of single digit strings e.g. [ "5", "7", "9" ].
An example to illustrate the desired functionality:
If you pass in an array of [ "5", "7", "9" ], it should return an array with all the possible 3-digit combinations of those 3 numbers i.e. [ "579", "759", "957", "795",…].
If you passed in an array of [ "2", "5", "4", "6" ], you would get the 4-digit combinations of those 4 numbers, i.e. [ "2546", "2654", "2465",…].
If you passed in an array of length 5, you would get the 5-digit combinations of those 5 numbers, and so on.
If the inputted array has the same digit multiple times, that number should appear multiple times in the resulting array e.g. an input of [ "5", "5", "6" ] produces an output of [ "556", "655", "565",…].
I have looked around and it seems that recursion might be the way to go, but I am struggling to get it working. I have attempted the below solution which currently works for 3-digit numbers but I can’t figure out how to make a function which works with an array of unknown length.
function getDoubleDigitCombinations(input) {
let result = [];
const first = input[0];
const last = input[1];
result.push(first + last);
result.push(last + first);
return result;
}
function getTripleDigitCombinations(input) {
let result = [];
let main; // This is the number in question.
let other1; // These are the other numbers.
let other2;
for (i = 0; i < input.length; i++) {
let arr = input.slice(); // Make a copy of the input array.
main = input[i];
arr.splice(i, 1); // Remove the main element from the array …
other1 = arr[0]; // … so that you are left with the other two numbers.
other2 = arr[1];
console.log(`Main is ${main}`);
console.log(`other1 is ${other1} and other2 is ${other2}`);
const arr2 = getDoubleDigitCombinations([other1, other2]); // Get the combinations of the others.
result.push(main + arr2[0]); // Concatenate main with both of the others combinations.
result.push(main + arr2[1]);
}
return result;
}
let result2 = getTripleDigitCombinations([ "6", "7", "8" ]);
console.log("result2 is ...");
for (i = 0; i < result2.length; i++) {
console.log(result2[i]);
}
Heap's algorithm is still, I believe, the gold standard for efficiency. Victor's answer covers that well.
But since any algorithm has to be at least O(n!), we're never going to get stunning performance in generating permutations. We might want to look also at simplicity.
Another implementation is decidedly simpler:
const excluding = (i) => (xs) =>
[... xs .slice (0, i), ... xs .slice (i + 1)]
const permutations = (xs) =>
xs .length == 0
? [[]]
: xs .flatMap ((x, i) => permutations (excluding (i) (xs)) .map (p => x + p))
console .log (permutations (['5', '6', '7']))
Here we use a small helper function, excluding, which returns a copy of an array removing the value at a given index. Then permutations loops over the values in the array, taking each in turn to be the first value of a set of permutations, and finding the remainder of those permutations by recurring on the array found by excluding the current index.
This has the nice feature that the permutations are returned in an obvious order. If the original array is sorted, say ['a', 'b', 'c'], then the results are returned in alphabetic order: ['abc', 'acb', 'bac', 'bca', 'cab', 'cba']. This is not usually essential, but it can be useful.
Note that this solution is more commonly written with this line:
: xs .flatMap ((x, i) => permutations (excluding (i) (xs)) .map (p => [x, ... p]))
which returns an array of arrays ([['5', '6', '7'], ['5', '7', '6'], ...]) instead of an array of strings (['567', '576', ...]).
There is another version which I've seen recently that is even simpler. It's not original, but I don't recall where I saw it -- probably here on StackOverflow. This is my own implementation of that idea, but I think it's pretty close to the original:
const rotations = ([l, ...ls], rs = []) =>
l == undefined ? [] : [[l, ...ls, ...rs], ... rotations (ls, [...rs, l])]
const permutations = ([l, ...ls]) =>
l == undefined ? [[]] : [...permutations (ls) .flatMap (p => rotations ([l, ...p])) ]
console .log (permutations (['5', '6', '7']) .map (p => p .join ('')))
.as-console-wrapper {max-height: 100% !important; top: 0}
Here we use a function rotations which takes an array and returns all the wrapped-around rotations of that array. For example:
rotations(['x', 'y', 'z'])
//=> [['x', 'y', 'z'], ['y', 'z', 'x'], ['z', 'x', 'y']]
We use that to write permutations by taking out the first element, recursively finding the permutations of the remaining elements, and for each, sticking the first element back on and returning all the rotations.
This is short and quite clever. I would probably stick to either Heap's algorithm for speed or to the one above for the nicely ordered output. But this one is still worth considering for the simplicity.
While this algorithm could be fixed up to return strings, it would be more intrusive than it was in the previous version, involving changes to both rotations and permutations, and I would prefer to stick to generating the strings on the output of it as done above with .map (p => p .join ('')). If you wanted to do it, though, it might look like this:
const rotations = ([l, ...ls], rs = '') =>
l == undefined ? [] : [l + ls.join('') + rs, ... rotations (ls, rs + l)]
const permutations = ([l, ...ls]) =>
l == undefined ? [[]] : [...permutations (ls) .flatMap (p => rotations (l + p)) ]
permutations (['5', '7', '7']) //=> ['577', '775', '757', '577', '775', '757']
A fun problem! I wanted to implement using generators. This allows you to work with the permutations one-by-one as they are generated, rather than having to compute all permutations before the entire answer is provided -
const input =
["🔴","🟢","🔵","🟡"]
for (const p of permutations(input))
console.log(p.join(""))
🔴🟢🔵🟡
🟢🔴🔵🟡
🟢🔵🔴🟡
🟢🔵🟡🔴
🔴🔵🟢🟡
🔵🔴🟢🟡
🔵🟢🔴🟡
🔵🟢🟡🔴
🔴🔵🟡🟢
🔵🔴🟡🟢
🔵🟡🔴🟢
🔵🟡🟢🔴
🔴🟢🟡🔵
🟢🔴🟡🔵
🟢🟡🔴🔵
🟢🟡🔵🔴
🔴🟡🟢🔵
🟡🔴🟢🔵
🟡🟢🔴🔵
🟡🟢🔵🔴
🔴🟡🔵🟢
🟡🔴🔵🟢
🟡🔵🔴🟢
🟡🔵🟢🔴
This allows us to do cool things like, finding specific patterns -
// find all permutations where red is left of green
for (const p of permutations(input))
if (p.indexOf("🔴") < p.indexOf("🟢"))
console.log(p.join(""))
🟡🔵🔴🟢
🔵🟡🔴🟢
🔵🔴🟢🟡
🔵🔴🟡🟢
🟡🔴🟢🔵
🟡🔴🔵🟢
🔴🟢🟡🔵
🔴🟡🟢🔵
🔴🟡🔵🟢
🔴🟢🔵🟡
🔴🔵🟢🟡
🔴🔵🟡🟢
// find all permutations where blue and yellow are adjacent
for (const p of permutations(input))
if (Math.abs(p.indexOf("🔵") - p.indexOf("🟡")) == 1)
console.log(p.join(""))
🟢🟡🔵🔴
🟡🔵🟢🔴
🟡🔵🔴🟢
🟢🔵🟡🔴
🔵🟡🟢🔴
🔵🟡🔴🟢
🟢🔴🟡🔵
🔴🟢🟡🔵
🔴🟡🔵🟢
🟢🔴🔵🟡
🔴🟢🔵🟡
🔴🔵🟡🟢
And if we wanted to find the only the first permutation where such a condition is true, we can use return or break to stop the generator and no more permutations will be computed.
We just have to implement permutations -
function* permutations (t)
{ if (t.length < 2)
yield t
else
for (const p of permutations(t.slice(1)))
for (const r of rotations(p, t[0]))
yield r
}
Which depends on rotations -
function* rotations (t, v)
{ if (t.length == 0)
yield [v]
else
yield *chain
( [[v, ...t]]
, map(rotations(t.slice(1), v), r => [t[0], ...r])
)
}
Which depends on two generic functions for working with iterables, map and chain -
function* map (t, f)
{ for (const e of t)
yield f(e)
}
function* chain (...ts)
{ for (const t of ts)
for (const e of t)
yield e
}
Expand the snippet to verify the results in your own browser
function* permutations (t)
{ if (t.length < 2)
yield t
else
for (const p of permutations(t.slice(1)))
for (const r of rotations(p, t[0]))
yield r
}
function* rotations (t, v)
{ if (t.length == 0)
yield [v]
else
yield *chain
( [[v, ...t]]
, map(rotations(t.slice(1), v), r => [t[0], ...r])
)
}
function* map (t, f)
{ for (const e of t)
yield f(e)
}
function* chain (...ts)
{ for (const t of ts)
for (const e of t)
yield e
}
const input =
["🔴","🟢","🔵","🟡"]
console.log("\nred is left of green")
for (const p of permutations(input))
if (p.indexOf("🔴") < p.indexOf("🟢"))
console.log(p.join(""))
console.log("\nblue and yellow are adjacent")
for (const p of permutations(input))
if (Math.abs(p.indexOf("🔵") - p.indexOf("🟡")) == 1)
console.log(p.join(""))
I hope you enjoyed this post as much as I enjoyed writing it :D
To compute combinations using a similar technique, see this related Q&A.
Heap's algorithm can be used to generate all permutations (without repetitions) of array elements, you can then use those permutations with array.join("") to convert them to strings. This works for arrays of any size and any type:
Here is a quick javascript implementation:
// some global variable to store the results
var result = []
// currentSize should be invoked with the array size
function permutation(arr, currentSize) {
if (currentSize == 1) { // recursion base-case (end)
result.push(arr.join(""));
return;
}
for (let i = 0; i < currentSize; i++){
permutation(arr, currentSize - 1);
if (currentSize % 2 == 1) {
let temp = arr[0];
arr[0] = arr[currentSize - 1];
arr[currentSize - 1] = temp;
} else {
let temp = arr[i];
arr[i] = arr[currentSize - 1];
arr[currentSize - 1] = temp;
}
}
}
let array = ["1","2","3","4"];
permutation(array, array.length);
console.log(result);
// use result here
Keep in mind that this is very computationally expensive, with complexity of O(n!).
The following might serve your need after adjusting argument to accept array instead of a number.
function combinator(nbr) {
if (typeof nbr !== 'number' || nbr>999999999) return 'NaN'
const combinedArr = []
const _nbr = `${nbr}`.split('')
combinatorRec(_nbr, [])
function combinatorRec(_nbr, prev){
if (_nbr.length === 0) {
combinedArr.push(parseInt(prev))
return
}
_nbr.forEach((char,i)=>{
const _nbrI = [..._nbr]
_nbrI.splice(i,1)
combinatorRec(_nbrI, prev+char )
})
}
const uniqueArray = combinedArr.filter((item, pos) => (combinedArr.indexOf(item) === pos))
return uniqueArray
}

All variations of matrix

I am trying to write an algorithm for getting all possible variations of a matrix in JavaScript.
Here is what I want to achieve:
blue,red
male,female,other
small,medium,large
-----------------
blue,male,small
blue,male,medium
blue,male,large
blue,female,small,
blue,female,medium,
blue,female,large
blue,other,small,
blue,other,medium,
blue,other,large
red,male,small
red,male,medium
red,male,large
red,female,small,
red,female,medium,
red,female,large
red,other,small,
red,other,medium,
red,other,large
Any idea how this can be done?
Wht you want is called the Cartesian product of several lists. If you have a fixed set of lists, nested loops are an easy way to generate the Cartesian product.
You can generalize this for an arbitrary list by iterating through the lists in odometer style. (Each digit of the odometer may have a different range, though.)
Here's how:
function cartesian(m) {
const res = [];
const index = []; // current index
const max = []; // length of sublists
for (let i = 0; i < m.length; i++) {
index.push(0);
max.push(m[i].length);
}
for (;;) {
res.push(index.map((i, j) => m[j][i]));
let i = 0;
index[i]++;
while (index[i] == max[i]) {
index[i] = 0;
i++;
if (i == m.length) return res;
index[i]++;
}
}
}
Call it like this:
const m = [
["blue", "red"],
["male", "female", "other"],
["small", "medium", "large"],
];
const p = cartesian(m);
This creates one huge list of all possibilities, which probably isn't ideal. You can change the function by doing whatever you want to do with each possibilitiy where the code above pushes to current list to the result array. The code above changes the first item in each iteration, which is the opposite of what you show in your post.
You can iterate through arrays using reduce:
let data = ['blue','red']
let data2 =['male','female','other']
let data3 =['small','medium','large']
let result = data.reduce((acc,rec) => {
return acc.concat(data2.reduce((acc2, rec2) => {
return acc2.concat(data3.reduce((acc3,rec3) =>{
return acc3.concat([`${rec}, ${rec2}, ${rec3}`])
},[]))
},[]))
},[])
console.log(result)

Get all possible set of combinations of two arrays as an array of arrays with JavaScript

Please note: the linked question, "How can I create every combination possible for the contents of two arrays?" does not solve this particular question. The persons that labeled that did not fully understand this specific permutation and request.
If you have two arrays (arr1, arr2) with n elements in each array (i.e., each array will be the same length), then the question is: What's the best method to get/determine all the possible matches where elements do not match with other elements in the same array and where order does not matter?
For example, let's say I have:
arr1 = ["A","B","C"];
arr2 = ["Z","Y","X"];
I would like to get back an array of arrays where each element of one array is paired with an element of another array. So the result would be a unique set of arrays:
matches = [
[["A","Z"],["B","Y"],["C","X"]],
[["A","Z"],["B","X"],["C","Y"]],
[["A","Y"],["B","X"],["C","Z"]],
[["A","Y"],["B","Z"],["C","X"]],
[["A","X"],["B","Z"],["C","Y"]],
[["A","X"],["B","Y"],["C","Z"]],
]
Please note, these two arrays would be the same:
[["A","Z"],["B","Y"],["C","X"]]
[["B","Y"],["C","X"],["A","Z"]]
I am trying to do this with vanilla JavaScript but am completely open to using Lodash as well. For an added bonus, since this can get out of control, speed and performance are important. But right now, I am just trying to get something that would yield a proper result set. To limit this, this function would probably not be used with more than two arrays of 50 elements each.
Here is my latest attempt (using lodash):
function getMatches(arr1, arr2){
var matches = [];
for (var arr1i = 0, arr1l = arr1.length; arr1i < arr1l; arr1i++) {
for (var arr2i = 0, arr2l = arr2.length; arr2i < arr2l; arr2i++) {
matches.push(_(arr1).zip(arr2).value());
arr2.push(arr2.shift());
}
}
return matches;
}
[[A, 1], [B, 2]]
is the same as
[[B, 2], [A, 1]]
in your case, which means that the solution depends on what you pair to the first elements of your array. You can pair n different elements as second elements to the first one, then n - 1 different elements as second elements to the second one and so on, so you have n! possibilities, which is the number of possible permutations.
So, if you change the order of the array elements but they are the same pair, they are equivalent, so you could view the first elements as a fixed ordered set of items and the second elements as the items to permutate.
Having arr1 = [a1, ..., an] and arr2 = [b1, ..., bn] we can avoid changing the order of a1. So, you permutate the inner elements and treat the outer elements' order as invariant, like:
const permutations = function*(elements) {
if (elements.length === 1) {
yield elements;
} else {
let [first, ...rest] = elements;
for (let perm of permutations(rest)) {
for (let i = 0; i < elements.length; i++) {
let start = perm.slice(0, i);
let rest = perm.slice(i);
yield [...start, first, ...rest];
}
}
}
}
var other = ['A', 'B', 'C'];
var myPermutations = permutations(['X', 'Y', 'Z']);
var done = false;
while (!done) {
var next = myPermutations.next();
if (!(done = next.done)) {
var output = [];
for (var i = 0; i < next.value.length; i++) output.push([other[i], next.value[i]]);
console.log(output);
}
}
You're just looking for permutations. The first elements of your tuples are always the same, the second ones are permuted so that you get all distinct sets of combinations.
const arr1 = ["A","B","C"];
const arr2 = ["Z","Y","X"];
const result = permutate(arr2).map(permutation =>
permutation.map((el, i) => [arr1[i], el])
);
This implementation uses Typescript and Lodash.
const permutations = <T>(arr: T[]): T[][] => {
if (arr.length <= 2)
return arr.length === 2 ? [arr, [arr[1], arr[0]]] : [arr];
return reduce(
arr,
(acc, val, i) =>
concat(
acc,
map(
permutations([...slice(arr, 0, i), ...slice(arr, i + 1, arr.length)]),
vals => [val, ...vals]
)
),
[] as T[][]
);
};

Categories