This question already has answers here:
Splitting a JS array into N arrays
(23 answers)
Closed 3 years ago.
I want to split an array in even (or as even as possible) chunks.
The input of the function should be the array and the size of the chunks.
Say you have the array
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]
If you put it in the function below, with 5 as chunk size, it will result in the following
[[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15],[16,17]
However, I want the result to be
[[1,2,3,4,5],[6,7,8,9],[10,11,12,13],[14,15,16,17]
Which means sacrificing the length of the arrays before to make them only differ one in length.
The function I'm currently using is stated below. I've tried various things with modulo but I can't figure it out.
function chunkArray(myArray, chunkSize){
var arrayLength = myArray.length;
var tempArray = [];
for (index = 0; index < arrayLength; index += chunkSize) {
myChunk = myArray.slice(index, index+chunkSize);
// Do something if you want with the group
tempArray.push(myChunk);
}
return tempArray;
}
With this solution you get evenly split array items until the last item which incorporates any left over items (collection of items that's length is less than the chunk size.)
const a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]
const chunk = 4
const chunk_array = (a, c) => {
let arr = []
a.forEach((_, i) => {
if (i%chunk === 0) arr.push(a.slice(i, i+chunk))
})
const [left_overs] = arr.filter(a => a.length < chunk)
arr = arr.filter(a => a.length >= chunk)
arr[arr.length-1] = [...arr[arr.length-1], ...left_overs]
return arr
}
console.log(
chunk_array(a, chunk)
)
My solution!
const chunk = (arr, chunkSize) => {
let chunked = [];
for (let i = 0; i< arr.length; i+=chunkSize){
chunked.push(
arr.slice(i, (i + chunkSize))
);
}
return chunked;
};
const data = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17];
console.log(chunk(data, 5));
// returns [[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15],[16,17]]
You could check with a modulo if your array length is odd :
let MY_CHUNK_CONST = 5
let first_chunk_size = MY_CHUNK_CONST,
other_chunk_size = MY_CHUNK_CONST;
let myArray = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17];
modulo_res = myArray.length % 2; // 0 if even 1 if odd
if(modulo_res){
other_chunk_size = first_chunk_size - 1;
}
var tempArray = [];
myChunk = myArray.slice(0, 0+first_chunk_size);
tempArray.push(myChunk);
for (index = 1; index < myArray.length; index += other_chunk_size) {
myChunk = myArray.slice(index, index+other_chunk_size);
// Do something if you want with the group
tempArray.push(myChunk);
}
console.log(tempArray)
// [[1,2,3,4,5],[6,7,8,9],[10,11,12,13],[14,15,16,17]
Hope it helps.
Related
I would like to compare two array then return the index once first array less than second array. But if the start value of first array greater than the second array, must skip until first array value less than second array. For example
case1: This must return index = 2 (exampleArr1 < exampleArr2 at index =2)
var exampleArr1 = [15,9,7,5,3,1];
var exampleArr2 = [2,6,8,12,17,22];
function compareArray(exampleArr1,exampleArr2){
...
return result
}
case2: This must return index = 6 (exampleArr1 < exampleArr2 at index =6)
var exampleArr1 = [1,2,4,5,15,9,7,5,3];
var exampleArr2 = [2,3,5,6,2,6,8,12,17];
function compareArray(exampleArr1,exampleArr2){
...
return result
}
Any advice or guidance on this would be greatly appreciated, Thanks.
A simple for loop will suffice. In our loop, we already have the index so we just compare the first array at this index with the second array at this index.
const compareArray = (arr1, arr2) => {
let startIndex = 0
if (arr1[0] < arr2[0]) {
// find the index from arr1 that is greater than arr2
startIndex = arr1.findIndex((a, index) => a > arr2[index])
}
for (let i = startIndex; i < arr1.length; i++) {
if (arr1[i] < arr2[i]) {
return i
}
}
}
const exampleArr1 = [15,9,7,5,3,1]
const exampleArr2 = [2,6,8,12,17,22]
const exampleArr3 = [1,2,4,5,15,9,7,5,3]
const exampleArr4 = [2,3,5,6,2,6,8,12,17]
console.log(compareArray(exampleArr1, exampleArr2))
console.log(compareArray(exampleArr3, exampleArr4))
Please use the findIndex method in your function like
var exampleArr1 = [15,9,7,5,3,1];
var exampleArr2 = [2,6,8,12,17,22];
function compareArray(exampleArr1,exampleArr2) {
if (exampleArr1[0] < exampleArr2[0]) {
exampleArr1 = exampleArr1.slice(1);
}
return exampleArr1.findIndex(function(e, i) { return exampleArr2[i] > e; });
}
console.log(compareArray(exampleArr1, exampleArr2));
How would you best turn [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] into [12,12]
Basically reducing an array into an aggregate every 12 items.
This is my not so elegant attempt:
let arr = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1];
let result = 0;
let finalArr = [];
arr.forEach((item,index) => {
result += item;
if((index+1) % 12 === 0) {
finalArr.push(result)
result = 0
}
})
Can this be done a bit more elegantly? Perhaps using reduce()? I haven't used js in a while so I am a bit rusty! Thanks in advance.
function reduceGroupByN(arr, count, fn) {
const out = []
if (count <= 1) throw new Error("Grouping must be greater than 1")
for (var last = 0; last < arr.length; last += count) {
out.push(arr.slice(last, last + count).reduce(fn))
}
return out
}
const arr = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
const result = reduceGroupByN(arr, 12, (a, b) => a + b)
For a more general solution, consider using a function that chunks an array into slices. (This sort of utility function is relatively common already.) Then call that function and add up the resulting subarrays with .map:
const arr = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1];
const chunk = (arr, length) => arr.reduce((a, num, i) => {
const chunkIndex = Math.floor(i / length);
if (!a[chunkIndex]) {
a[chunkIndex] = [];
}
a[chunkIndex].push(num);
return a;
}, []);
const finalArr = chunk(arr, 12)
.map(
subarr => subarr.reduce((a, b) => a + b)
);
console.log(finalArr);
At the risk of angering the modern JS gods, there is still something to be said for the performance and simplicity of a classic solution.
On my machine this runs two to three times faster compared to the other answers.
let arr =[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1];
let finalArr=[]
let chunkSize=12
for (n=0; n < arr.length;)
{
for( result=0, m=0; m < chunkSize; m++, n++)
{
result+=arr[n]
}
finalArr.push(result)
}
console.log(finalArr)
I'm trying to do a for loop on an array and be able to start that loop on a specific index and loop over the array x amount of times.
const array = ['c','d','e','f','g','a','b','c']
I want to loop 8 indexes starting at any index I wish. Example starting at array[4] (g) would return
'g','a','b','c','c','d','e','f'
This is what I've tried so far
const notes = ['c','d','e','f','g','a','b','c']
var res = []
for (var i = 4; i < notes.length; i++) {
res.push(notes[i])
}
console.log(res)
You can use modulo % operator.
const getArray = (array, index) => {
const result = [];
const length = array.length;
for (let i = 0; i < length; i++) {
result.push(array[(index + i) % length]);
}
return result;
};
Simple way.
var notes = ['c','d','e','f','g','a','b','c'];
function looparr(arr, start)
{
var res = [], start = start || 0;
for(var index = start, length=arr.length; index<length; index++)
{
res.push(arr[index]);
index == (arr.length-1) && (index=-1,length=start);
}
return res;
}
console.log(looparr(['c','d','e','f','g','a','b','c'], 0));
console.log(looparr(['c','d','e','f','g','a','b','c'], 2));
console.log(looparr(['c','d','e','f','g','a','b','c'], 4));
console.log(looparr(['c','d','e','f','g','a','b','c']));
Very simple solution below :)
While i < index, remove the first character from the array, store it in a variable and then add it back onto the end of the array.
let array = ['c','d','e','f','g','a','b','c'];
var index = 4;
for(var i = 0; i < index; i++) {
var letter1 = array.shift(i); // Remove from the start of the array
array.push(letter1); // Add the value to the end of the array
}
console.log(array);
Enjoy :)
This question already has answers here:
Split array into chunks
(73 answers)
Closed 6 years ago.
My example:
let arr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18];
let slice = (source, index) => source.slice(index, index + 4);
let length = arr.length;
let index = 0;
let result = [];
while (index < length) {
let temp = slice(arr, index);
result.push(temp);
index += 4;
}
console.log(result);
Logging:
[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16],[17,18]]
I want to slice the array to multiple parts for per 4 items [1,2,3,4] [5,6,7,8]...
The code is working fine.
I have 2 questions:
1/. Is there another way to do that via using inline code? Ex: result = arr.slice(...)
2/. After I define:
let push = result.push;
why cannot I still use:
push(temp)
Error message:
Uncaught TypeError: Array.prototype.push called on null or undefined
UPDATE: I've updated the solution based on the answers. Hope it's helpful.
let arr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18];
let result = [];
arr.forEach((x,y,z) => !(y % 4) ? result.push(z.slice(y, y + 4)) : '');
console.log(result);
Logging:
[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16],[17,18]]
A simple way to do it
var chunckArray = function(array, chunkCount){
var chunks = [];
while(array.length){
chunks.push(array.splice(0, chunkCount);
}
return chunks;
}
A non consumative way :
var chunckArray = function(array, chunkCount){
var chunks = [], i, j;
for (i = 0, j = array.length; i<j; i+= chunkCount) {
chunks.push(array.slice(i, i + chunkCount);
}
return chunks;
}
You can also do it using a for loop which will give you a same result:
let arr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18];
console.log(arr.length);
var result=[];
for(var i = 0; i < arr.length; i+=4) {
result=arr.slice(i, i+4);
console.log(result);
}
I've done this using a simple reduce (see plnkr):
let arr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18];
var counter = -1;
var result = arr.reduce((final, curr, i) => {
if (i % 4 === 0) {
final.push([curr])
counter++;
} else {
final[counter].push(curr);
}
return final;
}, []);
console.log(result);
You can tweak the 4 and substitute it with a variable chuckSize this will allow you to be able reapply to other things - you could also wrap this in a function with paramerters (array, chunkSize) if you wanted to
This question already has answers here:
Cartesian product of multiple arrays in JavaScript
(35 answers)
Closed 1 year ago.
I'm having trouble coming up with code to generate combinations from n number of arrays with m number of elements in them, in JavaScript. I've seen similar questions about this for other languages, but the answers incorporate syntactic or library magic that I'm unsure how to translate.
Consider this data:
[[0,1], [0,1,2,3], [0,1,2]]
3 arrays, with a different number of elements in them. What I want to do is get all combinations by combining an item from each array.
For example:
0,0,0 // item 0 from array 0, item 0 from array 1, item 0 from array 2
0,0,1
0,0,2
0,1,0
0,1,1
0,1,2
0,2,0
0,2,1
0,2,2
And so on.
If the number of arrays were fixed, it would be easy to make a hard coded implementation. But the number of arrays may vary:
[[0,1], [0,1]]
[[0,1,3,4], [0,1], [0], [0,1]]
Any help would be much appreciated.
Here is a quite simple and short one using a recursive helper function:
function cartesian(...args) {
var r = [], max = args.length-1;
function helper(arr, i) {
for (var j=0, l=args[i].length; j<l; j++) {
var a = arr.slice(0); // clone arr
a.push(args[i][j]);
if (i==max)
r.push(a);
else
helper(a, i+1);
}
}
helper([], 0);
return r;
}
Usage:
cartesian([0,1], [0,1,2,3], [0,1,2]);
To make the function take an array of arrays, just change the signature to function cartesian(args) instead of using rest parameter syntax.
I suggest a simple recursive generator function:
// JS
function* cartesianIterator(head, ...tail) {
const remainder = tail.length ? cartesianIterator(...tail) : [[]];
for (let r of remainder) for (let h of head) yield [h, ...r];
}
// get values:
const cartesian = items => [...cartesianIterator(items)];
console.log(cartesian(input));
// TS
function* cartesianIterator<T>(items: T[][]): Generator<T[]> {
const remainder = items.length > 1 ? cartesianIterator(items.slice(1)) : [[]];
for (let r of remainder) for (let h of items.at(0)!) yield [h, ...r];
}
// get values:
const cartesian = <T>(items: T[][]) => [...cartesianIterator(items)];
console.log(cartesian(input));
You could take an iterative approach by building sub arrays.
var parts = [[0, 1], [0, 1, 2, 3], [0, 1, 2]],
result = parts.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; }
After doing a little research I discovered a previous related question:
Finding All Combinations of JavaScript array values
I've adapted some of the code from there so that it returns an array of arrays containing all of the permutations:
function(arraysToCombine) {
var divisors = [];
for (var i = arraysToCombine.length - 1; i >= 0; i--) {
divisors[i] = divisors[i + 1] ? divisors[i + 1] * arraysToCombine[i + 1].length : 1;
}
function getPermutation(n, arraysToCombine) {
var result = [],
curArray;
for (var i = 0; i < arraysToCombine.length; i++) {
curArray = arraysToCombine[i];
result.push(curArray[Math.floor(n / divisors[i]) % curArray.length]);
}
return result;
}
var numPerms = arraysToCombine[0].length;
for(var i = 1; i < arraysToCombine.length; i++) {
numPerms *= arraysToCombine[i].length;
}
var combinations = [];
for(var i = 0; i < numPerms; i++) {
combinations.push(getPermutation(i, arraysToCombine));
}
return combinations;
}
I've put a working copy at http://jsfiddle.net/7EakX/ that takes the array you gave earlier ([[0,1], [0,1,2,3], [0,1,2]]) and outputs the result to the browser console.
const charSet = [["A", "B"],["C", "D", "E"],["F", "G", "H", "I"]];
console.log(charSet.reduce((a,b)=>a.flatMap(x=>b.map(y=>x+y)),['']))
Just for fun, here's a more functional variant of the solution in my first answer:
function cartesian() {
var r = [], args = Array.from(arguments);
args.reduceRight(function(cont, factor, i) {
return function(arr) {
for (var j=0, l=factor.length; j<l; j++) {
var a = arr.slice(); // clone arr
a[i] = factor[j];
cont(a);
}
};
}, Array.prototype.push.bind(r))(new Array(args.length));
return r;
}
Alternative, for full speed we can dynamically compile our own loops:
function cartesian() {
return (cartesian.cache[arguments.length] || cartesian.compile(arguments.length)).apply(null, arguments);
}
cartesian.cache = [];
cartesian.compile = function compile(n) {
var args = [],
indent = "",
up = "",
down = "";
for (var i=0; i<n; i++) {
var arr = "$"+String.fromCharCode(97+i),
ind = String.fromCharCode(105+i);
args.push(arr);
up += indent+"for (var "+ind+"=0, l"+arr+"="+arr+".length; "+ind+"<l"+arr+"; "+ind+"++) {\n";
down = indent+"}\n"+down;
indent += " ";
up += indent+"arr["+i+"] = "+arr+"["+ind+"];\n";
}
var body = "var res=[],\n arr=[];\n"+up+indent+"res.push(arr.slice());\n"+down+"return res;";
return cartesian.cache[n] = new Function(args, body);
}
var f = function(arr){
if(typeof arr !== 'object'){
return false;
}
arr = arr.filter(function(elem){ return (elem !== null); }); // remove empty elements - make sure length is correct
var len = arr.length;
var nextPerm = function(){ // increase the counter(s)
var i = 0;
while(i < len)
{
arr[i].counter++;
if(arr[i].counter >= arr[i].length){
arr[i].counter = 0;
i++;
}else{
return false;
}
}
return true;
};
var getPerm = function(){ // get the current permutation
var perm_arr = [];
for(var i = 0; i < len; i++)
{
perm_arr.push(arr[i][arr[i].counter]);
}
return perm_arr;
};
var new_arr = [];
for(var i = 0; i < len; i++) // set up a counter property inside the arrays
{
arr[i].counter = 0;
}
while(true)
{
new_arr.push(getPerm()); // add current permutation to the new array
if(nextPerm() === true){ // get next permutation, if returns true, we got them all
break;
}
}
return new_arr;
};
Here's another way of doing it. I treat the indices of all of the arrays like a number whose digits are all different bases (like time and dates), using the length of the array as the radix.
So, using your first set of data, the first digit is base 2, the second is base 4, and the third is base 3. The counter starts 000, then goes 001, 002, then 010. The digits correspond to indices in the arrays, and since order is preserved, this is no problem.
I have a fiddle with it working here: http://jsfiddle.net/Rykus0/DS9Ea/1/
and here is the code:
// Arbitrary base x number class
var BaseX = function(initRadix){
this.radix = initRadix ? initRadix : 1;
this.value = 0;
this.increment = function(){
return( (this.value = (this.value + 1) % this.radix) === 0);
}
}
function combinations(input){
var output = [], // Array containing the resulting combinations
counters = [], // Array of counters corresponding to our input arrays
remainder = false, // Did adding one cause the previous digit to rollover?
temp; // Holds one combination to be pushed into the output array
// Initialize the counters
for( var i = input.length-1; i >= 0; i-- ){
counters.unshift(new BaseX(input[i].length));
}
// Get all possible combinations
// Loop through until the first counter rolls over
while( !remainder ){
temp = []; // Reset the temporary value collection array
remainder = true; // Always increment the last array counter
// Process each of the arrays
for( i = input.length-1; i >= 0; i-- ){
temp.unshift(input[i][counters[i].value]); // Add this array's value to the result
// If the counter to the right rolled over, increment this one.
if( remainder ){
remainder = counters[i].increment();
}
}
output.push(temp); // Collect the results.
}
return output;
}
// Input is an array of arrays
console.log(combinations([[0,1], [0,1,2,3], [0,1,2]]));
You can use a recursive function to get all combinations
const charSet = [["A", "B"],["C", "D", "E"],["F", "G", "H", "I"]];
let loopOver = (arr, str = '', final = []) => {
if (arr.length > 1) {
arr[0].forEach(v => loopOver(arr.slice(1), str + v, final))
} else {
arr[0].forEach(v => final.push(str + v))
}
return final
}
console.log(loopOver(charSet))
This code can still be shorten using ternary but i prefer the first version for readability 😊
const charSet = [["A", "B"],["C", "D", "E"],["F", "G", "H", "I"]];
let loopOver = (arr, str = '') => arr[0].map(v => arr.length > 1 ? loopOver(arr.slice(1), str + v) : str + v).flat()
console.log(loopOver(charSet))
Another implementation with ES6 recursive style
Array.prototype.cartesian = function(a,...as){
return a ? this.reduce((p,c) => (p.push(...a.cartesian(...as).map(e => as.length ? [c,...e] : [c,e])),p),[])
: this;
};
console.log(JSON.stringify([0,1].cartesian([0,1,2,3], [[0],[1],[2]])));