How do I easily combine elements from two arrays in Javascript, alternating elements? - javascript

I have two arrays in JavaScript, of potentially different lengths:
var x = ['a', 'b', 'c'];
var y = ['g', 'h', 'i', 'j'];
I'd like to combine them into one array:
var z = ['a', 'g', 'b', 'h', 'c', 'i', 'j'];
How can I do that in JavaScript?

I see you answered your question at the same time as asking it. That's fine, but it's now clear that you were looking for a solution that leverages a library (eg, lodash) and not necessarily one that teaches you how to build such a procedure. In retrospect, I would've answered this differently, but nevertheless I think you can learn something from this answer.
I would recommend calling this something other than zip just because zip is used as name for a procedure that does something quite different from what you're looking for.
Here's a simple recursive definition of interleave -
const interleave = ([ x, ...xs ], ys = []) =>
x === undefined
? ys // base: no x
: [ x, ...interleave (ys, xs) ] // inductive: some x
const xs = [ 'a', 'b', 'c' ]
const ys = [ 'g', 'h', 'i', 'j' ]
console .log (interleave (xs, ys))
// [ a, g, b, h, c, i, j ]
And another variation that supports any number of input arrays -
const interleave = ([ x, ...xs ], ...rest) =>
x === undefined
? rest.length === 0
? [] // base: no x, no rest
: interleave (...rest) // inductive: no x, some rest
: [ x, ...interleave (...rest, xs) ] // inductive: some x, some rest
const ws = [ '0', '1', '2', '3' ]
const xs = [ 'a', 'b', 'c' ]
const ys = [ 'd', 'e', 'f' ]
const zs = [ 'g', 'h', 'i', 'j' ]
console .log (interleave (ws, xs, ys, zs))
// [ 0, a, d, g, 1, b, e, h, 2, c, f, i, 3, j ]

tl;dr: z = _.flatten(_.zip(x, y)).filter(element => element), as long as you don't care about null elements in the original arrays.
Some of the libraries providing functional tools, such as Lodash, provide enough mechanics to easily do this. For example, you can do this:
var z1 = _.zip(x, y);
// z1 is now [["a","g"],["b","h"],["c","i"],[null,"j"]]
var z2 = _.flatten(z1);
// z2 is now ["a","g","b","h","c","i",null,"j"]
var z3 = z2.filter(element => element)
// z3 is now ["a","g","b","h","c","i","j"]
Note that this will only work if the original arrays do not contain any null elements, as they are filtered out by the last step.

A simple implementation that will stitch the arrays:
function stitch(x, y) {
var arr = [];
var length = Math.max(x.length, y.length);
for(var i = 0; i < length; i++) {
i < x.length && arr.push(x[i]);
i < y.length && arr.push(y[i]);
}
return arr;
}
var x = ['a', 'b', 'c'];
var y = ['g', 'h', 'i', 'j'];
console.log(stitch(x, y));

This is the functional way to address the problem:
var x = ['a', 'b', 'c'];
var y = ['g', 'h', 'i', 'j'];
function stitch(x,y) {
var a = x.length > y.length ? x : y;
var b = x.length > y.length ? y : x;
var c = a.map(function (e, i) {
return b.length<i ? [e, b[i]] : [];
});
return [].concat.apply([],c)
}

Here's a very simple recursive solution:
const interlace = (xxs, ys) => {
if (xxs.length === 0) return ys;
const [x, ...xs] = xxs;
return [x, ...interlace(ys, xs)];
};
const xs = ['a', 'b', 'c'];
const ys = ['g', 'h', 'i', 'j'];
console.log(JSON.stringify(interlace(xs, ys)));
In addition, you can easily generalize this algorithm to an arbitrary number of arrays:
const interlace = (...xss) => xss.length > 0 ? interleave(...xss) : [];
const interleave = (xxs, ...yss) => {
if (xxs.length === 0) return interlace(...yss);
const [x, ...xs] = xxs;
return [x, ...interleave(...yss, xs)];
};
const xs = ['a', 'b', 'c'];
const ys = ['g', 'h', 'i', 'j'];
const zs = ['d', 'e', 'f'];
console.log(JSON.stringify(interlace()));
console.log(JSON.stringify(interlace(xs)));
console.log(JSON.stringify(interlace(xs, ys)));
console.log(JSON.stringify(interlace(xs, ys, zs)));
Hope that helps.

