Cumulative total with restart upon reaching a limit with a condition (part2) - javascript

Here is the link to a similar question I asked previously. This time I want to change the conditions. Here is the details:
I have an array:
inputArr = [2, 4, 3, 7, 8, 2, 3, 4, 9, 1];
All the numbers are below '10'. I want the cumulative total of the numbers pushed to the output array. But with two conditions:
if the cumulative total is not equal to '10' but closest to '10', after reaching the cumulative total closest to (not exceeding) '10', calculate the difference to '10' and push the difference as another element to the output array right after the last number that makes cumulative total to be closest to '10'.
AND restart pushing the cumulative total to output array from next number in the input array.
So, the output should be:
outputArr = [2, 6, 9, 1, 7, 3, 8, 2, 3, 4, 3, 9, 1];
Step by step explanation of how this output array is created:
I have tried modifying this code. Here is the modified code:
let input = [2, 4, 3, 7, 8, 2, 3, 4, 9, 1],
output = [],
cumulative = 0;
for (let i = 0; i < input.length; i++) {
cumulative += input[i];
if (cumulative < 10) {
output.push(cumulative)
} else if (cumulative == 10) {
output.push(10)
cumulative = 0
} else {
output.push(10 - output[i - 1])
cumulative = input[i]
output.push(cumulative)
}
}
console.log(output)
This code gives me this output:
outputArr = [2, 6, 9, 1, 7, 9, 8, 10, 3, 7, 0, 9, 10]
Here, only first 4 elements in the output array came as intended. How can I correct this code?
Any help is much appreciated.

Related

How to split nested array into "rows" with limited size

