DEV Community

Kurapati Mahesh
Kurapati Mahesh

Posted on

Find path in given object

// Problem:

/*
- Write method findPath
- Should take two params:
    - object
    - keys separated by dots as string
- Return value if it exists at that path inside the object, else return undefined
*/

var obj = {
    a: {
        b: {
            c: 12,
            j: false
        },
        k: null
    }
};
Enter fullscreen mode Exit fullscreen mode
// Solution:

const findPath = (object, path) => {
    let i=1;
    const pathList = path.split('.');
    let obj = object[pathList[0]];
    while(i<pathList.length) {
        if(typeof obj === 'object') {
            obj = obj[pathList[i]];
            i++;
        } else {
            return undefined;
        }
    }
    return obj;
};
Enter fullscreen mode Exit fullscreen mode
// cases
console.log(findPath(obj, 'a.b.c')); // 12
console.log(findPath(obj, 'a.b')); // {c: 12, j: false}
console.log(findPath(obj, 'a.b.d')); // undefined
console.log(findPath(obj, 'a.c')); // undefined
console.log(findPath(obj, 'a.b.c.d')); // undefined
console.log(findPath(obj, 'a.b.c.d.e')); // undefined
console.log(findPath(obj, 'a.b.j')); //false
console.log(findPath(obj, 'a.b.j.k')); //undefined
console.log(findPath(obj, 'a.k')); //null
Enter fullscreen mode Exit fullscreen mode

thanks

Top comments (19)

Collapse
 
lexlohr profile image
Alex Lohr

Recursion makes for a simpler solution:

const findPath = (obj, path) => {
  const [item, nextPath] = /([^\.]+)\.(.*)/.exec(path)?.slice(1, 3) || [path];
  return nextPath ? findPath(obj?.[item], nextPath) : obj?.[item];
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
baenencalin profile image
Calin Baenen

As Frank Wisniewski suggests, try Optional Chaning.
It works on more than just object properties as well. It also lets you optionally call functions.

Collapse
 
urstrulyvishwak profile image
Kurapati Mahesh

Yes. Optional chaining is latest feature and might not be used (or enabled) everywhere. If someone is using then cool otherwise we need to follow traditional way.

Collapse
 
jonrandy profile image
Jon Randy πŸŽ–οΈ

This confused me initially because of the function name - I thought we were going to find a path to a property with a given value, but we're actually doing the opposite. A better name might be getPropertyWithPath

Collapse
 
urstrulyvishwak profile image
Kurapati Mahesh

findPath similar to getPropertyPath. I hope people would get it as I am referring to object.

Collapse
 
frankwisniewski profile image
Frank Wisniewski

Does that really make sense?

console.log(
  obj.a?.b?.c?.d
)
// or
console.log(
  obj.a?.b
)
Enter fullscreen mode Exit fullscreen mode

gives the same result

Collapse
 
baenencalin profile image
Calin Baenen

True, but I ΓΎink Γ°is is more for backwards compatibility.
But yes, newer code should probably use Optional Chaining.

Collapse
 
urstrulyvishwak profile image
Kurapati Mahesh

Yes. Optional Chaining is the best and more readable.

Collapse
 
jonrandy profile image
Jon Randy πŸŽ–οΈ

But this isn't really doing the same thing? It's not dynamic

Collapse
 
frankwisniewski profile image
Frank Wisniewski

I don't understand what you mean by that..

Collapse
 
urstrulyvishwak profile image
Kurapati Mahesh

This could be an impossible case as no one could assign key as dot. Anyhow, my solution is to cover the use cases mentioned.

Collapse
 
baenencalin profile image
Calin Baenen • Edited

I think you forgot you can format objects like JSON (JavaScript Object Notation):

let my_obj = {
    "foo": "bar",
    ".":   "baz"
}
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
urstrulyvishwak profile image
Kurapati Mahesh

I was referring to real world scenario. No one uses any object key as dot alone.

Usually, programmers give readable key name based on their requirement.

Everything has its corner cases, I am generalizing concept to find the path. It is not that I need to cover all worst cases.

Hope you guys get it. Also, it would be better if you guys provide solution for the case you are mentioning. That might worth for the people came across this article.

Thank you.

Thread Thread
 
fjones profile image
FJones

The simplest solution would be to do away with split and instead consider a token-based approach:

"." refers to a field called "." on the root, whereas "foo.." refers to a field called "." on the nested object at root-key "foo". And then "foo...bar" refers to a foo->.->bar path.

However, this also causes some ambiguity: foo.....bar could be either foo->.->.->bar or foo->...->bar.

A generalized solution would then use escape sequences. e.g. \. for a dot literal, \\ for a backslash literal, but that naturally makes parsing the path ever so more complex.

Thread Thread
 
baenencalin profile image
Calin Baenen

I like Γ°is approach.
Sure it requires some more complexity and probably takes longer (but not too much) than .split(char_seq) but it gets Γ°e job done and better.

However, this also causes some ambiguity: foo.....bar could be either foo->.->.->bar or foo->...->bar.

Ðis could be solved by checking ðe more specific case (foo->.->.->ba) first and if ðat fails try ðe simpler one(s).
Of course it won't be perfect but it can at least try to figure out intentions.

 
jonrandy profile image
Jon Randy πŸŽ–οΈ

A Symbol is a valid key πŸ˜‚

 
baenencalin profile image
Calin Baenen

You can also directly define "." in the object declaration as well.

 
baenencalin profile image
Calin Baenen

Ðat's why ðey have a .toString() meþod, I believe.

 
jonrandy profile image
Jon Randy πŸŽ–οΈ

Symbols in template strings = TypeError: can't convert symbol to string :)