Related
I have an array with three values.
["a","b","c"]
I'm trying to create the following combination with the above array.
0: ["a", "a", "a"]
1: ["a", "a", "b"]
2: ["a", "a", "c"]
3: ["a", "b", "b"]
4: ["a", "b", "c"]
5: ["a", "c", "c"]
6: ["b", "b", "b"]
7: ["b", "b", "c"]
8: ["b", "c", "c"]
9: ["c", "c", "c"]
I wrote the code that was successful. But the code is not optimized. How can I make this code simple.
function test(arr, arr2=[], result=[]) {
if (arr2.length < 3) {
let proxy_arr = [...arr];
Object.keys(proxy_arr).forEach(index=>{
if (!test(arr, [...arr2].concat(proxy_arr[index]), result)) {
result.push([...arr2].concat(proxy_arr[index]));
} else {
//debugger;
arr = arr.slice(1);
}
}
);
return result;
}
return false;
}
result = test(["a", "b", "c"]);
You can use a recursive generator function to do most of the work. Array.from a generator will stuff the results in an array.
let vec = ['a', 'b', 'c'];
function* combo(n, k = 0, prefix = []) {
if (n == 0) yield prefix;
else for (let i = k; i < vec.length; ++i) {
yield* combo(n - 1, i, [...prefix, vec[i]]);
}
}
let test = Array.from(combo(3));
console.log(JSON.stringify(test));
Updated Version
Inspired by Wyck's solution, I made another version. This one is much cleaner. It uses much the same technique as Wyck did, but skips the generator code.
const makeBags = (n, xs, prefix = []) =>
n == 0
? [prefix]
: xs .flatMap ((v, i) => makeBags (n - 1, xs .slice (i), [...prefix, v]))
console .log (
JSON .stringify (makeBags (3, ['a', 'b', 'c']))
)
Note that although the additional default parameter looks like it might be for tail-call optimization, this code is not ready for TCO.
My First Solution
Here is a straightforward recursive solution, returning the empty list if the list of letters is empty and otherwise determining how many of the initial letter to include and recurring on the remaining letters. I have no idea if this is more optimal in any sense than the original except in terms of code cleanliness. But it's more generic, accepting an argument to tell how many items are in the output separate from the number of items in the list.
const range = (lo, hi) =>
[...Array (hi + 1 - lo)] .map ((_, i) => i + lo)
const prefixAll = (p, xs) =>
xs .map (x => [...p, ...x])
const groupsOf = (n, [x = undefined, ...xs]) =>
x == undefined
? []
: [
Array (n) .fill (x),
...range (1, n) .flatMap (i => prefixAll (Array (n - i) .fill (x), groupsOf (i, xs)))
]
console .log (
groupsOf (3, ['a', 'b', 'c'])
)
range is a simple utility function: range(3, 10) //=> [3, 4, 5, 6, 7, 8, 9, 10]
prefixAll is a helper, which could be inlined if preferred. It simply prefixes each array in the second argument with the values in the first one.
prefixAll(['a', 'b'], [['c'], ['c', 'c'], ['c', 'd']])
//=> [['a', 'b', 'c'], ['a', 'b', 'c', 'c'], ['a', 'b', 'c', 'd']]
While this isn't overly complex, there is almost certainly a better solution which does not involve Array (n) .fill (x), doing the recursive step as one simple flatMap. But I don't have time now to figure that out.
you are taking about the permutation algorithm.
here is solution without repitation
function permutateWithoutRep(permutationOptions) {
if (permutationOptions.length === 1) {
return [permutationOptions];
}
// Init permutations array.
const permutations = [];
// Get all permutations for permutationOptions excluding the first element.
const smallerPermutations = permutateWithoutRep(permutationOptions.slice(1));
// Insert first option into every possible position of every smaller permutation.
const firstOption = permutationOptions[0];
for (let permIndex = 0; permIndex < smallerPermutations.length; permIndex += 1) {
const smallerPermutation = smallerPermutations[permIndex];
// Insert first option into every possible position of smallerPermutation.
for (let positionIndex = 0; positionIndex <= smallerPermutation.length; positionIndex += 1) {
const permutationPrefix = smallerPermutation.slice(0, positionIndex);
const permutationSuffix = smallerPermutation.slice(positionIndex);
permutations.push(permutationPrefix.concat([firstOption], permutationSuffix));
}
}
return permutations;
}
console.log(permutateWithoutRep(['a', 'b', 'c']))
With Repitation:
function permutateWithRep(
permutationOptions,
permutationLength = permutationOptions.length,
) {
if (permutationLength === 1) {
return permutationOptions.map(permutationOption => [permutationOption]);
}
// Init permutations array.
const permutations = [];
// Get smaller permutations.
const smallerPermutations = permutateWithRep(
permutationOptions,
permutationLength - 1,
);
// Go through all options and join it to the smaller permutations.
permutationOptions.forEach((currentOption) => {
smallerPermutations.forEach((smallerPermutation) => {
permutations.push([currentOption].concat(smallerPermutation));
});
});
return permutations;
}
console.log(permutateWithRep(['a', 'b', 'c']))
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);
I need to merge two arrays into a single array. I have code but it is not working as expected-- it is merging them one after another, but I need to interlock the values.
<html>
<head>
<title></title>
<script src="https://code.jquery.com/jquery-3.1.1.js"></script>
</head>
<body>
<div id="result"></div>
<script type="text/javascript">
var array1 = [1, 2, 3, 4];
var array2 = ["a", "b", "c", "d"];
var newArray = $.merge(array1, array2);
$("#result").html(newArray);
//the result its 1234absd
//ineed result like 1a,2b,3c,4d
</script>
</body>
</html>
You can use Array#map in plain javascript.
var array1 = [1, 2, 3, 4];
var array2 = ["a", "b", "c", "d"];
var newArray = array1.map((e, i) => e + array2[i]);
console.log(newArray);
If you use map on array1 then first parameter is current value of array1 in loop and the second parameter is index of that element in array. So you can match current element in array1 with elements from other arrays with the same index using index.
var array1 = [1, 2, 3, 4];
var array2 = ["a", "b", "c", "d"];
var array3 = ["f", "d", "s", "a"];
var newArray = array1.map(function(value, index) {
return value + array2[index] + array3[index];
});
console.log(newArray);
You could use Array#reduce and Array#map for an arbitrary count of arrays with same length.
var a1 = [1, 2, 3, 4],
a2 = ["a", "b", "c", "d"],
a3 = [9, 8, 7, 6],
a4 = ["z", "y", "x", "w"],
result = [a1, a2, a3, a4].reduce((a, b) => a.map((v, i) => v + b[i]));
console.log(result);
ES5
var a1 = [1, 2, 3, 4],
a2 = ["a", "b", "c", "d"],
a3 = [9, 8, 7, 6],
a4 = ["z", "y", "x", "w"],
result = [a1, a2, a3, a4].reduce(function (a, b) {
return a.map(function (v, i) {
return v + b[i];
});
});
console.log(result);
Just a little variation note, to merge by index in an array multiple, to create an associative array of values.
const a = [1, 2, 3, 4],
b = [34, 54, 54, 43]
console.log(
a.map((e,i) => [e,b[i]])
)
Then later, to search for the closest match from a given value.
On this example, searching for the best associated value for 3.7:
const a = [1, 2, 3, 4],
b = [34, 54, 54, 43],
c = a.map((e,i) => [e,b[i]]),
search = 3.7,
temp = [];
c.forEach(function(e){
temp.push(e[0])
})
const seek = temp.reduce((m, n) => (Math.abs(n-search) < Math.abs(m-search) ? n : m))
const index = c.findIndex((s) => s.indexOf(seek) !== -1)
console.log(c[index][1])
This is not exactly how behave an object.
For example, identical index entries aren't merged but are holding their associated values in a new entry (duplicate index), we can roll Math from the index, and create transformation matrix. We can use any column as index and search both way.
You can use a combination of reduce and concat (source):
var array1 = [1, 2, 3, 4];
var array2 = ["a", "b", "c", "d"];
var newArray = array1.reduce(function(prev, curr) {
return prev.concat(curr, array2[prev.length / 2]);
}, []);
$("#result").html(newArray);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="result"></div>
Well actually JavaScript lacks a .zip() functor which would be very handy for exactly what you are trying to do. Lets invent it;
Array.prototype.zip = function(a){
return this.map((e,i) => typeof e === "object" || typeof a[i] === "object" ? [e,a[i]] : e+a[i]);
};
var arrays = [[1, 2, 3, 4],
["a", "b", "c", "d"],
[9, 8, 7, 6],
["z", "y", "x", "w"],
[[1,2,3],[4,5,6],[7,8,9],[10,11,12]]
],
result = arrays.reduce((p,c) => p.zip(c));
console.log(result);
In case someone needs to merge any number of arrays (with fixed length) in the way mentioned above in the post, following function will be useful
console.clear();
var array1 = ["a1", "b1", "c1", "d1"];
var array2 = ["a2", "b2", "c2", "d2"];
var array3 = ["a3", "b3", "c3", "d3"];
var array4 = ["a4", "b4", "c4", "d4"];
function mergeElementsAtIndex(one_or_more_arrays){
const total_arr = arguments.length;
newArray = arguments[0].map((v, k) => {
var temp_val = v;
for(var i=1; i < total_arr; i++){
temp_val += arguments[i][k];
}
return temp_val;
});
return newArray;
}
merged_array = mergeElementsAtIndex(array1, array2, array3, array4);
console.log(merged_array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var array1 = [1, 2, 3, 4];
var array2 = ["a", "b", "c", "d"];
var array3 = ["f", "d", "s", "a"];
var newArray = array1.map(function(value, index) {
return value + array2[index] + array3[index];
});
console.log(newArray);
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!
I have an array of e-mails (it can be just 1 email, or 100 emails), and I need to send the array with an ajax request (that I know how to do), but I can only send an array that has 10 or less e-mails in it. So if there is an original array of 20 e-mails I will need to split them up into 2 arrays of 10 each. or if there are 15 e-mails in the original array, then 1 array of 10, and another array of 5. I'm using jQuery, what would be the best way to do this?
Don't use jquery...use plain javascript
var a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
var b = a.splice(0,10);
//a is now [11,12,13,14,15];
//b is now [1,2,3,4,5,6,7,8,9,10];
You could loop this to get the behavior you want.
var a = YOUR_ARRAY;
while(a.length) {
console.log(a.splice(0,10));
}
This would give you 10 elements at a time...if you have say 15 elements, you would get 1-10, the 11-15 as you wanted.
var size = 10; var arrayOfArrays = [];
for (var i=0; i<bigarray.length; i+=size) {
arrayOfArrays.push(bigarray.slice(i,i+size));
}
console.log(arrayOfArrays);
Unlike splice(), slice() is non-destructive to the original array.
Just loop over the array, splicing it until it's all consumed.
var a = ['a','b','c','d','e','f','g']
, chunk
while (a.length > 0) {
chunk = a.splice(0,3)
console.log(chunk)
}
output
[ 'a', 'b', 'c' ]
[ 'd', 'e', 'f' ]
[ 'g' ]
You can use lodash:
https://lodash.com/docs
_.chunk(['a', 'b', 'c', 'd'], 2);
// → [['a', 'b'], ['c', 'd']]
Array.reduce could be inefficient for large arrays, especially with the mod operator. I think a cleaner (and possibly easier to read) functional solution would be this:
const chunkArray = (arr, size) =>
arr.length > size
? [arr.slice(0, size), ...chunkArray(arr.slice(size), size)]
: [arr];
Assuming you don't want to destroy the original array, you can use code like this to break up the long array into smaller arrays which you can then iterate over:
var longArray = []; // assume this has 100 or more email addresses in it
var shortArrays = [], i, len;
for (i = 0, len = longArray.length; i < len; i += 10) {
shortArrays.push(longArray.slice(i, i + 10));
}
// now you can iterate over shortArrays which is an
// array of arrays where each array has 10 or fewer
// of the original email addresses in it
for (i = 0, len = shortArrays.length; i < len; i++) {
// shortArrays[i] is an array of email addresss of 10 or less
}
Another implementation:
const arr = ["H", "o", "w", " ", "t", "o", " ", "s", "p", "l", "i", "t", " ", "a", " ", "l", "o", "n", "g", " ", "a", "r", "r", "a", "y", " ", "i", "n", "t", "o", " ", "s", "m", "a", "l", "l", "e", "r", " ", "a", "r", "r", "a", "y", "s", ",", " ", "w", "i", "t", "h", " ", "J", "a", "v", "a", "S", "c", "r", "i", "p", "t"];
const size = 3;
const res = arr.reduce((acc, curr, i) => {
if ( !(i % size) ) { // if index is 0 or can be divided by the `size`...
acc.push(arr.slice(i, i + size)); // ..push a chunk of the original array to the accumulator
}
return acc;
}, []);
// => [["H", "o", "w"], [" ", "t", "o"], [" ", "s", "p"], ["l", "i", "t"], [" ", "a", " "], ["l", "o", "n"], ["g", " ", "a"], ["r", "r", "a"], ["y", " ", "i"], ["n", "t", "o"], [" ", "s", "m"], ["a", "l", "l"], ["e", "r", " "], ["a", "r", "r"], ["a", "y", "s"], [",", " ", "w"], ["i", "t", "h"], [" ", "J", "a"], ["v", "a", "S"], ["c", "r", "i"], ["p", "t"]]
NB - This does not modify the original array.
Or, if you prefer a functional, 100% immutable (although there's really nothing bad in mutating in place like done above) and self-contained method:
function splitBy(size, list) {
return list.reduce((acc, curr, i, self) => {
if ( !(i % size) ) {
return [
...acc,
self.slice(i, i + size),
];
}
return acc;
}, []);
}
As a supplement to #jyore's answer, and in case you still want to keep the original array:
var originalArray = [1,2,3,4,5,6,7,8];
var splitArray = function (arr, size) {
var arr2 = arr.slice(0),
arrays = [];
while (arr2.length > 0) {
arrays.push(arr2.splice(0, size));
}
return arrays;
}
splitArray(originalArray, 2);
// originalArray is still = [1,2,3,4,5,6,7,8];
I would like to share my solution as well. It's a little bit more verbose but works as well.
var data = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
var chunksize = 4;
var chunks = [];
data.forEach((item)=>{
if(!chunks.length || chunks[chunks.length-1].length == chunksize)
chunks.push([]);
chunks[chunks.length-1].push(item);
});
console.log(chunks);
Output (formatted):
[ [ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15 ] ]
You can start with an empty array and push inside it sections with your desired range from the original array at the same time you are subtracting from your original array until is empty.
const originalArr = [1,2,3,4,5,6,7,8,9,10,11];
const splittedArray = [];
while (originalArr.length > 0) {
splittedArray.push(originalArr.splice(0,range));
}
output for range 3
splittedArray === [[1,2,3][4,5,6][7,8,9][10,11]]
output for range 4
splittedArray === [[1,2,3,4][5,6,7,8][9,10,11]]
This is also good for a fronted pagination if want.
Another method:
var longArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var size = 2;
var newArray = new Array(Math.ceil(longArray.length / size)).fill("")
.map(function() { return this.splice(0, size) }, longArray.slice());
// newArray = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]];
This doesn't affect the original array as a copy, made using slice, is passed into the 'this' argument of map.
Another implementation, using Array.reduce (I think it’s the only one missing!):
const splitArray = (arr, size) =>
{
if (size === 0) {
return [];
}
return arr.reduce((split, element, index) => {
index % size === 0 ? split.push([element]) : split[Math.floor(index / size)].push(element);
return split;
}, []);
};
As many solutions above, this one’s non-destructive. Returning an empty array when the size is 0 is just a convention. If the if block is omitted you get an error, which might be what you want.
More compact:
const chunk = (xs, size) =>
xs.map((_, i) =>
(i % size === 0 ? xs.slice(i, i + size) : null)).filter(Boolean);
// Usage:
const sampleArray = new Array(33).fill(undefined).map((_, i) => i);
console.log(chunk(sampleArray, 5));
function chunkArrayInGroups(arr, size) {
var newArr=[];
for (var i=0; i < arr.length; i+= size){
newArr.push(arr.slice(i,i+size));
}
return newArr;
}
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6], 3);
Using ES6 Generators
Late to the party, but ES6 generators opened up another neat way to achieve what is asked for.
/**
* Returns chunks of size n.
* #param {Array<any>} array any array
* #param {number} n size of chunk
*/
function* chunks(array, n){
for(let i = 0; i < array.length; i += n) yield array.slice(i, i + n);
}
const result = [...chunks([1, 2, 3, 4, 5, 6, 7, 8 , 9, 10], 3)];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Make it work for infinite Generators
Using the same idea you can create a generator which can also generate an infinite amount of n-sized chunks from values retrieved from another (possibly infinite) generator function. This can be very handy to lazy-generate values once they are required which significantly reduces the required memory or it can even be used to generate a possibly infinite/ unknown number of chunks.
Here an example which uses two generators.
nextNaturalNumber() is an infinite generator which always returns the next natural number. I am using the ES2020 bigint datatype here so there is no restriction (by JavaScript) for the size of the value.
chunksFromIterable() creates n-sized chunks from an possibly infinite iterable.
/**
* Returns chunks of size n for a possibly infinite iterator.
* n must be >= 1
* #param {Iterable<any>} iterable any array
* #param {number} n size of chunk for n >= 1
*/
function* chunksFromIterable(iterable, n){
let arr = [];
let i = n;
for (const value of iterable) {
if(i <= 0) {
// another chunk of size n is filled => return chunk
yield arr;
arr = []; // create new empty array
i = n;
};
arr.push(value);
i--;
}
// in case the iterable is not infinite check if there are still values in the array and return them if necessary
if(arr.length > 0) yield arr;
}
/**
* Infinite iterator which always gets the next natural number.
*/
function* nextNaturalNumber(){
let i = 0n;
while(true) {
i += 1n;
yield i;
}
}
console.log("Finite iterable:");
// this version can now be used using the for ... of loop
for(const threeNaturalNumbers of chunksFromIterable([1, 2, 3, 4, 5, 6, 7, 8 , 9, 10], 3)){
console.log(threeNaturalNumbers);
}
console.log("Infinite iterable:");
// and it can also be used for this infinite generator
for(const threeNaturalNumbers of chunksFromIterable(nextNaturalNumber(), 3)){
printBigIntArray(threeNaturalNumbers);
if(threeNaturalNumbers[0] > 30) break; // end here to avoid an infinite loop
}
// helper function to print array of bigints as this does not seem to be working for snippets
function printBigIntArray(arr){
console.log(`[${arr.join(", ")}]`);
}
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can take a look at this code . Simple and Effective .
function chunkArrayInGroups(array, unit) {
var results = [],
length = Math.ceil(array.length / unit);
for (var i = 0; i < length; i++) {
results.push(array.slice(i * unit, (i + 1) * unit));
}
return results;
}
chunkArrayInGroups(["a", "b", "c", "d"], 2);
Here is a simple one liner
var segment = (arr, n) => arr.reduce((r,e,i) => i%n ? (r[r.length-1].push(e), r)
: (r.push([e]), r), []),
arr = Array.from({length: 31}).map((_,i) => i+1);
console.log(segment(arr,7));
as a function
var arrayChunk = function (array, chunkSize) {
var arrayOfArrays = [];
if (array.length <= chunkSize) {
arrayOfArrays.push(array);
} else {
for (var i=0; i<array.length; i+=chunkSize) {
arrayOfArrays.push(array.slice(i,i+chunkSize));
}
}
return arrayOfArrays;
}
to use
arrayChunk(originalArray, 10) //10 being the chunk size.
using recursion
let myArr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16];
let size = 4; //Math.sqrt(myArr.length); --> For a n x n matrix
let tempArr = [];
function createMatrix(arr, i) {
if (arr.length !== 0) {
if(i % size == 0) {
tempArr.push(arr.splice(0,size))
}
createMatrix(arr, i - 1)
}
}
createMatrix(myArr, myArr.length);
console.log(tempArr);
Note: The existing array i.e. myArr will be modified.
using prototype we can set directly to array class
Array.prototype.chunk = function(n) {
if (!this.length) {
return [];
}
return [this.slice(0, n)].concat(this.slice(n).chunk(n));
};
console.log([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].chunk(5));
let original = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
let size = 5;
let fragments = Array.from(Array(Math.ceil(a.length / size))).map((_,index) => a.slice(index * size,(index + 1) * size))
If you want a method that doesn't modify the existing array, try this:
let oldArray = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
let newArray = [];
let size = 3; // Size of chunks you are after
let j = 0; // This helps us keep track of the child arrays
for (var i = 0; i < oldArray.length; i++) {
if (i % size === 0) {
j++
}
if(!newArray[j]) newArray[j] = [];
newArray[j].push(oldArray[i])
}
function chunkArrayInGroups(arr, size) {
var newArr=[];
for (var i=0; arr.length>size; i++){
newArr.push(arr.splice(0,size));
}
newArr.push(arr.slice(0));
return newArr;
}
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6], 3);
You can use the following code to achieve the required functionality
const splitter = (arr, splitBy, cache = []) => {
const tmp = [...arr]
while (tmp.length) cache.push(tmp.splice(0, splitBy))
return cache
}
const split = splitter([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21], 10)
console.log(split);
Notice, it was an array of length 22 then the splitter function splits it into 2 smaller arrays of 10 items and 1 array of 2 items.
let a = [1, 2, 3, 4, 6, 7, 8, 9, 10, 11];
let _splitCount = 3;
let b = a.reduce((acc, curr, index) => {
if (index % _splitCount === 0) {
acc.push([curr]);
} else {
acc[acc.length - 1].push(curr);
}
return acc;
}, []);
this is the easy solution i think❤️
You can use the below function if you know the number array (numGroups) to be split.
function createGroups(arr, numGroups) {
const perGroup = Math.ceil(arr.length / numGroups);
return new Array(numGroups)
.fill('')
.map((_, i) => arr.slice(i * perGroup, (i + 1) * perGroup));
}
Sample Use:
createGroups([0, 1, 2, 3, 4, 5, 6], 3); //arr = [0, 1, 2, 3, 4, 5, 6] and numGroups = 3