This can be done in regular Javascript. No need for fancy tricks:
function splicer(array, element, index) {
array.splice(index * 2, 0, element);
return array;
}
function weave(array1, array2) {
return array1.reduce(splicer, array2.slice());
}
var x = ['a', 'b', 'c'];
var y = ['g', 'h', 'i', 'j'];
var z = weave(x, y);
console.log(z);

var x = ['a', 'b', 'c'];
var y = ['g', 'h', 'i', 'j'];
var z=[];
if(y.length>=x.length){
for(var i=0;i<x.length;i++){
z.push(x[i]);
z.push(y[i]);
}
while(i<y.length)
z.push(y[i++]);
}else{
for(var i=0;i<y.length;i++){
z.push(x[i]);
z.push(y[i]);
}
while(i<x.length)
z.push(x[i++]);
}
window.alert(JSON.stringify(z)); // print ["a","g","b","h","c","i","j"]

Related

Group every two items in an array while sharing the endings, like a chain

Assuming I have an array like this: [A, B, C, D, E, F]
, How can I group them like this:
[[A, B], [B, C], [C, D], [D, E], [E, F]]
(Notice how every last element is shared with the next group, but with the opposite index.)
I know this ain't a big deal of a problem, but I'm trying to keep it simple and short, maybe with Array.reduce() if possible:
arr.reduce(function (rows, key, index) {
return (index % 2 == 0 ? rows.push([key])
: rows[rows.length-1].push(key)) && rows;
}, []);
// Output: [[A, B], [C, D], [E, F]]
One liner solution is
arr.map((c, i) => [c, arr[i + 1]]).slice(0, -1)
SOLUTION 1
You can use map and filter here to achieve the result
At current index return an array which will contain current element and next element till last element
const arr = ['A', 'B', 'C', 'D', 'E', 'F'];
const result = arr
.map((c, i) => (i < arr.length - 1 ? [c, arr[i + 1]] : null))
.filter(Boolean);
console.log(result);
SOLUTION 2
You can also acheve this if you get all array combination and remove last one as:
const arr = ['A', 'B', 'C', 'D', 'E', 'F'];
const result = arr.map((c, i) => [c, arr[i + 1]]).slice(0, -1);
console.log(result);
Just with reduce method, you can add the current item and the item after it in an array, and then push this array into the accumulator of the reducer method, and before push you need to check if the current item isn't last item in the array.
const arr = ['A', 'B', 'C', 'D', 'E', 'F']
const result = arr.reduce((acc, item, index) => {
const nextItem = arr[index + 1]
nextItem ?? acc.push([item, nextItem])
return acc
}, [])
console.log(result)
If you don't want to stick to the reduce() approach here's another method using map() and slice().
const data = ['A', 'B', 'C', 'D', 'E', 'F'];
const result = data.map((_, i) => (i < data.length - 1 ? data.slice(i, i + 2) : null)).filter(Boolean);
console.log(result);
The simplest will be a standard for loop. It happens to also be shorter than many of the reduce() answers with no extraneous filters or conditions.
const arr = ['A', 'B', 'C', 'D', 'E', 'F'];
const result = [];
for (let i = 0; i < arr.length - 1; i++) {
result.push([arr[i], arr[i + 1]]);
}
console.log(result);
Alternatively a while loop is actually shorter if a little less transparent.
const arr = ['A', 'B', 'C', 'D', 'E', 'F'];
let result = [], i = 0;
while (i < arr.length - 1) {
result.push([arr[i], arr[++i]]);
}
console.log(result);

Merge 1d and 2d array to create a new 1d array in javascript

I have 2 arrays as such:
let x = ['1min', '2min', '3min', '4min']
let y = [
['a', 'b', 'c', 'd'],
['e', 'f', 'g'],
['h', 'i', 'j'],
['k', 'l', 'm']
]
let z = x.map(function(e, i) {
return [e, y[i]];
});
console.log(z)
the output i'm trying to get is this:
z = [[a,'1min'], [b,'1min'], [c,'1min'], [e,'2min'], [f,'2min'], [g,'2min'], [h,'3min'], [i,'3min'], [j,'3min'], ...]
var x = ['1min', '2min', '3min', '4min']
var y = [['a','b','c','d'],['e','f','g'],['h','i','j'],['k','l','m']]
var z = x.flatMap((e, i)=> y[i].map(f => [f, e] ));
console.log(z)

Reorder the objects in the array