I have an array of arrays of different sizes. The goal is to generate "rows" where each row can contain a max of 12 elements.
For example:
Input data can be something like this:
const groups = [[1,2,3,4],[1,2,3,4,5,6], [1,2,3,4,5,6,7,8,9,10,11,12], [1,2,3,4,5,6,7], [1,2,3],[1,2,3]]
groups[0].length + groups[1].length = 10 -> row0
groups[2].length = 12 -> row1
groups[3].length + groups[4].length = 10 -> row3
groups[5].length = 3 -> row4
Output for such array should be:
[[[1,2,3,4], [1,2,3,4,5,6]], [[1,2,3,4,5,6,7,8,9,10,11,12]], [[1,2,3,4,5,6,7], [1,2,3]], [[1,2,3]]]
I was thinking of a recursive function for this but couldn't figure out how to solve it.
You can use Array#reduce() to do this.
The code first checks if the current last element (last "row") has more than 12 numbers in it if you add the next group:
(acc[acc.length - 1].flat().length + cv.length) <= 12
if it will be less than 12, the elements will get pushed into the "row":
acc[acc.length - 1].push(cv)
and if not, a new "row" will be added to the outer array:
acc.push([cv])
const groups = [[1, 2, 3, 4],[1, 2, 3, 4, 5, 6],[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],[1, 2, 3, 4, 5, 6, 7],[1, 2, 3],[1, 2, 3]];
const rows = groups.reduce((acc, cv) => {
(acc[acc.length - 1].flat().length + cv.length) <= 12 ?
acc[acc.length - 1].push(cv) :
acc.push([cv])
return acc
}, [[]]);
console.log(JSON.stringify(rows))
Here's one way to solve it recursively:
const regroup = (max, [g, ...gs], filled = [], curr = [], cc = 0) =>
g == undefined
? filled .concat ([curr])
: g .length + cc <= max
? regroup (max, gs, filled, curr .concat ([g]), cc + g.length)
: regroup (max, gs, filled .concat ([curr]), [g], g .length)
const groups = [[1, 2, 3, 4], [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
[1, 2, 3, 4, 5, 6, 7], [1, 2, 3], [1, 2, 3]]
console .log (regroup (12, groups))
.as-console-wrapper {max-height: 100% !important; top: 0}
We pass the maximum size and the list of items, and then we default three parameters:
filled will track the output rows we've filled; it starts with an empty array
curr stores the row we're working on; it also starts with an empty array
cc stores the count of all elements in the current row; it starts with zero
On each recursive call, we have one of three possibilities:
There are no more arrays to process, and we return all the filled rows with the current row appended.
The next array is small enough to fit in the current row, and we update the current to include it, and the current count to accommodate it.
The next array is too large, and we add the existing current row to the filled ones, and start a new current row with this array, setting the count appropriately.

Loop backwards Javascript Array

I would like to loop backwards in a array in javascript and then get the index of each element in the array for example if a array has 10 elements and is looped backwards it would log 9, 8, 7, 6, 5, 4, 3, 2, 1, 0. for some werid reason i am getting a bunch of negaitive -1s and im confused why it wont just return the index properly.
here is the code
//Arrays I would like to pass into the function
const valid1 = [4, 5, 3, 9, 6, 7, 7, 9, 0, 8, 0, 1, 6, 8, 0, 8];
const invalid1 = [4, 5, 3, 2, 7, 7, 8, 7, 7, 1, 0, 9, 1, 7, 9, 5];
function validateCred(arr) {
let sum = 0;
for (let i = arr.length - 1; i >= 0; i--) {
console.log(arr.indexOf(i));
}
}
console.log(validateCred(valid1));
why -1s?
It is because of arr.indexOf(i) when the loop starts i=15 so:
arr.indexOf(15) will return -1 because you don't have a 15 in your array.
next i=14 same as above.
.
.
.
i=9 then it will find the element at index 3.
As UnholySheep explains above, Array.indexOf(i) gives you the index of the first occurrence of the value represented by i in the array. Here is some code to help you debug:
function validateCred(arr) {
let sum = 0
for (let i = arr.length - 1; i >= 0; i--) {
console.log(i) // log the index
console.log(arr[i]) // log the value
}
}

Using splice to delete item in array

Why does the remaining in original array = [1, 3, 5, 7, 9]
Since arr.splice(i, 1) = i is the target index and 1 is the number of item to be removed, i is increases to 10 respectively from i++ which short for i = i + 1, So why does it remove 5 index and remain 5 in the array ? that's what i know so far and i have struggled to read the docs but still have no idea to understand, please explain it for me
let arr = [1,2,3,4,5,6,7,8,9,10];
for(let i = 1; i < arr.length; i++) {
arr.splice(i, 1);
}
It is because the length of arr decreases everytime splice function runs.
Here is how the array changes.
[2, 3, 4, 5, 6, 7, 8, 9, 10]
[2, 4, 5, 6, 7, 8, 9, 10]
[2, 4, 6, 7, 8, 9, 10]
[2, 4, 6, 8, 9, 10]
[2, 4, 6, 8, 10]
So every loop, i increases and arr.length decreases by 1. so only 5 loops runs and the result is [2, 4, 6, 8, 10]
You're wondering why it's removing 1, 3, 5, 7, and 9, right?
Here's why. As the for loop iterates, i keeps increasing by one.
HOWEVER, by calling .splice, you are removing the first element of the array, so as a result, every other element moves down an index.
Let's play this out step by step for a few iterations of the for loop.
i = 0; arr.splice(0, 1) removes 1, so arr is [2, 3, 4, 5, 6, 7, 8, 9, 10]
i = 1; arr.splice(1, 1) removes 3, not 2, because now 3 is at index 1 of arr. Performing the splice leaves arr as [2, 4, 5, 6, 7, 8, 9, 10].
i = 2; arr.splice(2, 1) removes 5, because 5 is currently at index 2. As a result, arr becomes [2, 4, 6, 7, 8, 9, 10].
Is it clear now what's going on?
If your goal is to successively remove each element, one at a time, then instead of calling .splice(i, 1) in each iteration of the loop, you should call .splice(0, 1), since the value at index 0 changes each time you call .splice.
Remember, arrays are 0 based. Second, the length is changing each time it evaluates.
MDN links:
Splice
Map
So you may want to try
i =< length
Where length is determined and is set ahead of time and constant. You can try mapping the array to a new one, so the original array stays pure and unaffected by the splice.
You need to check the for loop end condition, i is not increasing to 10. Why? because i < arr.length.
So it will like this :
Iteration 1:
i=0; arr.length = 10; arr = [1,2,3,4,5,6,7,8,9,10]; ==> result [2,3,4,5,6,7,8,9,10];
Iteration 2:
i=1; arr.length = 9; arr = [2,3,4,5,6,7,8,9,10]; ==> result [2,4,5,6,7,8,9,10];
Iteration 3:
i=2; arr.length = 8; arr = [2,4,5,6,7,8,9,10]; ==> result [2,4,6,7,8,9,10];
.
.
.and so forth
i = 5 ==> arr.length: 5 ==> final result : [2, 4, 6, 8, 10]
So if you want to delete all items using splice:
let arr = [1,2,3,4,5,6,7,8,9,10];
while(arr.length > 0) {
arr.splice(0, 1);
}

Javascript Generate Multidimensional array of 2 sizes

I cannot figure out the best way to dynamically generate a multidimensional array with 2 different sizes.
We have a UI that requires a row of 4 items, then 3. This pattern would repeat until the content in the array has been spent.
This is essentially what I need to do:
// Convert
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
// to
const rows [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10, 11], [12, 13, 14]];
This is what I currently have, it is only converting the arrays to 4 each.
const buildRows = (arr, length) => arr.reduce((rows, val, i) => (
i % length == 0 ? rows.push([val]) : rows[rows.length-1].push(val)
) && rows, []);
Thank you in advance for the help.
The following solution will mutate (empty) the input array array:
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
let result = [], i = 0;
while(array.length) { // while there still items in array (array is continually getting shrunk untill it is emptied (array.length === 0))
result.push(array.splice(0, i++ % 2? 3: 4)); // cut the first 3 or 4 numbers depending on the index of the cut i (if i is pair, then cut 4, else, cut 3) and then push the cut-out items into the result array
}
console.log(result);
If you don't want to mutate it, then use slice instead of splice, but you'll have to provide the start index of the cut:
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
let result = [], i = 0, next = 0; // next will be the index from which the cut will start
while(next < array.length) { // there is still items to cut
let itemsToCut = i % 2? 3: 4; // determine how many items we are going to cut
result.push(array.slice(next, next + itemsToCut)); // cut the items between next and next + itemsToCut
next += itemsToCut; // increment next by the number of cut-out items so it will point to the next item
i++;
}
console.log(result);
I suggest a more self-documenting generator solution where even & odd row-sizes are not hardcoded but supplied via arguments:
function* reshape(array, ...rows) {
let i = 0;
while (true) for (let row of rows) {
if (i >= array.length) return;
yield array.slice(i, i += row);
}
}
// Example:
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
for (let row of reshape(array, 4, 3)) console.log(row);
A true generator purist would simplify reshape by first introducing a repeat generator:
function* repeat(iterable) {
while (true) yield* iterable;
}
function* reshape(array, ...rows) {
let i = 0;
for (let row of repeat(rows)) {
if (i >= array.length) break;
yield array.slice(i, i += row);
}
}
// Example:
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
for (let row of reshape(array, 4, 3)) console.log(row);
You can achieve that using Array#reduce, a pointer to the last place, and a step variable that alternates between 3 and 4:
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
let last = 0;
let step = 0;
const result = array.reduce((r, num, i) => {
if(i === last + step) { // when the previous sub array is full
r.push([]); // add another sub array
last = i; // mark the start index of the current sub array
step = step === 4 ? 3 : 4; // alternate the step
}
r[r.length - 1].push(num); // push the number to the last sub array
return r;
}, []);
console.log(result);
Straighfoward and easy-to-read solution:
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
const r = [];
let chunk = [];
let l = 4;
array.forEach((el, i) => {
if (chunk.length < l) chunk.push(el);
if (chunk.length === l) {
r.push(chunk); chunk = [];
l = ( l === 4 ) ? 3 : 4;
}
})
console.log(r)
Yet another solution. Clearly everyone is having a good time with this one.
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
function divide(array, idx, num, result)
{
if(array.length <= idx) return result;
result.push(array.slice(idx,idx+num));
return divide(array, idx+num, num === 4 ? 3 : 4, result);
}
console.log(divide(array, 0, 4, []));
We can think of it as slicing elements from the array in a loop. It's just that we need to alternate between 4 and 3 instead of a constant value to slice.
We can parameterize alternating values by passing them in a function instead of hardcoding it in the solution like below:
Use Array##slice and
Just swap current and next like this by using destructuring assignment to achieve the solution.
Sub array sizes(4,3) can be modified outside actual logic or can be passed in a function to have flexible solution.
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
function TransformArray(array, current, next) {
let start = 0,
ans = [];
while (start < array.length - 1) {
ans.push(array.slice(start, start + current));
start += current;
[current, next] = [next, current]; //swap the count of array size
}
return ans;
}
console.log(TransformArray(array, 4, 3));
console.log(TransformArray(array, 3, 3));
Here's kind of a different way of doing this, I'm expanding a bit to allow you to arbitrarily pass array lengths, this allows the PM to change their mind any time and it isn't a big deal.
This could be cleaned up a bit, I wanted to leave it more verbose to make it easier to read.
// Setup the function getting in:
// an array
// first array's length
// second array's length
const arrayParser = (inArr,arr1len,arr2len) => {
// Create a new array.
let outArr = [];
// Basic forEach is basic, we need the value and the index.
inArr.forEach((val,idx) => {
// If the index's modulus of the total of the two array lengths is:
// 0 OR the first array's length
// Push a new empty array.
if (idx%(arr1len+arr2len)===0 || idx%(arr1len+arr2len)===arr1len) {
// Create a new array with the current value
outArr.push([]);
}
// Push the value to the last array in the out multidimensional array
outArr[outArr.length-1].push(val);
});
// You got it.. return the array.
return outArr;
};
// Single Dimensional Array
const singleArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30];
// Parse it.
// Expects:
// A Single Dimensional Array
// Length of the first array
// Length of the second array
console.log(arrayParser(singleArray,10,4));
console.log(arrayParser(singleArray,2,4));
console.log(arrayParser(singleArray,3,4));
console.log(arrayParser(singleArray,4,3));
console.log(arrayParser(singleArray,1,2));
This works, because you know the length of each of the inner arrays, so you don't really need to figure out anything.
Here's a 4,3 set broken out.
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
Push a new array at 0 and 4.
4+3
index total Modulus
0 % 7 = 0 <-- push [], push 1
1 % 7 = 1 <-- push 2
2 % 7 = 2 <-- push 3
3 % 7 = 3 <-- push 4
4 % 7 = 4 <-- push [], push 5
5 % 7 = 5 <-- push 6
6 % 7 = 6 <-- push 7
7 % 7 = 0 <-- push [], push 8
8 % 7 = 1 <-- push 9
9 % 7 = 2 <-- push 10
10 % 7 = 3 <-- push 11
11 % 7 = 4 <-- push [], push 12
12 % 7 = 5 <-- push 13
13 % 7 = 6 <-- push 14
Returns
[[1,2,3,4],[5,6,7],[8,9,10,11],[12,13,14]]
It ain't pretty and as much as I try to write functional code, it's pretty easy with a while loop...
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
const newArray = [];
let i = 0;
while (i < array.length) {
let four = array.slice(i, i + 4)
if (!(four.length > 0)) {
break;
}
newArray.push(four)
i += 4;
let three = array.slice(i, i + 3);
if (!(three.length > 0)){
break;
}
newArray.push(three);
i += 3;
}
return newArray

