I want to fetch the object from multi level structure
I written function for it but even on return its not coming out from function and returning value, its continue with next recursion. I know its returning value to the previously called function and as its scope is block its getting overridden and that's why returning undefined value
var selectedObj = findObjectByUid( existingStructure, selectedUid);
function findObjectByUid( root, selectedUid ) {
if( root.uniqueId === selectedUid ) {
return root;
}
if( root.children && root.children.length > 0 ) {
for( var k in root.children ) {
if( root.children[ k ].uniqueId === selectedUid ) {
return root.children[ k ];
} else if( root.children.length ) {
return findObjectByUid( root.children[ k ], selectedUid );
}
}
}
}
Here i want to get back to my initial calling function when it got matching uid.
Actually you return with the first child, regardless of the found node.
You could take a temporary variable and store the result of the children check and if not falsy return this value.
BTW, you could take the child directly of the array for the recursion.
function findObjectByUid(root, selectedUid) {
if (root.uniqueId === selectedUid) return root;
if (!root.children || !root.children.length) return;
for (let child of root.children) {
let temp = findObjectByUid(child, selectedUid);
if (temp) return temp;
}
}
var selectedObj = findObjectByUid(existingStructure, selectedUid);
There are three problems with using this approach on arrays. First, the for...in also iterates over an object's prototype properties if those properties are enumerable. For example:
Array.prototype.voice = "James Earl Jones";
var tMinus = [
"Two",
"One",
"Blast off!"
];
var countdown = "";
for (var step in tMinus) {
countdown += tMinus[step] + "\n";
}
console.log(countdown);
// => "Two
// One
// Blast Off!
// James Earl Jones
// "
That can be solved by using hasOwnProperty to exclude prototype properties.
Example:
for (var step in tMinus) {
if (tMinus.hasOwnProperty(step)) {
countdown += tMinus[step] + "\n";
}
}
Here are corrected code. You had used return findObjectByUid in inner calling by which code was terminating before completing loop.
function findObjectByUid( root, selectedUid ,foundArr) {
if( root.uniqueId === selectedUid ) {
foundArr.push(root);
return root;
}
else if( root.children && root.children.length > 0 ) {
for( var k in root.children ) {
findObjectByUid( root.children[k], selectedUid,foundArr );
if(root.children[k]=== selectedUid){break;}
}
}
return foundArr.length>0?foundArr[0]:null;
}
Sample json and calling method
var root = {uniqueId:1,children:[{uniqueId:10},{uniqueId:11,children:[{uniqueId:21,children:[]},{uniqueId:22,children:[]},{uniqueId:23,children:[{uniqueId:31,children:[]},{uniqueId:32,children:[]}]}]},{uniqueId:12,children:[]},{uniqueId:13,children:[]}]};
findObjectByUid(root,32,[]);
Related
I am trying to create a depth of children based on a passed parameter n.
Basically if n is 4, the resulting object should be
parent.children.children.children.children.
I've come up with this so far:
parent = {}
function makechildren( current, depth ){
current['children']={}
while (depth>0){ {
str = JSON.stringify(current)
return makechildren(current, depth-1)
}
}
}
makechildren(parent, 4)
I tested this code and it works
parent={};
var obj;
function makechildren( current, depth){
if(depth>0)
{
current = JSON.parse(JSON.stringify(current).replace('{}','{"children":{}}'))
makechildren(current, depth-1);
}else{
obj = current;
return ;
}
}
console.log(obj)
I have a first version of using a recursive function with Javascript which produces expected results. Below a working version :
// Call the recursive function and get final (a,b) results
var HitTemp = JSON.parse(JSON.stringify(HitCurrent));
var result= recursiveFunction(HitTemp, HitTemp.playerCurrent, maxNodes);
var a = HitTemp.coordPlayable[0];
var b = HitTemp.coordPlayable[1];
// Recursive function
function recursiveFunction(HitCurrent, colorCurrent, depth) {
// Indices
var i, j, k;
// Evaluation
var arrayTemp, eval, e;
// Set current color to HitCurrent
HitCurrent.playerCurrent = colorCurrent;
// Deep copy of arrayCurrent array
arrayTemp = JSON.parse(JSON.stringify(HitCurrent.arrayCurrent));
// If depth equal to 0
if (depth == 0)
return evaluation(HitCurrent);
// Starting evaluation
eval = -infinity;
// Browse all possible hits
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++) {
if (HitCurrent.arrayPlayable[i][j] == 'playable') {
for (k = 0; k < 8; k++) {
// Explore line started from (i,j) with direction "k"
exploreHitLine(HitCurrent, i, j, k, 'drawing');
}
// Recursive call
e = recursiveFunction(JSON.parse(JSON.stringify(HitCurrent)), ((JSON.stringify(HitCurrent.playerCurrent) == JSON.stringify(playerBlack)) ? playerWhite : playerBlack), depth-1);
if (e > eval) {
HitCurrent.coordPlayable = [i,j];
eval = e;
}
}
// Restore arrayCurrent array
HitCurrent.arrayCurrent = JSON.parse(JSON.stringify(arrayTemp));
}
return eval;
}
From this, I would like to use "inline" WebWorkers to dedicate the recursion to WebWorker and avoid hanging process in browsers.
I tried to follow this link and this other link
I don't know if I have to "postmessage" the object HitCurrent or the value eval to the main thread : by using WebWorker, I make confusions between the return instruction (which returns a value in the terminal case) and the objet HitCurrent argument passed for next recursive call.
If someone could give some clues to reproduce this original algorithm by using inline webworker (or with classical way of using webworker).
Inline webworker example:
As your code, there is no function evaluation, function exploreHitLine.
Before use following code, you must insert them into code.
{
let workerScript = URL.createObjectURL( new Blob( [ `
"use strict";
// Recursive function
function recursiveFunction( HitCurrent, colorCurrent, depth ) {
// Indices
var i, j, k;
// Evaluation
var arrayTemp, eval, e;
// Set current color to HitCurrent
HitCurrent.playerCurrent = colorCurrent;
// Deep copy of arrayCurrent array
arrayTemp = JSON.parse(JSON.stringify(HitCurrent.arrayCurrent));
// If depth equal to 0
if ( depth === 0 ) return evaluation(HitCurrent);
// Starting evaluation
eval = -infinity;
// Browse all possible hits
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++) {
if (HitCurrent.arrayPlayable[i][j] === 'playable') {
for (k = 0; k < 8; k++) {
// Explore line started from (i,j) with direction "k"
exploreHitLine(HitCurrent, i, j, k, 'drawing');
}
// Recursive call
e = recursiveFunction(JSON.parse(JSON.stringify(HitCurrent)), ((JSON.stringify(HitCurrent.playerCurrent) == JSON.stringify(playerBlack)) ? playerWhite : playerBlack), depth-1);
if (e > eval) {
HitCurrent.coordPlayable = [i,j];
eval = e;
}
}
// Restore arrayCurrent array
HitCurrent.arrayCurrent = JSON.parse(JSON.stringify(arrayTemp));
}
}
return eval;
}
onmessage = function ( event ) {
let params = event.data;
postMessage( { result: recursiveFunction( ...params ) } );
}
` ], { type: "plain/text" } ) );
// Call the recursive function and get final (a,b) results
new Promise( resolve => {
let HitTemp = JSON.parse(JSON.stringify(HitCurrent));
let firstWorker = new Worker( workerScript );
firstWorker.onmessage = function ( event ) {
resolve( event.data ); //{ result: XXX }
}
firstWorker.postMessage( HitTemp, HitTemp.playerCurrent, maxNodes );
} ).then( ( { result } ) => {
let [ a, b ] = result.coordPlayable;
console.log( result );
} );
}
Additionally following is working inline WebWorker:
{
let workerScript = URL.createObjectURL( new Blob( [ `
"use strict";
onmessage = function ( event ) {
let sum = 0, count = event.data;
for ( let i = 0; i < count**count; i++ ) {
sum += i;
}
postMessage( { result: sum, count } );
}
` ], { type: "plain/text" } ) );
let firstWorker = new Worker( workerScript );
let firstAlive = setTimeout( () => {
firstWorker.terminate();
console.log( "terminated" );
}, 3000 );
firstWorker.onmessage = function ( event ) {
clearTimeout( firstAlive );
console.log( event.data );
}
firstWorker.postMessage( 10 );
let secondWorker = new Worker( workerScript );
let secondAlive = setTimeout( () => {
secondWorker.terminate();
console.log( "terminated" );
}, 3000 );
secondWorker.onmessage = function ( event ) {
clearTimeout( secondAlive );
console.log( event.data );
}
secondWorker.postMessage( 5 );
}
Update 1.
{
// Inline webworker version
let workerScript = URL.createObjectURL( new Blob( [ `
"use strict";
// Recursive function
function recursiveFunction( HitCurrent, colorCurrent, depth ) {
// Indices
var i, j, k;
// Evaluation
var arrayTemp, evaluated, e;
// Set current color to HitCurrent
HitCurrent.playerCurrent = colorCurrent;
// Deep copy of arrayCurrent array
arrayTemp = JSON.parse(JSON.stringify(HitCurrent.arrayCurrent));
// If depth equal to 0
if (depth == 0)
return evaluation(HitCurrent);
// Starting evaluation
evaluated = -infinity;
// Browse all possible hits
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++) {
if (HitCurrent.arrayPlayable[i][j] == 'playable') {
for (k = 0; k < 8; k++) {
// Explore line started from (i,j) with direction "k"
exploreHitLine(HitCurrent, i, j, k, 'drawing');
}
// Recursive call
e = recursiveFunction(JSON.parse(JSON.stringify(HitCurrent)), ((JSON.stringify(HitCurrent.playerCurrent) == JSON.stringify(playerBlack)) ? playerWhite : playerBlack), depth-1);
if ( e > evaluated ) {
HitCurrent.coordPlayable = [i,j];
evaluated = e;
}
if (e == -infinity) { HitCurrent.coordPlayable = [ i, j ]; }
// Restore arrayCurrent array
HitCurrent.arrayCurrent = JSON.parse(JSON.stringify(arrayTemp));
}
}
}
return evaluated;
}
onmessage = function ( event ) {
let params = event.data;
//postMessage( { result: recursiveFunction( HitCurrent, HitCurrent.playerCurrent, maxNodes ) } );
postMessage( { result: recursiveFunction( ...params ) } );
};
` ], { type: "plain/text" } ) );
// Call the recursive function and get final (a,b) results
new Promise( resolve => {
let HitTemp = JSON.parse(JSON.stringify(HitCurrent));
let firstWorker = new Worker( workerScript );
firstWorker.onmessage = function ( event ) {
resolve( event.data ); //{ result: XXX }
}
firstWorker.postMessage( [ HitTemp, HitTemp.playerCurrent, maxNodes ] );
} ).then( ( { result } ) => {
let [ a, b ] = result.coordPlayable;
console.log( result );
} );
}
Explanation of my faults:
It is impossible to use "eval" as the name of a variable when in "strict mode".
=>
from: eval
to: evaluated
Worker.postMessage( aMessage, Transferrable ), in this case, you don't need to use the second parameter.
=>
from: firstWorker.postMessage( HitTemp, HitTemp.playerCurrent, maxNodes );
to: firstWorker.postMessage( [ HitTemp, HitTemp.playerCurrent, maxNodes ] );
(https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage)
Continue to 2, passing parameter to recursiveFunction is fixed.
eval is a keyword; use some other variable name.
postMessage only
accepts one argument (it also accepts a transferable, but that is not applicable to your case), and if that argument is not a primitive value, it should
be an object that can be serializable (e.g. you cannot pass functions
or methods directly to the web worker)
I might be misunderstanding what you are trying to do, but you might want to reconsider recursively spawning an unkown number of webworkers! Just spawn one webworker and pass the parameters of your function to it and do your recursive calculations inside that webworker synchronously without spawning a new worker, if you just want to free up your main stack. Spawning too many web workers will consume a lot of a resources and will actually slow down your calculations! FYI, spawning each new web worker takes ~40ms and takes up resources. This is a general observation with regard to utilizing multithreading for calculating recursive functions! This might help:
https://softwareengineering.stackexchange.com/questions/238729/can-recursion-be-done-in-parallel-would-that-make-sense
Concerning all functions called into URL.createObjectURL( new Blob( ... )) block, Have I got to include them in this block or can I write them outside of it (as you say for evaluation and explotHitLine functions) ?
Your webworker is a completely separate JS file and execution context; you will not have access to anything that is not defined within its context or send to it as a message.
Btw, there are a few errors in your code which will prevent it from compiling correctly anyways: infinity should be Infinity, playerBlack and playerWhite are undefined, etc.
This question already has answers here:
Accessing nested JavaScript objects and arrays by string path
(44 answers)
Closed 5 years ago.
How do i get object property using array of string (name of properties)? (the last element in array is the inner property of object)
See the code below:
Handy way:
let myObject = {
"property": {
"subproperty": {
"targetproperty": "Hi, We done it!"
}
}
};
let myString = "property:subproperty:targetproperty";
let parts = myString.split( ":" );
console.log( myObject[ parts[ 0 ] ][ parts[ 1 ] ][ parts[ 2 ] ] ); // Output: "Hi, We done it!"
Eval way:
let myObject = {
"property": {
"subproperty": {
"targetproperty": "Hi, We done it!"
}
}
};
let myString = "property:subproperty:targetproperty";
let parts = myString.split( ":" );
let code = "myObject";
for ( let i = 0; i < parts.length; i++ ) {
code += "['" + parts[ i ] + "']";
}
code += ";";
console.log( code );
console.log( eval( code ) ); // Output: "Hi, We done it!"
Eval is evil. so i need a cleaner way to do it.
How do i do it without eval and handy job?
You can use .reduce():
let myObject = {
"property": {
"subproperty": {
"targetproperty": "Hi, We done it!"
}
}
};
let myString = "property:subproperty:targetproperty";
let value = myString.split(":").reduce(function(obj, prop) {
return obj && obj[prop];
}, myObject);
console.log(value);
For loop:
function getByValue(arr, value) {
for (var i=0, iLen=arr.length; i<iLen; i++) {
if (arr[i].b == value) return arr[i];
}
}
.filter
function getByValue2(arr, value) {
var result = arr.filter(function(o){return o.b == value;} );
return result? result[0] : null; // or undefined
}
.forEach
function getByValue3(arr, value) {
var result = [];
arr.forEach(function(o){if (o.b == value) result.push(o);} );
return result? result[0] : null; // or undefined
}
If, on the other hand you really did mean for..in and want to find an object with any property with a value of 6, then you must use for..in unless you pass the names to check. e.g.
function getByValue4(arr, value) {
var o;
for (var i=0, iLen=arr.length; i<iLen; i++) {
o = arr[i];
for (var p in o) {
if (o.hasOwnProperty(p) && o[p] == value) {
return o;
}
}
}
}
You can use a reduce solution:
var obj = {prop1: {prop2: {prop3: 'xpto'}}};
var props = ['prop1','prop2','prop3'];
var result = props.reduce((acc,val)=>acc[val],obj);
console.log(result);
The recursive way ;)
Create a function that takes the current property, allparts and index.
Start with zero, return call to next index, try read and return with next call and increment index untill there are no more props to read/extract then return the value you got as the current property.
Let me know if you need working code
Here is a recursive approach, it will return undefined if the property isn't found:
const getPath = (o, keyPath, delimiter = '.') => {
if (Array.isArray(keyPath)) {
keyPath = keyPath.join(delimiter)
}
// o might not be an object when called recursively
if(Object(o) === o) {
let keys = keyPath.split(delimiter);
let key = keys.shift();
if(o.hasOwnProperty(key)) {
if(keys.length) {
// there are more keys to check, call with attribute and remaining keys
return getPath(o[key], keys.join(delimiter), delimiter);
} else {
// no more keys to check and object does have property
return o[key];
}
}
// didn't early return from having the key above, object does not have property
return undefined;
} else if(keyPath.length === 0) {
// o is not an object, but there is no remaining keyPath, so we will assume we've unwound the stack
return o;
}
// not an object and keyLength is non-zero, object does not contain property
return undefined;
};
let myObject = {
"property": {
"subproperty": {
"targetproperty": "Hi, We done it!"
}
}
};
console.log(getPath(myObject, "property:subproperty:targetproperty", ":"));
You can loop through your parts array, accessing the value of each key in each iteration.
function valueFromPath(obj, path) {
for (var i = 0; i < path.length; ++i) {
obj = obj[path[i]];
}
return obj;
};
valueFromPath(myObject, parts);
You probably want to clone the object first, in case you are going to use it for something else.
Alternatively, you can use traverse. Specifically traverse#getpath.
traverse(myObject).get(parts);
You may do as follows;
function getNestedValue(o,...a){
var val = o;
for (var prop of a) val = typeof val === "object" &&
val !== null &&
val[prop] !== void 0 ? val[prop]
: undefined;
return val;
}
let myObject = {
"property": {
"subproperty": {
"targetproperty": "Hi, We done it!"
}
}
};
let myString = "property:subproperty:targetproperty";
console.log(getNestedValue(myObject, ...myString.split(":")));
I have the following code
$.map( [ 0, 1, 2 ], function( n ) {
return n > 0 ? n + 1 : null;
});
Out Put: [ 2, 3 ]
I know $.map Translate all items in an array or object to new array of items.(Documentation).
What I want to know how call back function in a .map works(internal implentation)?
One possible answer could be
.map has some loop which passes each element of array to call back method, that return some value.
.map manage each value return from call back method.In this case push in some internal array.
At the end of loop .map return array.
EDIT
But I am not sure how it works, is it works as I explained??
But I am not sure is this how it works??
Yes, that's basically how it works. Full details, as always, in the source code (that line number will rot over time...). Currently, it looks like this:
map: function( elems, callback, arg ) {
var value,
i = 0,
length = elems.length,
isArray = isArraylike( elems ),
ret = [];
// Go through the array, translating each of the items to their new values
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret.push( value );
}
}
// Go through every key on the object,
} else {
for ( i in elems ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret.push( value );
}
}
}
// Flatten any nested arrays
return concat.apply( [], ret );
},
Well, there is no better way to check than looking at the source code
function (elems, callback, arg) {
var value, i = 0,
length = elems.length,
isArray = isArraylike(elems),
ret = [];
// Go through the array, translating each of the items to their new values
if (isArray) {
for (; i < length; i++) {
value = callback(elems[i], i, arg);
if (value != null) {
ret.push(value);
}
}
// Go through every key on the object,
} else {
for (i in elems) {
value = callback(elems[i], i, arg);
if (value != null) {
ret.push(value);
}
}
}
// Flatten any nested arrays
return concat.apply([], ret);
}
Yes, whether its an Array version or object, it is looping and calling the callback to set the value
Yes, for both loops, the value is pushed
Yes, it is returning a flattened array by calling concat
I have a bunch of filter criteria stored in an object. The criteria changes from time to time, so I can't have a static filter (ie: price > 5 && price < 19 && ...).
var criteria = {
price: {
min: 5,
max: 19
},
age: {
max: 35
}
};
I then have a loop setup to filter through an array based on the criteria and return the filtered array:
var filtered = [];
var add = true;
for (var i=0; i < data.length; i++ ){
add = true;
var item = data[i];
for (var main in criteria){
for (var type in criteria[main] ){
if ( type === 'min') {
if ( !(item[main] > criteria[main][type]) ) {
add = false;
break;
}
} else if ( type === 'max') {
if ( !(item[main] < criteria[main][type]) ) {
add = false;
break;
}
}
}
}
if (add) {
filtered.push(item);
}
}
Is there a more efficient way to setup the filter conditionals ahead of time (ie: item.price > 5 && item.price < 19 && item.age < 35) and then filter the array? As opposed to what I'm currently doing and referencing the object during each array loop - which is inefficient with all the conditionals and sub-loops.
See my jsbin - http://jsbin.com/celin/2/edit .
i would use Array.prototype.filter:
var filtered = data.filter(function (item) {
var main, critObj;
for (main in criteria) {
critObj = criteria[main];
if (critObj.min && critObj.min >= item[main]) {
return false;
}
if (critObj.max && critObj.max <= item[main]) {
return false;
}
}
return true;
});
return falseif it should not be included in your filtered list. inside the for-loop, the function just checks if the criteria has a min, and if if this is bigger than the same property in the array item. if so, it just returns false for this element (the same of course for the max-property).
if both fit, the function returns true, and i will be included in your filtered list!
edit: now with fixed bin
I've been working on the Ramda library, and using it to do this is fairly straightforward:
var test = R.allPredicates(R.reduce(function(tests, key) {
var field = criteria[key];
if ('min' in field) {tests.push(R.pipe(R.prop(key), R.gt(R.__, field.min)));}
if ('max' in field) {tests.push(R.pipe(R.prop(key), R.lt(R.__, field.max)));}
return tests;
}, [], R.keys(criteria)));
console.log( 'filtered array is: ', data.filter(test) );
(also available in this JSBin.)
To do this without a library, I converted the code above to into a library-less version, and it's a bit more complicated, but still readable:
var test = (function(criteria) {
var tests = Object.keys(criteria).reduce(function(tests, key) {
var field = criteria[key];
if ('min' in field) {tests.push(function(item) {
return item[key] > field.min;
});}
if ('max' in field) {tests.push(function(item) {
return item[key] < field.max;
});}
return tests;
}, []);
return function(item) {
return tests.every(function(test) {return test(item);});
};
}(criteria));
console.log( 'filtered array is: ', data.filter(test) );
(JSBin)
In either version, the criteria is parsed once to create a set of predicate functions. Those functions are combined into a single predicate which is passed as a filter.