I have arrays as following
var A = ['C', 'D', 'E', 'F', 'G'];
var B = [3, 0, 4, 1, 2];
I need to rearrange array A with the given index values in array B. My solution to the problem is following
function reArrange(A,B){
var num;
var letter;
for(var i = 0; i < A.length; i++){
num = B[i];
letter = A[i];
A[num] = letter;
}
return A;
}
reArrange(A, B);
I get an output of ['D', 'C', 'E', 'C', 'E'] when it should be ['D', 'F', 'G', 'C', 'E']
Hope this will help.
var A = ['C', 'D', 'E', 'F', 'G'];
var B = [3, 0, 4, 1, 2];
var C = []
function reArrange(A,B){
var num;
var letter;
for(var i = 0; i < A.length; i++){
num = B[i];
letter = A[i];
C[num] = letter;
}
return C;
}
reArrange(A, B);
console.log(C)
You could use the second array for the indices for assinging the actual value of the given array.
var array = ['C', 'D', 'E', 'F', 'G'],
order = [3, 0, 4, 1, 2],
result = order.reduce((r, a, i) => (r[a] = array[i], r), []);
console.log(result); // ['D', 'F', 'G', 'C', 'E']
Simple and short
var A = ['C', 'D', 'E', 'F', 'G'];
var B = [3, 0, 4, 1, 2];
function reorderAB(first, second){
var result= new Array();
for(var i=0;i<second.length;i++){
result[second[i]]=first[i];
}
console.log(result);
}
reorderAB(A,B);
The problem is that you replaced array A with new value.
Example with the first i in your loop.
i = 0, num = 3 and letter = C
The original A[3] = F
Then you assign A[num] = letter, mean that A[3] (F) has new value/replaced C and so on.
You can create a Map from B -> A and then order the keys accordingly to output your expected array, however I am not sure what use case you are using it for.
const A = ['C', 'D', 'E', 'F', 'G'];
const B = [3, 0, 4, 1, 2];
let bToA = new Map();
B.forEach((a, i) => bToA.set(a, A[i]))
const final = [...bToA.keys()].sort().map(x => bToA.get(x))
console.log(final);

Finding every second element in a repeating pattern