Why my push function is not working when I am inserting the value in same array [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
var myNums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var Remve = [];
var i;
for(i=0;i<myNums.length;i++){
if(myNums[i] % 3 == 0){
Remve.push(myNums[i])
}else{
myNums.push(myNums[i])
}
}
document.write(myNums);
document.write(Remve);
Why is my push function not working when I am inserting the value in the same array?
When I am inserting in another array it works.
Dissecting the relevant parts of your code:
// Allocate an array containing three values that will match: [3, 6, 9]
var myNums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Get the length of myNums *every iteration*
for (i = 0; i < myNums.length; i++) {
if (myNums[i] % 3 == 0) {
...
} else {
// Append the value to myNums if it is evenly divisible by three
myNums.push(myNums[i])
}
}
This will hit an infinite loop.
On each iteration, myNums will look like:
[_1_, 2, 3, 4, 5, 6, 7, 8, 9, 10] // append 1
[1, _2_, 3, 4, 5, 6, 7, 8, 9, 10, 1] // append 2
[1, 2, _3_, 4, 5, 6, 7, 8, 9, 10, 1, 2]
[1, 2, 3, _4_, 5, 6, 7, 8, 9, 10, 1, 2] // append 4
[1, 2, 3, 4, _5_, 6, 7, 8, 9, 10, 1, 2, 4] // append 5
... and so on
Once you've hit the end of the collection, every iteration will append yet another value to the very end.
You can solve this pretty easily, if you don't want to hit the appended items:
var myNums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var Remve = [];
var i;
var l = myNums.length;
for (i = 0; i < l; i++) {
if (myNums[i] % 3 == 0) {
Remve.push(myNums[i])
} else {
myNums.push(myNums[i])
}
}
document.write(myNums);
document.write(Remve);
By caching the length first, you won't enter the infinite loop.
You can also push the undivisible items into a different array instead of onto the end of the current one, which will solve the problem equally well.
A) You are constantly checking myNums.length, while inserting values into it.
B) It IS working, as you can see the same values appearing in myNums over and over again. But this is an infinite loop.
C) Console.log(Rmve); shows that you are indeed selecting for numbers divisible by 3.
Your current code is generating an infinite loop, your exit condition is never met.
Notice that myNums.length is constantly increased since you're adding new elements inside loop.
You should get array length before start iterating
var myNums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var Remve = [];
var i;
var arrayLength = myNums.length; // get length BEFORE starting for loop
for(i=0; i < arrayLength; i++){
if(myNums[i] % 3 == 0){
Remve.push(myNums[i])
}else{
myNums.push(myNums[i])
}
}
alert(myNums);
alert(Remve);
It is running in an infinite loop. You are pushing value in a same array so the array length keeps on increasing and it will never end.
var myNums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var Remve = [];
var i;
for(i=0;i<myNums.length;i++){
if(myNums[i] % 3 == 0){
Remve.push(myNums[i])
}else{
myNums.push(myNums[i]) //problematic line
}
}
You didn't say what you meant by "not working," and I'm not sure what you're trying to do, but I saw a problem. The loop was going off of the length of the array, which was changing each time the array ran after the 9th iteration, causing an infinite loop. Saving that in a static variable fixed this:
var myNums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var Remve = [];
var i;
var myNumLength = myNums.length;
for(i=0;i<myNumLength;i++){
if(myNums[i] % 3 == 0){
Remve.push(myNums[i])
}else{
myNums.push(myNums[i])
}
}
document.write(myNums);
document.write('<br>');
document.write(Remve);
Update: new code:
var originalNums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var newNums = [];
var removedNums = [];
var i;
var myNumLength = originalNums.length;
for(i=0;i<myNumLength;i++){
if(originalNums[i] % 3 == 0){
removedNums.push(originalNums[i])
}else{
newNums.push(originalNums[i])
}
}
document.write(newNums);
document.write('<br>');
document.write(removedNums);
Update again
var myNums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var removedNums = [];
var i;
var myNumLength = myNums.length;
for(i=0;i<myNumLength;i++){
if(myNums[i] % 3 == 0){
removedNums.push(myNums[i]);
myNums.splice(i, 1);
i--;
}
}
document.write(myNums);
document.write('<br>');
document.write(removedNums);
Use a separate array (myNumsArr) to store the values instead of myNums. Your current code will result in infinite loop.
var myNums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var myNumsArr = [];
var Remve = [];
var i;
for(i=0;i<myNums.length;i++){
if(myNums[i] % 3 == 0){
Remve.push(myNums[i])
}else{
myNumsArr.push(myNums[i])
}
}
myNums = myNums.concat(myNumsArr);

Categories

Resources