Marco
Marco

Reputation: 2463

Add path to a JSON property

Good evening.

I have the following JSON. Where I'd like path to contain the position in the tree using the url property and keeping in mind it can be nested deep. I have added some comments but I'm aware this is never possible in plain JSON. :)

[
  {
    "url": "articles",
    "path": "...", // should become path: "articles"
    "subitems": [
      {
        "url": "article-one",
        "path": "...",  // should become path: "articles/article-one"
        "subitems": []
      },
      {
        "url": "article-two",
        "path": "...", 
        "subitems": []
      },
    ]
  },
  {
    "url": "homepage",
    "path": "...",
    "subitems": []
  },
  {
    "url": "home",
    "path": "...",    // should become path: "home"
    "subitems": [
      {
        "url": "maintenance",
        "path": "...",     // should become path: "home/maintenance"
        "subitems": []
      },
      {
        "url": "customer-service",
        "path": "...",
        "subitems": []
      },
      {
        "url": "news",
        "path": "...", // should become path: "home/news"
        "subitems": [
          {
            "url": "news-item-1", // should become path: "home/news/news-item-1"
            "path": "...",
            "subitems": []
          }
        ]
      }
    ]
  }
]

Can someone help me with this?

Thank you so much!

Upvotes: 0

Views: 320

Answers (2)

vincent
vincent

Reputation: 2181

Here is an iterative solution using object-scan

// const objectScan = require('object-scan');

const data = [{ url: 'articles', subitems: [{ url: 'article-one', subitems: [] }, { url: 'article-two', subitems: [] }] }, { url: 'homepage', subitems: [] }, { url: 'home', subitems: [{ url: 'maintenance', subitems: [] }, { url: 'customer-service', subitems: [] }, { url: 'news', subitems: [{ url: 'news-item-1', subitems: [] }] }] }];

const inject = objectScan(['**(^subitems$).url'], {
  useArraySelector: false,
  rtn: 'count',
  filterFn: ({ parents }) => {
    parents[0].path = parents
      .reverse()
      .filter((e) => !Array.isArray(e))
      .map(({ url }) => url)
      .join('/');
  }
});

console.log(inject(data));
// => 9

console.log(data);
// => [ { url: 'articles', subitems: [ { url: 'article-one', subitems: [], path: 'articles/article-one' }, { url: 'article-two', subitems: [], path: 'articles/article-two' } ], path: 'articles' }, { url: 'homepage', subitems: [], path: 'homepage' }, { url: 'home', subitems: [ { url: 'maintenance', subitems: [], path: 'home/maintenance' }, { url: 'customer-service', subitems: [], path: 'home/customer-service' }, { url: 'news', subitems: [ { url: 'news-item-1', subitems: [], path: 'home/news/news-item-1' } ], path: 'home/news' } ], path: 'home' } ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/[email protected]"></script>

Disclaimer: I'm the author of object-scan

Upvotes: 0

Nenad Vracar
Nenad Vracar

Reputation: 122057

You could do this with recursive function and add path property when the current element in the for...in loop is object (typeof object and not Array.isArray)

const data = [{"url":"articles","subitems":[{"url":"article-one","subitems":[]},{"url":"article-two","subitems":[]}]},{"url":"homepage","subitems":[]},{"url":"home","subitems":[{"url":"maintenance","subitems":[]},{"url":"customer-service","subitems":[]},{"url":"news","subitems":[{"url":"news-item-1","subitems":[]}]}]}]

function addPath(data, prev = '') {
  for (let i in data) {
    if (typeof data[i] === 'object') {
      let path = prev;

      if (!Array.isArray(data[i])) {
        data[i].path = path += (path ? '/' : '') + data[i].url
      }

      addPath(data[i], path)
    }
  }
}

addPath(data);
console.log(data)

Upvotes: 1

Related Questions