3
\$\begingroup\$

I have a code snippet that I have been using and works. It is used a lot in my application and I need to speed it up. Is there a faster better way of deep accessing objects using a string path. It needs to able to access properties and methods, see my examples below:

     function get(obj, path) {
        var paths = path.split('.'),
        curProp = obj;
        for(var i=0;i<paths.length;i++){
            if (!curProp[paths[i]]) return 
            curProp = (typeof curProp[paths[i]] !== "function") ? curProp[paths[i]] : curProp[paths[i]]() ;
        }
        return curProp;
    }

The snippet gets the property via the path for the object passed

var obj = {contact:{name:"john"}};
console.log(get(obj, "contact.name"));

If the object has a method that returns an object it can return that too

var obj = {contact:function(){return {name:"john"}}};
console.log(get(obj, "contact.name"));
\$\endgroup\$
9
  • \$\begingroup\$ Perhaps this would do? return path.split('.').reduce((a, k) => a !== undefined && (typeof a === 'function' ? a()[k] : a[k]), obj) \$\endgroup\$
    – elclanrs
    Commented Dec 27, 2017 at 17:02
  • \$\begingroup\$ I ran some tests on jsperf my code is slightly faster, which is suprising \$\endgroup\$
    – MartinWebb
    Commented Dec 27, 2017 at 17:24
  • \$\begingroup\$ A for loop is likely to be faster than reduce. Although, "faster" at this level of optimization often doesn't matter. I'd go with what you think reads better. \$\endgroup\$
    – elclanrs
    Commented Dec 27, 2017 at 17:37
  • 2
    \$\begingroup\$ Typo in your question. The example calls get("contact.name",obj) have the arguments in the wrong order. You can not speed the function up unless you use some very hacky methods. Eg flatten the loop so it runs iterations inline, and use a unique function property to determine the function as typeof is slower eg prop = curProp[paths[i]].call ? curProp[paths[i]]() : curProp[paths[i]]; but the improvements are tiny at best. \$\endgroup\$
    – Blindman67
    Commented Dec 27, 2017 at 17:50
  • \$\begingroup\$ @elclanrs the question is can my code be optimized. And I get the impression the answer is no. I have ran a js perf test of a solution using reduce and my code is slightly faster. I'm not looking for tiny improvements I'm merely asking is their a faster, more concise way of doing it, since the code was written, 5 years ago. \$\endgroup\$
    – MartinWebb
    Commented Dec 27, 2017 at 17:51

1 Answer 1

1
\$\begingroup\$

If we're talking , I'd use .forEach() instead of a for loop. It abstracts the iteration process.


if (!curProp[paths[i]]) return does not seem quite right to me, because it will work funny with falsy values. It's better to rely on object.hasOwnProperty(), or Object.keys() depending on the way you want it to work.

Example:

var obj = {};
obj["property1"] = 0;
obj["property2"] = "";

obj["property1"] ? obj["property1"] : "No property1 is available";
  // --> "No property1 is available", isn't right from the humans perspective noramally

obj["property2"] ? obj["property2"] : "No property2 is available";
  // --> "No property2 is available"

I think, it's always good to spell out the return value. In other words, return undefined is better than return in this function.


Slightly different variant:

function get(targetObject, targetPropertyPath) {
    const pathPartList = targetPropertyPath.split('.');
    let currentProperty = targetObject;

    pathPartList.forEach(pathPart => {
        if (!currentProperty.hasOwnProperty(pathPart)) return undefined;

        const property = currentProperty[pathPart];
        currentProperty = (typeof property !== 'function') ? property : property();
    });

    return currentProperty;
}
\$\endgroup\$
6
  • \$\begingroup\$ Runs slower, it seems the original code is fast, though maybe not politically correct for design. Thanks for pointing out the errors will look into this, and re-test. \$\endgroup\$
    – MartinWebb
    Commented Dec 27, 2017 at 18:33
  • \$\begingroup\$ Not in the title i agree but it is mentioned: "It is used a lot in my application and I need to speed it up" \$\endgroup\$
    – MartinWebb
    Commented Dec 27, 2017 at 18:44
  • \$\begingroup\$ You're right, I missed that part. My bad. Anyway, at least I pointed out the correctness issue which is more important. Reversed my vote... \$\endgroup\$ Commented Dec 27, 2017 at 18:49
  • \$\begingroup\$ Yes and you are right. I have modified the tags so it is clear. Sorry for any misunderstanding and thank you again for correcting this. \$\endgroup\$
    – MartinWebb
    Commented Dec 27, 2017 at 18:50
  • \$\begingroup\$ @MartinWebb it runs slower because of the .forEach it's definitely not as performant compared to a for loop. \$\endgroup\$ Commented Dec 27, 2017 at 19:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.