I am trying to make a small program that prompts a user to add items to a grocery list.
I read about using recursion to loop. I understand a while loop would probably be better suited for this task, but I ran into the same problems with the while loop and I wanted to try recursion. It just sounds like I know what I'm doing... "Yeh, I used recursion to enumerate the array while prompting validation from the user... hur hur hur"... but, I digress.
Here is the code:
function addToArray() {
var array = [];
array.push(prompt("Add items to array or 'q' to stop"));
if (array.pop() == 'q') {
document.write(array)
}
else {
addToArray();
}
}
addToArray();
If you'll notice, it loops like its supposed to but it is not adding items to an array. I have tried the array[i] = i technique as well but to no avail, the array remains empty. Also, why is it that by using a function with no args am I not running into too much recursion? Is it because of the conditional statement?
If you know what I'm doing wrong, try and hint towards the right answer rather than just blurting it out. I'd like to have that 'Aha' moment. I think this all helps us learn a bit better.
Thanks guys. (and gals)
You're creating a new array instead of passing it to the recursive call.
Do this instead.
DEMO: http://jsfiddle.net/kDtZn/
function addToArray(array) {
array.push(prompt("Add items to array or 'q' to stop"));
if (array[array.length-1] == 'q') {
array.pop();
document.write(array)
}
else {
addToArray(array);
}
}
addToArray([]);
Now you start with an empty array, and for each recursive call, it passes the same array forward.
Also, I changed it so that it doesn't use .pop() in the if() condition, otherwise you'll always end up with an empty array when it comes time to write it. (The .pop() method actually removes the last item.)
Finally, make sure you're not using document.write after the DOM is loaded. If so, you need to change it to use DOM manipulation methods instead.
You could take a different approach so that you don't need .pop() at all.
DEMO: http://jsfiddle.net/kDtZn/1/
function addToArray(array) {
var item = prompt("Add items to array or 'q' to stop");
if (item == 'q') {
document.body.textContent = array;
} else {
array.push(item);
addToArray(array);
}
}
addToArray([]);
The reason your while loop didn't work is very likely because of the original .pop() issue.
Your function recreates var array = [] on every loop/recursion. I am not sure if recursion is the right tool for the job in your case - it does not seems like it - but if you're starting out with JavaScript/development and just trying it out then you're fine.
While an 'infinite loop' is probably what you really want (as it would probably make the code simpler), you can do this with recursion by defaulting the array and passing it as an argument to the function. Like so...
function addToArray( array ) {
var array = array || [];
array.push(prompt( "Add items to array or 'q' to stop" ));
if ( array[array.length - 1] === 'q' ) {
document.write(array.slice( 0, -1 ))
} else {
addToArray( array );
}
}
addToArray();
There's two issues with the code as you presented. One, as pointed out earlier, you're redefining your array variable every time you call your function. Second, array.pop() alters your array, so when you get to the document.write call, you'd be printing an empty array anyways.
Related
Say I want to have an if statement to test if an array is empty or not,
Looking it up I have found no way to check besides array[0], which in frameworks like React and Angular give errors on the array's item not existing.
let arr = [];
if (!arr[0]) {
arr.map(val=>{
return <div>{val.name}</div>
});
}
In this example I want it to Display the JSX and if not, do not display any JSX. But doing this throws an error. Is there anyway to do this? preferably without any outside libraries.
edit: "added 'this' after 'to do'"
This checks whether the variable is defined and whether it contains anything (although in this case you'd probably not need the first check):
if ( arr && arr.length )
Also, don't forget to return arr.map(...
If you're not even certain your variable even is an array, this is what I would do:
if (Array.isArray(arr) && arr.length) { /*...*/ }
For your special case you probably don't even need the 2nd check, though, because all of the Array iterators don't run even once if the Array they are called on is empty.
Example:
const arr = [];
console.log(arr.map(Number));
console.log(arr.some(parseInt));
console.log(arr.every(parseInt));
console.log(arr.filter(Number));
None of the methods will throw an error provide arr is an array.
I am trying to create a function that will trim off array values that are not 4 chars long. For some reason, it does not work. I think that it might be with the return statement but I am not sure and I need help with it. Here is the code for the function: (Please don't suggest ways of making the hasher better, I just need help on the function)
function cutit(seq){
for(var i=0;i<seq.length;i++){
var temparr=seq[i].split("");
if(temparr.length % 4 !== 0){
seq.splice(seq[i],1);
return seq;
}
}
}
Five things:
Yes, the return should happen after the loop not after the first found item to delete.
.splice() takes an index for the first parameter, not the element. Pass i instead of seq[i].
splice(…, 1) does decrease the length of the array you're iterating over by one. You need to take care of that and decrease your counter as well to have a look at the i index again, where on the next iteration the next element will sit.
Instead of splitting the string into an array of single chars and taking the length of that, just take the length of the string directly.
Your functions removes all strings that are not a multiple of 4 long, not all that are not exactly 4 characters long. Not sure whether that is intended.
function cutit(seq) {
for (var i=0; i<seq.length; i++) {
if (seq[i].length !== 4) {
seq.splice(i--, 1);
}
}
return seq;
}
Also, notice that mutating an array is seldom a good idea (especially in an inefficient way like this with multiple splices). Returning a new array is so much easier, and you can use the higher-order filter Array method for that:
function cutit(seq) {
return seq.filter(function(str) {
return str.length === 4;
});
}
Scenario: I'm searching for a specific object in a deep object. I'm using a recursive function that goes through the children and asks them if I'm searching for them or if I'm searching for their children or grandchildren and so on. When found, the found obj will be returned, else false. Basically this:
obj.find = function (match_id) {
if (this.id == match_id) return this;
for (var i = 0; i < this.length; i++) {
var result = this[i].find(match_id);
if (result !== false) return result;
};
return false;
}
i'm wondering, is there something simpler than this?:
var result = this[i].find(match_id);
if (result) return result;
It annoys me to store the result in a variable (on each level!), i just want to check if it's not false and return the result. I also considered the following, but dislike it even more for obvious reasons.
if (this[i].find(match_id)) return this[i].find(match_id);
Btw I'm also wondering, is this approach even "recursive"? it isn't really calling itself that much...
Thank you very much.
[edit]
There is another possibility by using another function check_find (which just returns only true if found) in the if statement. In some really complicated cases (e.g. where you don't just find the object, but also alter it) this might be the best approach. Or am I wrong? D:
Although the solution you have is probably "best" as far as search algorithms go, and I wouldn't necessarily suggest changing it (or I would change it to use a map instead of an algorithm), the question is interesting to me, especially relating to the functional properties of the JavaScript language, and I would like to provide some thoughts.
Method 1
The following should work without having to explicitly declare variables within a function, although they are used as function arguments instead. It's also quite succinct, although a little terse.
var map = Function.prototype.call.bind(Array.prototype.map);
obj.find = function find(match_id) {
return this.id == match_id ? this : map(this, function(u) {
return find.call(u, match_id);
}).filter(function(u) { return u; })[0];
};
How it works:
We test to see if this.id == match_id, if so, return this.
We use map (via Array.prototype.map) to convert this to an array of "found items", which are found using the recursive call to the find method. (Supposedly, one of these recursive calls will return our answer. The ones which don't result in an answer will return undefined.)
We filter the "found items" array so that any undefined results in the array are removed.
We return the first item in the array, and call it quits.
If there is no first item in the array, undefined will be returned.
Method 2
Another attempt to solve this problem could look like this:
var concat = Function.prototype.call.bind(Array.prototype.concat),
map = Function.prototype.call.bind(Array.prototype.map);
obj.find = function find(match_id) {
return (function buildObjArray(o) {
return concat([ o ], map(o, buildObjArray));
})(this).filter(function(u) { return u.id == match_id })[0];
};
How it works:
buildObjArray builds a single, big, 1-dimensional array containing obj and all of obj's children.
Then we filter based on the criteria that an object in the array must have an id of match_id.
We return the first match.
Both Method 1 and Method 2, while interesting, have the performance disadvantage that they will continue to search even after they've found a matching id. They don't realize they have what they need until the end of the search, and this is not very efficient.
Method 3
It is certainly possible to improve the efficiency, and now I think this one really gets close to what you were interested in.
var forEach = Function.prototype.call.bind(Array.prototype.forEach);
obj.find = function(match_id) {
try {
(function find(obj) {
if(obj.id == match_id) throw this;
forEach(obj, find);
})(obj);
} catch(found) {
return found;
}
};
How it works:
We wrap the whole find function in a try/catch block so that once an item is found, we can throw and stop execution.
We create an internal find function (IIFE) inside the try which we reference to make recursive calls.
If this.id == match_id, we throw this, stopping our search algorithm.
If it doesn't match, we recursively call find on each child.
If it did match, the throw is caught by our catch block, and the found object is returned.
Since this algorithm is able to stop execution once the object is found, it would be close in performance to yours, although it still has the overhead of the try/catch block (which on old browsers can be expensive) and forEach is slower than a typical for loop. Still these are very small performance losses.
Method 4
Finally, although this method does not fit the confines of your request, it is much, much better performance if possible in your application, and something to think about. We rely on a map of ids which maps to objects. It would look something like this:
// Declare a map object.
var map = { };
// ...
// Whenever you add a child to an object...
obj[0] = new MyObject();
// .. also store it in the map.
map[obj[0].id] = obj[0];
// ...
// Whenever you want to find the object with a specific id, refer to the map:
console.log(map[match_id]); // <- This is the "found" object.
This way, no find method is needed at all!
The performance gains in your application by using this method will be HUGE. Please seriously consider it, if at all possible.
However, be careful to remove the object from the map whenever you will no longer be referencing that object.
delete map[obj.id];
This is necessary to prevent memory leaks.
No there is no other clear way, storing the result in a variable isn't that much trouble, actually this is what variables are used for.
Yes, that approach is recursive:
you have the base case if (this.id==match_id) return this
you have the recursive step which call itself obj.find(match_id) { ... var result = this[i].find(match_id); }
I don't see any reason, why storing the variable would be bad. It's not a copy, but a reference, so it's efficient. Plus the temporary variable is the only way, that I can see right now (I may be wrong, though).
With that in mind, I don't think, that a method check_find would make very much sense (it's most probably basically the same implementation), so if you really need this check_find method, I'd implement it as
return this.find(match_id) !== false;
Whether the method is recursive is hard to say.
Basically, I'd say yes, as the implementations of 'find' are all the same for every object, so it's pretty much the same as
function find(obj, match_id) {
if (obj.id == match_id) return obj;
for (var i = 0; i < obj.length; ++i) {
var result = find(obj[i], match_id);
if (result !== false) return result;
}
}
which is definitely recursive (the function calls itself).
However, if you'd do
onesingleobjectinmydeepobject.find = function(x) { return this; }
I'm not quite sure, if you still would call this recursive.
if(childrens are present) {
while(childrens.length ! ==0) {
do something
}
}
// Execute this function fnName()
My problem here is the while condition is getting executed, but when the length becomes zero... it does not come out... and execute the fnName()
If you're actually removing children from the array in the loop, that should be fine other than that you've got the spacing wrong on the operator; it should be !==, not ! ==:
while(childrens.length !== 0) {
Note that to actually remove children from the array, you have to use one of the array mutator methods, like pop or splice. My guess is that you're not doing that.
The more normal thing to do would be to loop through the array without mutating it:
var index, len, child;
for (index = 0, len = childrens.length; index < len; ++index) {
child = childrens[index];
// Use `child` for each child
}
Or using the new ES5 stuff like forEach (which requires knowing your users will have a very up-to-date browser, or including an "ES5 shim" since forEach is one of the things that can be shimmed):
childrens.forEach(function(child) {
// Use `child` for each child
});
Side note: The word "children" is already a plural (the singular is "child"), so there's no "s" on the end of it. Just one of English's irregular plurals (there are several).
The usual strategy is to create childrens as an array or similar object that has a consistent property that is checked on entering the loop, which continues while the condition is true.
In this case, it would be best to create childrens as an array, then decrement it in the loop so:
while (childrens.length) {
// do stuff to reduce the length of childrens
}
So when childrens.length is zero, the test is false and the loop exits (or if childrens.length is zero to start with, is never entered).
For checking not equal to zero it should be
children.length != 0 // (you have written in the question children.length ! ==0)
If this is a typo the other problem might be you are not decrementing children in the while loop. Hence, it goes to an infinite loop.
I thought this was going to be easy, but it's turning into quite a headache. I have a multidimensional JSON object being returned from my webserver. I'm parsing it to build a fairly deep navigation pane. I need to be able to search this object, but I'm coming up blank for the means.
The object is structured as so: tree->rows[]=>tree->rows[]=>tree …
Each tree can have many rows, and each row can have a tree etc.
At the row level there are a few variables, I need to search and find the value of one.
EX: if(tree.rows[x].tree.rows[y].url =="http://stackoverflow.com" return true;
My difficulty is, I don't know how to traverse the whole object. Even if I do it recursively I don't know how I could keep going up and down all the rows.
Here's an example of the object:
var jsonLNav = {itemClassName:"NodeLink",linkClassName:"NodeLinkTitle",linkHideClassName:"HideFromProd",navCategoryClassName:"NavCategory",onLoadJS:"",tree:[{pos:1,wid:"263a97c2-7cb9-470c-bf86-cadc28ae1323",pid:"1",rows:[{hide:0,title:"More IT Help",isNC:0,isMig:0,url:"http://vm-hsspdv-d09p/en-us/Help/Pages/ITHelp.aspx",isOL:0,tree:{pos:2,wid:"263a97c2-7cb9-470c-bf86-cadc28ae1323",pid:"3"}},{hide:0,title:"Office 2010",isNC:0,isMig:1,url:"http://office2010.lmig.com/Pages/Default.aspx",isOL:0,tree:{pos:2,wid:"263a97c2-7cb9-470c-bf86-cadc28ae1323",pid:"9"}},{hide:0,title:"E-mail Management",isNC:0,isMig:0,url:"http://vm-hsspdv-d09p/en-us/Help/EmailManagement/Pages/default.aspx",isOL:0,tree:{pos:2,wid:"8be66348-8da1-4e5c-90c5-0930d2f52d1a",pid:"123"}},]}]};
This example piece doesn't have any child trees of the row, the object that does is tens of thousands characters long, I can post if necessary.
The best code I can think of would be close to this (not tested, conceptually I'm missing something):
function findURL(url)
{
alert(searchJson(jsonLNav.tree[0],url));
}//end findURL
function searchJson(tree,url)
{
for(var x=0; x<=tree.rows.length-1;x++)
{
if(url == tree.rows[x].url)
{
return tree.rows[x].title;
}//end if
else
{
searchJson( tree.rows[x].tree,url)
}//end else
}//end for
}//end searchJson
Thanks!
When your search function calls itself recursively, it has to pay attention to the returned value, and somehow determine whether it's found it or not. Your function doesn't do anything special when it finds nothing, which I suppose is OK because the return value will be undefined.
else
{
var t = searchJson( tree.rows[x].tree,url);
if (t) return t;
}//end else
That way, the first loop that finds the url will return the (hopefully non-empty) "title", and that will propagate up the stack as a non-empty value for all the "if (t)" statements on the call stack.