object-traversal icon indicating copy to clipboard operation
object-traversal copied to clipboard

Accessing parent properties on arrays (grandparent)

Open lveillard opened this issue 3 years ago • 6 comments

Nice lib! I just have a quick question of something i was not able to do. Imagine you have at some point a node like this:

{ 
 id: "1", 
 name: "Peter", 
 books: [ 
  {name: "Funny book"} , 
  {name: "Sad book"} 
  ]
 }

When the current value is the node {name:"funny Book"} accessing the parent gets the parent array and not the parent node. Is there a way to access the "id" property from the children?

📝It is possible to do it with the original object using getNodeByPath() but id is not there from the begining, I create it while I traverse the tree so i need a reference to the parent node that has been already extended

📝Also no way to get it from the array, because when the value is the array itself, key and parent are null

lveillard avatar Oct 22 '22 18:10 lveillard

Hi! Thanks for the feedback. I agree that being able to traverse up the tree is a nice feature. I intend to add it but can't give any timeframe atm.

In the meantime I believe your use case should be achievable like this (stackblitz):

const { traverse, getNodeByPath } = require('object-traversal');

const root = {
  // Commenting out id from the original example, since you say we are adding it dynamically
  // id: '1',
  name: 'Peter',
  books: [{ name: 'Funny book' }, { name: 'Sad book' }],
};

const isBookRegex = /books\.\d+$/;

console.log('root before: ', root);
traverse(root, ({ parent, key, value, meta }) => {
  // for example sake, we are adding id to any object which contains the 'books' property
  if (key === 'books') {
    parent.id = 1;
  }

  // if we are accessing a book, add the ownerId into it
  const isBook = isBookRegex.test(meta.nodePath);
  if (isBook) {
    const grandParentPath = meta.nodePath.split('.').slice(0, -2).join('.');
    const grandParent = getNodeByPath(root, grandParentPath);
    value.ownerId = grandParent.id; // value is book
  }
});
console.log('root after: ', root);

This outputs:

root before:  {
  name: 'Peter',
  books: [ { name: 'Funny book' }, { name: 'Sad book' } ]
}

root after:  {
  name: 'Peter',
  books: [
    { name: 'Funny book', ownerId: 1 },
    { name: 'Sad book', ownerId: 1 }
  ],
  id: 1
}

Also note that in your example when value is the array itself, parent and key are not null. Instead the parent is the root object and the key is 'books' (as demonstrated in the stackblitz).

Hope it helps!

kepelrs avatar Oct 23 '22 09:10 kepelrs

Thanks for the answer! When I've tried the getNodeByPath() it was getting the object before it was updated, but I guess it is because i'm using immer like this: return produce(root, (draft) => traverse(draft, myFunction));

I think I was unable to pass draft as a second parameter to MyFunction() but I guess I can just put the function directly there.

Will check it in more detail 👀, thanks!

lveillard avatar Nov 02 '22 00:11 lveillard

Btw before being able to go to grandparent etc I would add access to parent when Array.isArray(value) . Right now on those levels where we are an array, there is no direct access to parent, they are null

A really simple use case, imagine building a deepSort(root, fn) that will use object-traversal to traverse and sort every array it finds using a fn.

lveillard avatar Nov 02 '22 02:11 lveillard

Btw before being able to go to grandparent etc I would add access to parent when Array.isArray(value) . Right now on those levels where we are an array, there is no direct access to parent, they are null

There's nothing stopping you from accessing parent when value is an array. You can see this by running node array-parent.js in the stackblitz I provided. It is likely immer is what is messing with your implementation.

The only time parent is null, is when value is the root itself.

If I'm missing something, please provide a stackblitz example. Good luck!

kepelrs avatar Nov 03 '22 06:11 kepelrs

Thanks for the examples. So weird, i'm not able to reproduce it... but i'm using your lib a lot :) If i end up again facing the issue I will build a stackblitz to show you the issue. Thanks!

lveillard avatar Nov 04 '22 18:11 lveillard

Keeping this open as a reminder for the upward traversal feature.

kepelrs avatar Nov 09 '22 20:11 kepelrs