How would I go about creating a recursive function that can search through a list for a node where x = 10?
I do not have any javascript experience so I am not sure where to start so would appreciate and input
I have come across the following code and tried to adapt it but I am not sure if I am on the right track:
function search(_for, _in) {
var r;
for (var p in _in) {
if ( p === 10 ) {
return _in[p];
}
if ( typeof _in[p] === 'object' ) {
if ( (r = search(_for, _in[p])) !== null ) {
return r;
}
}
}
return null;
}
Thank you in advance
Try this
var finder = function(needle, haystack) {
if (haystack.length == 0)
return false
if (haystack[0] == needle)
return true
return finder(pin, haystack.slice(1))
}
Or
var finder = function(pin, haystack) {
return (haystack[0] == pin) || (haystack.length != 0) && finder(pin, haystack.slice(1))
}
Recursion FTW
Related
I have solved the problem Count the smiley faces:
Given an array (arr) as an argument complete the function countSmileys that should return the total number of smiling faces.
Rules for a smiling face:
Each smiley face must contain a valid pair of eyes. Eyes can be marked as : or ;
A smiley face can have a nose but it does not have to. Valid characters for a nose are - or ~
Every smiling face must have a smiling mouth that should be marked with either ) or D
No additional characters are allowed except for those mentioned.
Valid smiley face examples: :) :D ;-D :~)
Invalid smiley faces: ;( :> :} :]
Example
countSmileys([':)', ';(', ';}', ':-D']); // should return 2;
countSmileys([';D', ':-(', ':-)', ';~)']); // should return 3;
countSmileys([';]', ':[', ';*', ':$', ';-D']); // should return 1;
Note
In case of an empty array return 0. You will not be tested with invalid input (input will always be an array). Order of the face (eyes, nose, mouth) elements will always be the same.
Then when I look through the solutions I find that many people use regexp. Then I want write a state machine to implement regexp and solve this problem. But I failed. This is my code:
function countSmileys(smileys) {
let state = smileyHasValidEye;
return smileys.filter(smiley => {
for (let s of [...smiley]) {
state = state(s);
}
return state === true;
}).length;
}
function smileyHasValidEye(s) {
if (s === ':' || s === ';') {
return smileyHasValidNose;
}
return smileyHasValidEye;
}
function smileyHasValidNose(s) {
if (s === '-' || s === '~') {
return smileyHasValidMouth;
}
return smileyHasValidMouth(s);
}
function smileyHasValidMouth(s) {
if (s === ')' || s === 'D') {
return true;
}
return;
}
console.log(countSmileys([':)', ';(', ';}', ':-D']));
And the error I get is:
state = state(s);
^
TypeError: state is not a function
Then I debugged my code I found the procedure doesn't enter the smileyHasValidNose function. Then I don't know the reason.
The problem is you don't really reset state in between smileys. So the next smiley state will be true which you can't call (it's not a function).
You could use a local variable for state that resets it to the first function (the first step).
function countSmileys(smileys) {
let firstStep = smileyHasValidEye;
return smileys.filter(smiley => {
let state = firstStep;
for (let s of [...smiley]) {
state = state(s);
}
return state === true;
}).length;
}
function smileyHasValidEye(s) {
if (s === ':' || s === ';') {
return smileyHasValidNose;
}
return smileyHasValidEye;
}
function smileyHasValidNose(s) {
if (s === '-' || s === '~') {
return smileyHasValidMouth;
}
return smileyHasValidMouth(s);
}
function smileyHasValidMouth(s) {
if (s === ')' || s === 'D') {
return true;
}
return;
}
console.log(countSmileys([':)', ';(', ';}', ':-D']));
This code however, will error if there's more on the string besides the smiley (or a partial of the smiley).
I would change smileyHasValidMouth to return false if it doesn't detect a smiley. Just to be more consistent here...
function smileyHasValidMouth(s) {
if (s === ')' || s === 'D') {
return true;
}
return false;
}
And adjust your loop to exit early if it finds a value that is not a function.
for (let s of [...smiley]) {
state = state(s);
if(typeof state !== 'function') return state;
}
function countSmileys(smileys) {
let firstStep = smileyHasValidEye;
return smileys.filter(smiley => {
let state = firstStep;
for (let s of [...smiley]) {
state = state(s);
if (typeof state !== 'function') return state;
}
}).length;
}
function smileyHasValidEye(s) {
if (s === ':' || s === ';') {
return smileyHasValidNose;
}
return smileyHasValidEye;
}
function smileyHasValidNose(s) {
if (s === '-' || s === '~') {
return smileyHasValidMouth;
}
return smileyHasValidMouth(s);
}
function smileyHasValidMouth(s) {
if (s === ')' || s === 'D') {
return true;
}
return false;
}
console.log(countSmileys([':~(', ':>', ':D', ':(', ':o>', ';)', ':)']));
Question at hand: https://leetcode.com/problems/same-tree/
Can someone point out why my JavaScript solution can pass the test cases but fails during the actual submission?
var isSameTree = function(p, q) {
let queue = [];
queue.push(p);
queue.push(q);
while (!queue.length) {
let p = queue.shift();
let q = queue.shift();
if (p == null && q == null) {
continue;
} else if (p == null || q == null || p.val != q.val) {
return false;
} else {
queue.push(p.left);
queue.push(q.left);
queue.push(p.right);
queue.push(q.right);
}
}
return true;
};
#trincot mentioned it in the comments.
Just update the comparison inside the while loop to get into that loop.
var isSameTree = function(p, q) {
let queue = [];
queue.push(p);
queue.push(q);
while (queue.length != 0) {
let p = queue.shift();
let q = queue.shift();
if (p == null && q == null) {
continue;
} else if (p == null || q == null || p.val != q.val) {
return false;
} else {
queue.push(p.left);
queue.push(q.left);
queue.push(p.right);
queue.push(q.right);
}
}
console.log(queue);
return true;
};
This will pass all the test cases.
I need a bit of assistance with this shorthand condition. My attempt at it so far is becoming a bit of a challenge and cant seem to make it more readable. I believe it was short circuited from minifying.
if (a === !0 || perSearch.rates.fy2) {
e();
} else if ( perSearch.rates.fy1.multiple || perSearch.rates.fy2.multiple ){
calculateRates();
} else {
perSearch.rates.fy1.multiple;
}
From this terse expression:
a === !0 || (perSearch.rates.fy2 ? perSearch.rates.fy1.multiple || perSearch.rates.fy2.multiple ? e() : calculateRates() : perSearch.rates.fy1.multiple ? e() : calculateRates())
Your expression corresponds to
if (a === !0) {
} else {
if (perSearch.rates.fy2) {
if (perSearch.rates.fy1.multiple || perSearch.rates.fy2.multiple) {
e();
} else {
calculateRates();
}
} else {
if (perSearch.rates.fy1.multiple) {
e();
} else {
calculateRates();
}
}
}
which can be simplified to
if (a !== true) {
if (perSearch.rates.fy1.multiple || (perSearch.rates.fy2 && perSearch.rates.fy2.multiple)) {
e();
} else {
calculateRates();
}
}
This should be it:
if (a !== false) {
if (perSearch.rates.fy2) {
if (!perSearch.rates.fy1.multiple) {
if (perSearch.rates.fy2.multiple) {
e()
}
else {
calculateRates()
}
}
}
else {
if (perSearch.rates.fy1.multiple) {
e()
}
else {
calculateRates()
}
}
}
Is this a return of some sort (return a || result), or a large conditional (a is true or this other code evaluates true)?. Either way I would tend to attack it in stages, block code, then abstract, repeat until the code looks manageable.
Block
Block out your conditionals to make it easier to read.
(
a === !0
|| (
perSearch.rates.fy2
? (perSearch.rates.fy1.multiple || (perSearch.rates.fy2.multiple ? e() : calculateRates()))
: (perSearch.rates.fy1.multiple ? e() : calculateRates())
)
)
Abstract
Abstract out some any big easy repetitions of logic.
const x = rates => ( (rates) ? e() : calculateRates() );
(
a === true || (perSearch.rates.fy2)
? ((perSearch.rates.fy1.multiple) || x(perSearch.rates.fy2.multiple))
: x(perSearch.rates.fy1.multiple)
)
Continue
Work into the code to separate out the conditionals.
const calc = rates => ((rates) ? e() : calculateRates());
const compare = rates => {
let fy1 = (rates.hasOwnProperty('fy1')) ? rates.fy1 : false;
let fy2 = (rates.hasOwnProperty('fy2')) ? rates.fy2 : false;
if (fy2) {
if (fy1.multiple) {
return fy1.multiple;
}
return calc(fy2.multiple);
} else {
return calc(fy1.multiple);
}
}
a === true || compare(perSearch.rates);
Edit (more continue!)
Looking at this again I think it would benefit from some early returns.
Look at conditional for simplification.
If fy2 and if fy1.multiple {return fy1.multiple}
If not fy2 {return fy1.multiple}
If fy2 and not fy1.multiple {return fy2.multiple}
const calc = rates => ((rates) ? e() : calculateRates());
const compare = rates => {
let fy1 = (rates.hasOwnProperty('fy1')) ? rates.fy1 : false;
let fy2 = (rates.hasOwnProperty('fy2')) ? rates.fy2 : false;
// consolidate conditions leading to same place.
if (!fy2 || (fy2 && fy1 && fy1.multiple)) {
return calc(fy1.multiple);
}
return calc(fy2.multiple);
}
a === true || compare(perSearch.rates);
How can I pass the function logic to the find-function in order to use it on each document?
I am trying to find Mongo-documents recursively with certain values in a property. I have defined an "algorithm" and would like to this to the find-function, but despite reading about queries, searching and find(), I have had no luck.
// match every node with propertyname "Reference" with the exact value "43_1"
var logic = function(currentKey, currentValue) {
return ( currentKey == "Reference" && currentValue === "43_1" );
};
db.getCollection('metavalg').find(function() {
function recurSearchObj(doc) {
return Object.keys(doc).some(function(key) {
if ( typeof(doc[key]) == "object" && doc[key] !== null ) {
return recurSearchObj(doc[key]);
} else {
// invoke matching-logic
return logic(key, doc[key]);
}
});
}
// search document recursively
return recurSearchObj(this);
})
With the help of Vladimir M, I ended up rearringing the code and doing this:
db.getCollection('metavalg').find(function() {
function inspectObj(doc, logic) {
return Object.keys(doc).some(function(key) {
if ( typeof(doc[key]) == "object" && doc[key] !== null ) {
return inspectObj(doc[key], logic);
} else {
return logic(key, doc[key]);
}
});
}
return inspectObj(this, function(currentKey, currentValue) {
return ( currentKey == "Nummer" && currentValue === "43_1" );
//return ( currentKey == "Navn" && /max\.? 36/i.test(currentValue) );
});
})
You can pass logic method as a parameter to recurcive search object:
// match every node with propertyname "Reference" with the exact value "43_1"
var logic = function(currentKey, currentValue) {
return ( currentKey == "Reference" && currentValue === "43_1" );
};
db.getCollection('metavalg').find(function() {
function recurSearchObj(doc, aLogic) {
return Object.keys(doc).some(function(key) {
if ( typeof(doc[key]) == "object" && doc[key] !== null ) {
return recurSearchObj(doc[key], aLogic);
} else {
// invoke matching-logic
if( aLogic(key, doc[key]) ){
// then do somthing
}
}
});
}
// search document recursively
return recurSearchObj(this, logic);
});
Is there any way of making this function recursive so that I do not need to create a switch for each length of filter criteria ?
var data = [
{a:'aaa',b:'bbb',c:'ccc',d:'ddd',e:'eee'},
{a:'aaa',b:'bbb',c:'ccc',d:'eee',e:'fff'},
{a:'xxx',b:'bbb',c:'ccc',d:'ddd',e:'fff'}
]
function select(data,where){
return data.filter(function(e){
var k = Object.keys(where);
switch(k.length){
case 1: return (e[k[0]] == where[k[0]]);
case 2: return (e[k[0]] == where[k[0]] && e[k[1]] == where[k[1]]);
case 3: return (e[k[0]] == where[k[0]] && e[k[1]] == where[k[1]] && e[k[2]] == where[k[2]]);
case 4: return (e[k[0]] == where[k[0]] && e[k[1]] == where[k[1]] && e[k[2]] == where[k[2]] && e[k[3]] == where[k[3]]);
case 5: return (e[k[0]] == where[k[0]] && e[k[1]] == where[k[1]] && e[k[2]] == where[k[2]] && e[k[3]] == where[k[3]] && e[k[4]] == where[k[4]]);
}
})
}
var where = {a:'aaa',b:'bbb'}
console.log(select(data,where));
It doesn't need to be recursive (I'm not sure you understand what that means), you just need to loop on the elements in where:
function select(data, where) {
return data.filter(function(e) {
var k = Object.keys(where);
return k.every(function(key) {
return e[key] == where[key];
});
})
}
var data = [
{a:'aaa',b:'bbb',c:'ccc',d:'ddd',e:'eee'},
{a:'aaa',b:'bbb',c:'ccc',d:'eee',e:'fff'},
{a:'xxx',b:'bbb',c:'ccc',d:'ddd',e:'fff'}
]
var where = {a:'aaa',b:'bbb'}
console.log(select(data,where));
Try this code:
function select(data, where) {
return data.filter(function (e) {
for (var key in where) {
if (where.hasOwnProperty(key)) {
if (e.hasOwnProperty(key)) {
if (e[key] != where[key]) {
return false;
}
}
else {
return false
}
}
}
return true;
})
}