I have a simple recursive javascript function that can be called with additional arguments:
AllDataRows(grid.Rows, process);
AllDataRows(grid.Rows, process, storeIDs);
The problem is that if the function has to call itself then any additional arguments are lost. I tried using Array.prototype.slice.call(arguments, 2) to pass the arguments along, but they end up as one element arrays. The cb function then fails because it isn't expecting an array (it would be a hidden textbox).
How can I resolve this?
Thanks
function AllDataRows(rowList, cb) {
if (rowList.getRow(0).GroupByRow) {
for (var i = 0; i < rowList.length; i++)
AllDataRows(rowList.getRow(i).Rows, cb);
} else {
var args = Array.prototype.slice.call(arguments, 2);
for (var j = 0; j < rowList.length; j++)
cb.apply(rowList.getRow(j), args);
}
}
function AllDataRows(rowList, cb) {
if (rowList.getRow(0).GroupByRow) {
for (var i = 0; i < rowList.length; i++) {
var aa = Array.prototype.slice.call(arguments, 0);
aa[0] = rowList.getRow(1).Rows;
AllDataRows.apply(this, aa);
}
} else {
var args = Array.prototype.slice.call(arguments, 2);
for (var j = 0; j < rowList.length; j++)
cb.apply(rowList.getRow(j), args);
}
}
Just use apply when you make the recursive call, fixing up the argument array to account for the sub-group you're opening up.
Related
Here is my code sans input
// Check if three addends equal sum and return the product if so
let result;
function addNumbers(first,second,third,sum) {
if (first + second + third === sum) {
result = first * second * third;
return (first * second * third);
}
};
// find three numbers in list that add up to specific number
function testResult(list,sum) {
let firstAddend;
let secondAddend;
let thirdAddend;
for (let i = 0; i < list.length; i++) {
firstAddend = list.shift();
for (let j = 0; j < list.length; j++) {
secondAddend = list[j];
for (let k = 1; k < list.length; k++) {
thirdAddend = list[k];
addNumbers(firstAddend,secondAddend,thirdAddend,sum);
}
}
}
};
What I want is testResult() to return the result from addNumbers() when it returns the product. I want to get rid of let result; and result = ... in addNumbers(). I've been confused about scope but I think I'm starting to understand. Does each for loop contain the scope of the previous? If anyone is interested this is from Advent of Code Day 1. I am not certain if having the data is necessary here. If it is let me know and I will edit accordingly.
Does each for loop contain the scope of the previous?
Yes, it does. Whenever you create a sub-scope, all the variables in the previous scope are available. So, you don't actually have to declare firstAddend and secondAddend and thirdAddend ahead of time:
function testResult(list,sum) {
for (let i = 0; i < list.length; i++) {
let firstAddend = list.shift();
for (let j = 0; j < list.length; j++) {
let secondAddend = list[j];
for (let k = 1; k < list.length; k++) {
let thirdAddend = list[k];
addNumbers(firstAddend,secondAddend,thirdAddend,sum);
}
}
}
}
Next, the return means that when you call the function, it takes on the value that you return. So, you don't need a global result variable, as you can just utilize the return value. However, you should move the if statement out of the addNumbers function and into the testResult function, as you need to know when to return, not just what to return. In fact, the addNumbers function is simply enough to where it should just go directly into testResult:
function testResult(list,sum) {
for (let i = 0; i < list.length; i++) {
let firstAddend = list.shift();
for (let j = 0; j < list.length; j++) {
let secondAddend = list[j];
for (let k = 1; k < list.length; k++) {
let thirdAddend = list[k];
if (firstAddend + secondAddend + thirdAddend === sum) {
return firstAddend * secondAddend * thirdAddend;
}
}
}
}
}
For practice, if you want the function, you could do something like the following:
function addNumbers(first,second,third,sum) {
if (first + second + third === sum) {
return (first * second * third);
} else {
return null; // return some value so the calling function knows that no sum was found
}
}
function testResult(list,sum) {
for (let i = 0; i < list.length; i++) {
let firstAddend = list.shift();
for (let j = 0; j < list.length; j++) {
let secondAddend = list[j];
for (let k = 1; k < list.length; k++) {
let thirdAddend = list[k];
let result = addNumbers(firstAddend, secondAddend, thirdAddend, sum);
if (result !== null) {
return result;
}
}
}
}
}
Trying to make this function true. I think the problem is in my for loop.
function forEach(array, callback){
console.log(array, callback);
for(var i = 0; i < array.length; i++) {
}
}
// testing your code with console.assert
var total = 1;
var myArray = [1, 2, 3, 4];
function multiplyTotal(a) {
total *= a;
}
forEach(myArray, multiplyTotal);
// and finally assert; if this fails, the program stops
console.assert(total === 24);
function forEach(array, callback){
console.log(array, callback);
for(var i = 0; i < array.length; i++) {
callback(array[i]); // you need to call callback function
}
}
Additionally, Javascript already has a built in function for this:
myArray.forEach(multiplyTotal);
http://www.w3schools.com/jsref/jsref_forEach.asp
I implemented a search function and loop that search by collection. In first iteration I need to search by all collection, but all next iterations only by result of the first iteration. I use if-statement if (i >= 1) collection = result; to do it, but it's not safe because I need to save collection. Is it possible to rewrite loop to recursion function? How I can make it or make my code elegant?
var targets = target.split(' '); // => ['hello', 'world'];
for (var i = 0; i < targets.length; ++i) {
if (i >= 1) {
collection = result;
}
result = includes(collection, props, targets[i]);
}
My search function:
function includes(collection, props, target) {
var result = [];
var collection_length = collection.length;
var props_length = props.length;
var target_length = target.length;
for (var i = 0; i < collection_length; ++i) {
for (var j = 0; j < props_length; ++j) {
if (collection[i][props[j]]) {
if (collection[i][props[j]].toLowerCase().slice(0, target_length) === target) {
result.push(collection[i]);
continue;
}
}
}
}
return result.length !== 0 ? result : false;
}
var result = collection;
for (var i = 0; i < targets.length; ++i) {
result = includes(result, props, targets[i]);
}
Maybe I'm missing something but isn't this what you're trying to do?
I've created a simple forEach function and I'm trying to understand why, when I run it with myArray, it doesn't mutate the array even though I run element*2.
function forEach(array, callback) {
for (var i = 0; i < array.length; i++) {
callback(array[i],i,array)
};
}
var myArray = [1,2,3]
forEach(myArray,function(element){element*2})
console.log(myArray)///[1,2,3]
You have to modify the array in the for loop, like this:
function forEach(array, callback) {
for (var i = 0; i < array.length; i++) {
array[i] = callback(array[i],i,array)
};
}
var myArray = [1,2,3]
forEach(myArray,function(element){return element*2})
console.log(myArray)
As we were discussing in the comments, the best way would be to implement something like the map function: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/map
function map(array, callback) {
var out = [];
for (var i = 0; i < array.length; i++) {
out.push(callback(array[i],i,array))
};
return out;
}
var myArray = [1,2,3]
var myOtherArray = map(myArray,function(element){return element*2})
console.log(myArray)
console.log(myOtherArray)
This way myArray is not touched, you create a new one. This is usually the best option, but sometimes you may want to modify it in place, because it is huge or for some other (good?) reason. In that case you can use the first option.
You should assign new array element value, because primitive types (like numbers in your case) are immutable, so element * 2 does not modify element.
To do the job, you should not touch you current forEach implementation, because forEach is not supposed to return values from callback (this is not map). In this case you should do something like this:
function forEach(array, callback) {
for (var i = 0; i < array.length; i++) {
callback(array[i], i, array);
}
}
var myArray = [1,2,3];
forEach(myArray, function(element, i, arr) {
arr[i] = element * 2;
});
document.write(JSON.stringify( myArray ));
This should work, explicitly assigning the variable.
function forEach(array, callback) {
for (var i = 0; i < array.length; i++) {
array[i] = callback(array[i])
};
}
var myArray = [1,2,3]
forEach(myArray,function(element){return element*2})
console.log(myArray)///[1,2,3]
Yep, bad answer. This [snippet] would do it though.
Anyway, in modern browsers we have Array.forEach to availability
function foreach(array, callback) {
for (var i = 0; i < array.length; i++) {
array[i] = callback(array[i]);
// ^ assign the new value (from the callback function)
};
}
var myArray = [1,2,3]
foreach( myArray, function (element){ return element * 2 } );
// ^ return the new value
document.querySelector('#result').textContent = myArray;
<pre id="result"></pre>
Array.prototype.remove = function (obj) {
for(var i = 0; i < this.length; i++) {
if(this[i] === obj) {
if (i == this.length) {
this[i] = null;
} else {
for(var j = i; j < this.length-1; j++) {
this[j] = this[j+1];
}
delete this[j]; // updated from this[j] = null; still not working.
}
}
}
return this;
};
calling it with:
write("ARRAY TEST = " + [22, 33, 44].remove(33).remove(22));
..it prints:
44,,
Why this 2 commas and how to fix my remove function to remove the commas as well?
delete on an Array will not remove the element, it will set it to undefined. And since undefined when printed results in an empty string, that explains the results of write().
You need to use splice() to remove the element. If you combine it with indexOf (you may need to define it for older browser) you get a pretty short function:
Array.prototype.remove = function (obj) {
this.splice(this.indexOf(obj), 1);
return this;
}
PS: I'm not an advocate of expanding native prototypes...
Setting the item to null leaves an item in the array (but it is a null item), which is why you see the commas still.
As previously mentioned, deleting or setting the item to null still leaves the item in the array. What you want to use is Array.splice
Here's an implementation that should work:
Array.prototype.remove = function (obj) {
for(var i = 0; i < this.length; i++) {
if(this[i] === obj)
{
this.splice(i,1);
break;
}
}
return this;
};
You don't remove the elements from the array you just set them to null.
If you need inspiration look at this remove method. It's by index and not by element.
http://ejohn.org/blog/javascript-array-remove/
try:
Array.prototype.remove = function (obj) {
for(var i = 0; i < this.length; i++) {
if(this[i] === obj) {
if (i == this.length) {
this.splice(i,1);
} else {
for(var j = i; j < this.length-1; j++) {
this[j] = this[j+1];
}
this.splice(j,1);
}
}
}
return this;
};
Array.prototype.remove = function (obj) {
for(var i = 0; i < this.length; i++) {
if(this[i] === obj) {
if (i == this.length) {
#this[i] = null;
delete this[i];
} else {
for(var j = i; j < this.length-1; j++) {
this[j] = this[j+1];
}
#this[j] = null;
delete this[i];
}
}
}
return this;
};
Pretty sure that is what you want