Data with repeated 'i's followed by 'i's and/or 't's.
data = ['i','t','t','i','i','t','t','t']
Trying to retrieve the index of the last 't' in the pattern ['i','t','t']:
[2,6] // ['i','t','t','i','i','t','t','t'] # position of the returned 't's
// _______ ^ _______ ^
I'm looking for a non-recursive solution using (pure) functions only, using ramdajs for example.
Tried to use reduce and transduce, but unsuccessful sofar.
One approach would be to use R.aperture to iterate over a 3-element sliding window of the data list, then tracking the position of any sub-list that equals the pattern ['i', 't', 't'].
const data = ['i','t','t','i','i','t','t','t']
const isPattern = R.equals(['i', 't', 't'])
const reduceWithIdx = R.addIndex(R.reduce)
const positions = reduceWithIdx((idxs, next, idx) =>
isPattern(next) ? R.append(idx + 2, idxs) : idxs
, [], R.aperture(3, data))
console.log(positions)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.24.1/ramda.min.js"></script>
A point-free version of this approach could look something like the following, though whether this is preferable comes down to a preference of style/readability.
const data = ['i','t','t','i','i','t','t','t']
const isPattern = R.equals(['i', 't', 't'])
const run = R.pipe(
// create sliding window of 3 elements
R.aperture(3),
// zip sliding window with index
R.chain(R.zip, R.compose(R.range(0), R.length)),
// filter matching pattern
R.filter(R.compose(isPattern, R.nth(1))),
// extract index
R.map(R.compose(R.add(2), R.head))
)
console.log(run(data))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.24.1/ramda.min.js"></script>
You could use a nested approach with a temporary array for checking the same pattern for different starting points. This proposal works with an arbitrary length of pattern and returns the index of the predefined pattern.
This solution features obviously plain Javascript.
index i t i t t i i t t t temp result comment
----- ------------------------------ ------ -------- ------------
0 <i> [0] [] match
1 i <t> [0] [] match
<-> [0] [] no match
2 i t <-> [] [] no match
<i> [2] [] match
3 i <t> [2] [] match
<-> [2] [] no match
4 i t <t> [] [4] pattern found
<-> [] [4] no match
5 <i> [5] [4] match
6 i <-> [] [4] no match
<i> [6] [4] match
7 i <t> [6] [4] match
<-> [6] [4] no match
8 i t <t> [] [4, 8] pattern found
<-> [] [4, 8] no match
9 <-> [] [4, 8] no match
<t> matches 't' at position
<-> does not match at position
function getPatternPos(array, pattern) {
var result = [];
array.reduce(function (r, a, i) {
return r.concat(i).filter(function (j) {
if (i - j === pattern.length - 1 && a === pattern[i - j]) {
result.push(i);
return false;
}
return a === pattern[i - j];
});
}, []);
return result;
}
console.log(getPatternPos(['i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
// [2, 6]
console.log(getPatternPos(['i','t','i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
// [4, 8]
console.log(getPatternPos(['a', 'b', 'a', 'b', 'b', 'a', 'b', 'c', 'd'], ['a', 'b', 'c']));
// [7]
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do it using Array.prototype.reduce() with a simple condition.
data = ['i','t','t','i','i','t','t','t']
var newData = data.reduce(function (acc, item, index) {
// Check if current element is `t` and the item before it is `i`, `t`
if (item === 't' && data[index - 1] === 'i' && data[index - 2] === 't') {
acc.push(item)
}
return acc;
}, []);
console.log(newData); // ['t', 't']
You can do simply by for loop and check last values of array:
var data = ['i','t','t','i','i','t','t','t'];
var positions = new Array();
for(var i=2; i< data.length; i++){
if(data[i-2] === 'i' && data[i-1] === 't' && data[i] === 't') {
positions.push(i)
}
}
console.log(positions)
data.filter((c, i, d) => c === 't' && d[i - 1] === 't' && d[i - 2] === 'I')
**No negative indexes: **
const matchMaker = () => {
let memo = [‘a’, ‘b’];
return (c, i, d) => {
memo.unshift(c);
return memo[1] + memo[2] + c === 'itt';
}
};
data.filter(matchMaker());
function getPattern(arr, p) {
var r = [],
dir = [];
for (let [i, v] of arr.entries()) {
dir = dir.concat(i).filter(function(x) {
if (v === p[i - x] && i - x === p.length - 1) {
r.push(i);
return false;
}
return v === p[i - x];
})
};
return r;
}
console.log(getPattern(['i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
console.log(getPattern(['i', 't', 'i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
console.log(getPattern(['a', 'b', 'a', 'b', 'b', 'a', 'b', 'c', 'd'], ['a', 'b', 'c']));
.as-console-wrapper { max-height: 100% !important; top: 0; }
In case anyone wants to see an example without using a library that does not use look ahead or behinds ("i + 1" or "i - 2'", etc.).
I think it works similarly to what the Ramda approach does, but I chose to combine the partitioning and equality check in the same loop:
For every step in reduce
Take a section of the array matching the pattern length
Check if it is equal to the pattern
If it is, add the index of the last element in the section to the result of reduce
The code, in which pattern and data are both arrays of strings:
const findPattern = (pattern, data) => data.reduce(
(results, _, i, all) =>
// Check if a slice from this index equals the pattern
arrEqual(all.slice(i, i + pattern.length), pattern)
// Add the last index of the pattern to our results
? (results.push(i + pattern.length - 1), results)
// or, return what we had
: results,
[]);
// Utility method to check array equality
const arrEqual = (arr1, arr2) =>
arr1.length === arr2.length &&
arr1.every((x, i) => x === arr2[i]);
I tested on several data sets and think it meets all requirements:
const findPattern = (pattern, data) => data.reduce(
(results, _, i, all) =>
arrEqual(all.slice(i, i + pattern.length), pattern)
? push(results, i + pattern.length - 1)
: results,
[]);
const arrEqual = (arr1, arr2) =>
arr1.length === arr2.length &&
arr1.every((x, i) => x === arr2[i]);
const push = (xs, x) => (xs.push(x), xs);
// For just string patterns we could also do:
// const arrEqual = (arr1, arr2) => arr1.join("") === arr2.join("");
// Test cases
const dataSets = [
// (i) marks a matching index
// [i] marks a last matching index that should be returned
// | marks a new start
{ pattern: ["i","t","t"], input: ['i','t','t','i','i','t','t','t'], output: [2, 6] },
// |(0) (1) [2]| 3 -(4) (5) [6]| 7
{ pattern: ["i","t"], input: ['i','t','i','t','t','i','i','t','t','t'], output: [1, 3, 7] },
// |(0) [1]|(2) [3]| 4 | 5 |(6) [7]| 8 | 9
{ pattern: ["i","t","t"], input: ['i','t','i','t','t','i','i','t','t','t'], output: [4, 8] },
// |(0) (1)|(2) (3) [4]| 5 |(6) (7) [8]| 9
{ pattern: ["i","t","i"], input: ['i','t','i','t','i','t','i','t','i','t'], output: [2, 4, 6, 8] }
// |(0) (1) [2]| |(6) (7) [8]| 9
// |(2) (3) [4]
// |(4) (5) [6]
];
dataSets.forEach(({pattern, input, output}) =>
console.log(
"| input:", input.join(" "),
"| control:", output.join(", "),
"| answer:", findPattern(pattern, input).join(", ")
)
)
Two years later, lost traveler stumbles upon this question and notices that for variable sized (and especially large pattern with even larger input array or multiple input arrays), classical KMP algorithm would be great.
I think it is worth studing this algorithm.
We will start with simple imperative implementation. Then switch to (at least for me) more intuitive (but probably slightly less optimal, and definitely less optimal when it comes to memory) version with finite automaton. At the end, we'll see something that looks like functional but it is not 100% pure. I wasn't in a mood to torture my self with pure functional implementation of KMP in JS :).
Prefix function KMP, imperative implementation:
function getPatternPos(array, pattern) {
const result = [];
// trying to explain this is a waste of time :)
function createPrefix(pattern) {
// initialize array with zeros
const prefix = Array.apply(null, Array(pattern.length)).map(Number.prototype.valueOf, 0);
let s = 0;
prefix[0] = 0;
for (let i = 1; i < pattern.length; ++i) {
while (s > 0 && pattern[s] !== pattern[i]) {
s = prefix[s - 1];
}
if (pattern[i] === pattern[s]) {
++s;
}
prefix[i] = s;
}
return prefix;
}
const prefix = createPrefix(pattern);
let s = 0;
for (let i = 0; i < array.length; ++i) {
while (s > 0 && pattern[s] !== array[i]) {
s = prefix[s - 1];
}
if (array[i] === pattern[s]) {
++s;
}
if (s === pattern.length) {
result.push(i);
s = 0;
}
}
return result;
}
console.log(getPatternPos(['i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
// [2, 6]
console.log(getPatternPos(['i','t','i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
// [4, 8]
console.log(getPatternPos(['a', 'b', 'a', 'b', 'b', 'a', 'b', 'c', 'd'], ['a', 'b', 'c']));
// [7]
console.log(getPatternPos("ababxabababcxxababc".split(""), "ababc".split("")));
// [11, 18]
console.log(getPatternPos("abababcx".split(""), "ababc".split("")));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Finate automaton KMP implementation:
function getPatternPos(array, pattern) {
const result = [];
function patternCode(i) {
return pattern[i].charCodeAt(0);
}
function createStateMachine(pattern) {
// return single dimensional array as matrix instead of array of arrays,
// for better perfomanse (locality - cache optimizations) and memory usage.
// zero initialize matrix
const sm = Array.apply(null, Array(256 * pattern.length)).map(Number.prototype.valueOf, 0);
let s = 0;
sm[patternCode(0) * pattern.length + 0] = 1;
for (let i = 1; i < pattern.length; ++i) {
// go to same states as if we would go after backing up, so copy all
for (let code = 0; code < 256; ++code)
sm[code * pattern.length + i] = sm[code * pattern.length + s];
// only in case of current symbol go to different/next state
sm[patternCode(i) * pattern.length + i] = i + 1;
// update the state that fallows backup path
s = sm[patternCode(i) * pattern.length + s];
}
return sm;
}
const sm = createStateMachine(pattern);
numStates = pattern.length;
let s = 0;
// now simply fallow state machine
for (let i = 0; i < array.length; ++i) {
s = sm[array[i].charCodeAt(0) * numStates + s];
if (s === pattern.length) {
result.push(i);
s = 0;
}
}
return result;
}
console.log(getPatternPos(['i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
// [2, 6]
console.log(getPatternPos(['i','t','i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
// [4, 8]
console.log(getPatternPos(['a', 'b', 'a', 'b', 'b', 'a', 'b', 'c', 'd'], ['a', 'b', 'c']));
// [7]
console.log(getPatternPos("ababxabababcxxababc".split(""), "ababc".split("")));
// [11, 18]
console.log(getPatternPos("abababcx".split(""), "ababc".split("")));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Funcational-ish KMP implementation:
function getPatternPos(array, pattern) {
// pure function that creates state machine,
// but it's implementation is not complitely pure internally.
function createStateMachine(pattern) {
const initState = Object.create(null);
initState[pattern[0]] = Object.create(initState);
const {currState: finalState} = pattern.slice(1).reduce(function(acc, cval, cidx) {
const newFallbackState = acc.fallbackState[cval] || initState;
// WARNING: non-functional/immutable part,
// to make it complitely pure we would probably need to
// complicate our lives with better data structures or
// lazy evalutaion.
acc.currState[cval] = Object.create(newFallbackState);
return {currState: acc.currState[cval], fallbackState: newFallbackState};
}, {currState: initState[pattern[0]], fallbackState: initState});
return {initState: initState, finalState: finalState};
}
const {initState, finalState} = createStateMachine(pattern);
return array.reduce(function (acc, cval, cidx, array) {
const newState = acc.currState[cval];
if (typeof newState === 'undefined') {
return {currState: initState, result: acc.result};
}
if (newState === finalState) {
// WARNING: not purly functional/immutable,
// still implemenations of JS pure functional/immutable libraries
// probaly use mutation under the hood, and just make it look pure,
// this is what happens here also :)
acc.result.push(cidx);
return {currState: initState, result: acc.result};
}
return {currState: newState, result: acc.result};
}, {currState: initState, result: []}).result;
}
console.log(getPatternPos(['i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
// [2, 6]
console.log(getPatternPos(['i','t','i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
// [4, 8]
console.log(getPatternPos(['a', 'b', 'a', 'b', 'b', 'a', 'b', 'c', 'd'], ['a', 'b', 'c']));
// [7]
console.log(getPatternPos("ababxabababcxxababc".split(""), "ababc".split("")));
// [11, 18]
console.log(getPatternPos("abababcx".split(""), "ababc".split("")));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Finding All Combinations (Cartesian product) of JavaScript array values

How can I produce all of the combinations of the values in N number of JavaScript arrays of variable lengths?
Let's say I have N number of JavaScript arrays, e.g.
var first = ['a', 'b', 'c', 'd'];
var second = ['e'];
var third = ['f', 'g', 'h', 'i', 'j'];
(Three arrays in this example, but its N number of arrays for the problem.)
And I want to output all the combinations of their values, to produce
aef
aeg
aeh
aei
aej
bef
beg
....
dej
EDIT: Here's the version I got working, using ffriend's accepted answer as the basis.
var allArrays = [['a', 'b'], ['c', 'z'], ['d', 'e', 'f']];
function allPossibleCases(arr) {
if (arr.length === 0) {
return [];
}
else if (arr.length ===1){
return arr[0];
}
else {
var result = [];
var allCasesOfRest = allPossibleCases(arr.slice(1)); // recur with the rest of array
for (var c in allCasesOfRest) {
for (var i = 0; i < arr[0].length; i++) {
result.push(arr[0][i] + allCasesOfRest[c]);
}
}
return result;
}
}
var results = allPossibleCases(allArrays);
//outputs ["acd", "bcd", "azd", "bzd", "ace", "bce", "aze", "bze", "acf", "bcf", "azf", "bzf"]
This is not permutations, see permutations definitions from Wikipedia.
But you can achieve this with recursion:
var allArrays = [
['a', 'b'],
['c'],
['d', 'e', 'f']
]
function allPossibleCases(arr) {
if (arr.length == 1) {
return arr[0];
} else {
var result = [];
var allCasesOfRest = allPossibleCases(arr.slice(1)); // recur with the rest of array
for (var i = 0; i < allCasesOfRest.length; i++) {
for (var j = 0; j < arr[0].length; j++) {
result.push(arr[0][j] + allCasesOfRest[i]);
}
}
return result;
}
}
console.log(allPossibleCases(allArrays))
You can also make it with loops, but it will be a bit tricky and will require implementing your own analogue of stack.
I suggest a simple recursive generator function as follows:
// Generate cartesian product of given iterables:
function* cartesian(head, ...tail) {
let remainder = tail.length ? cartesian(...tail) : [[]];
for (let r of remainder) for (let h of head) yield [h, ...r];
}
// Example:
const first = ['a', 'b', 'c', 'd'];
const second = ['e'];
const third = ['f', 'g', 'h', 'i', 'j'];
console.log(...cartesian(first, second, third));
You don't need recursion, or heavily nested loops, or even to generate/store the whole array of permutations in memory.
Since the number of permutations is the product of the lengths of each of the arrays (call this numPerms), you can create a function getPermutation(n) that returns a unique permutation between index 0 and numPerms - 1 by calculating the indices it needs to retrieve its characters from, based on n.
How is this done? If you think of creating permutations on arrays each containing: [0, 1, 2, ... 9] it's very simple... the 245th permutation (n=245) is "245", rather intuitively, or:
arrayHundreds[Math.floor(n / 100) % 10]
+ arrayTens[Math.floor(n / 10) % 10]
+ arrayOnes[Math.floor(n / 1) % 10]
The complication in your problem is that array sizes differ. We can work around this by replacing the n/100, n/10, etc... with other divisors. We can easily pre-calculate an array of divisors for this purpose. In the above example, the divisor of 100 was equal to arrayTens.length * arrayOnes.length. Therefore we can calculate the divisor for a given array to be the product of the lengths of the remaining arrays. The very last array always has a divisor of 1. Also, instead of modding by 10, we mod by the length of the current array.
Example code is below:
var allArrays = [first, second, third, ...];
// Pre-calculate divisors
var divisors = [];
for (var i = allArrays.length - 1; i >= 0; i--) {
divisors[i] = divisors[i + 1] ? divisors[i + 1] * allArrays[i + 1].length : 1;
}
function getPermutation(n) {
var result = "", curArray;
for (var i = 0; i < allArrays.length; i++) {
curArray = allArrays[i];
result += curArray[Math.floor(n / divisors[i]) % curArray.length];
}
return result;
}
Provided answers looks too difficult for me. So my solution is:
var allArrays = new Array(['a', 'b'], ['c', 'z'], ['d', 'e', 'f']);
function getPermutation(array, prefix) {
prefix = prefix || '';
if (!array.length) {
return prefix;
}
var result = array[0].reduce(function(result, value) {
return result.concat(getPermutation(array.slice(1), prefix + value));
}, []);
return result;
}
console.log(getPermutation(allArrays));
You could take a single line approach by generating a cartesian product.
result = items.reduce(
(a, b) => a.reduce(
(r, v) => r.concat(b.map(w => [].concat(v, w))),
[]
)
);
var items = [['a', 'b', 'c', 'd'], ['e'], ['f', 'g', 'h', 'i', 'j']],
result = items.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; }
Copy of le_m's Answer to take Array of Arrays directly:
function *combinations(arrOfArr) {
let [head, ...tail] = arrOfArr
let remainder = tail.length ? combinations(tail) : [[]];
for (let r of remainder) for (let h of head) yield [h, ...r];
}
Hope it saves someone's time.
You can use a typical backtracking:
function cartesianProductConcatenate(arr) {
var data = new Array(arr.length);
return (function* recursive(pos) {
if(pos === arr.length) yield data.join('');
else for(var i=0; i<arr[pos].length; ++i) {
data[pos] = arr[pos][i];
yield* recursive(pos+1);
}
})(0);
}
I used generator functions to avoid allocating all the results simultaneously, but if you want you can
[...cartesianProductConcatenate([['a', 'b'], ['c', 'z'], ['d', 'e', 'f']])];
// ["acd","ace","acf","azd","aze","azf","bcd","bce","bcf","bzd","bze","bzf"]
Easiest way to find the Combinations
const arr1= [ 'a', 'b', 'c', 'd' ];
const arr2= [ '1', '2', '3' ];
const arr3= [ 'x', 'y', ];
const all = [arr1, arr2, arr3];
const output = all.reduce((acc, cu) => {
let ret = [];
acc.map(obj => {
cu.map(obj_1 => {
ret.push(obj + '-' + obj_1)
});
});
return ret;
})
console.log(output);
If you're looking for a flow-compatible function that can handle two dimensional arrays with any item type, you can use the function below.
const getUniqueCombinations = <T>(items : Array<Array<T>>, prepend : Array<T> = []) : Array<Array<T>> => {
if(!items || items.length === 0) return [prepend];
let out = [];
for(let i = 0; i < items[0].length; i++){
out = [...out, ...getUniqueCombinations(items.slice(1), [...prepend, items[0][i]])];
}
return out;
}
A visualisation of the operation:
in:
[
[Obj1, Obj2, Obj3],
[Obj4, Obj5],
[Obj6, Obj7]
]
out:
[
[Obj1, Obj4, Obj6 ],
[Obj1, Obj4, Obj7 ],
[Obj1, Obj5, Obj6 ],
[Obj1, Obj5, Obj7 ],
[Obj2, Obj4, Obj6 ],
[Obj2, Obj4, Obj7 ],
[Obj2, Obj5, Obj6 ],
[Obj2, Obj5, Obj7 ],
[Obj3, Obj4, Obj6 ],
[Obj3, Obj4, Obj7 ],
[Obj3, Obj5, Obj6 ],
[Obj3, Obj5, Obj7 ]
]
You could create a 2D array and reduce it. Then use flatMap to create combinations of strings in the accumulator array and the current array being iterated and concatenate them.
const data = [ ['a', 'b', 'c', 'd'], ['e'], ['f', 'g', 'h', 'i', 'j'] ]
const output = data.reduce((acc, cur) => acc.flatMap(c => cur.map(n => c + n)) )
console.log(output)
2021 version of David Tang's great answer
Also inspired with Neil Mountford's answer
const getAllCombinations = (arraysToCombine) => {
const divisors = [];
let permsCount = 1;
for (let i = arraysToCombine.length - 1; i >= 0; i--) {
divisors[i] = divisors[i + 1] ? divisors[i + 1] * arraysToCombine[i + 1].length : 1;
permsCount *= (arraysToCombine[i].length || 1);
}
const getCombination = (n, arrays, divisors) => arrays.reduce((acc, arr, i) => {
acc.push(arr[Math.floor(n / divisors[i]) % arr.length]);
return acc;
}, []);
const combinations = [];
for (let i = 0; i < permsCount; i++) {
combinations.push(getCombination(i, arraysToCombine, divisors));
}
return combinations;
};
console.log(getAllCombinations([['a', 'b'], ['c', 'z'], ['d', 'e', 'f']]));
Benchmarks: https://jsbench.me/gdkmxhm36d/1
Here's a version adapted from the above couple of answers, that produces the results in the order specified in the OP, and returns strings instead of arrays:
function *cartesianProduct(...arrays) {
if (!arrays.length) yield [];
else {
const [tail, ...head] = arrays.reverse();
const beginning = cartesianProduct(...head.reverse());
for (let b of beginning) for (let t of tail) yield b + t;
}
}
const first = ['a', 'b', 'c', 'd'];
const second = ['e'];
const third = ['f', 'g', 'h', 'i', 'j'];
console.log([...cartesianProduct(first, second, third)])
You could use this function too:
const result = (arrayOfArrays) => arrayOfArrays.reduce((t, i) => { let ac = []; for (const ti of t) { for (const ii of i) { ac.push(ti + '/' + ii) } } return ac })
result([['a', 'b', 'c', 'd'], ['e'], ['f', 'g', 'h', 'i', 'j']])
// which will output [ 'a/e/f', 'a/e/g', 'a/e/h','a/e/i','a/e/j','b/e/f','b/e/g','b/e/h','b/e/i','b/e/j','c/e/f','c/e/g','c/e/h','c/e/i','c/e/j','d/e/f','d/e/g','d/e/h','d/e/i','d/e/j']
Of course you can remove the + '/' in ac.push(ti + '/' + ii) to eliminate the slash from the final result. And you can replace those for (... of ...) with forEach functions (plus respective semicolon before return ac), whatever of those you are more comfortable with.
An array approach without recursion:
const combinations = [['1', '2', '3'], ['4', '5', '6'], ['7', '8']];
let outputCombinations = combinations[0]
combinations.slice(1).forEach(row => {
outputCombinations = outputCombinations.reduce((acc, existing) =>
acc.concat(row.map(item => existing + item))
, []);
});
console.log(outputCombinations);
let arr1 = [`a`, `b`, `c`];
let arr2 = [`p`, `q`, `r`];
let arr3 = [`x`, `y`, `z`];
let result = [];
arr1.forEach(e1 => {
arr2.forEach(e2 => {
arr3.forEach(e3 => {
result[result.length] = e1 + e2 + e3;
});
});
});
console.log(result);
/*
output:
[
'apx', 'apy', 'apz', 'aqx',
'aqy', 'aqz', 'arx', 'ary',
'arz', 'bpx', 'bpy', 'bpz',
'bqx', 'bqy', 'bqz', 'brx',
'bry', 'brz', 'cpx', 'cpy',
'cpz', 'cqx', 'cqy', 'cqz',
'crx', 'cry', 'crz'
]
*/
A solution without recursion, which also includes a function to retrieve a single combination by its id:
function getCombination(data, i) {
return data.map(group => {
let choice = group[i % group.length]
i = (i / group.length) | 0;
return choice;
});
}
function* combinations(data) {
let count = data.reduce((sum, {length}) => sum * length, 1);
for (let i = 0; i < count; i++) {
yield getCombination(data, i);
}
}
let data = [['a', 'b', 'c', 'd'], ['e'], ['f', 'g', 'h', 'i', 'j']];
for (let combination of combinations(data)) {
console.log(...combination);
}

Categories