简体   繁体   中英

Restructure nested JSON data by tags - Javascript

I have a JSON file with post topics:

"topics": [
  {
    "id": "9551",
    "tags": ["tag1", "tag2"],
    "title": "title"
   },
   {
    "id": "9552",
    "tags": ["tag3"],
    "title": "title2"
   },
   {
    "id": "9553",
    "tags": ["tag1"],
    "title": "title3"
   }
]

I would like to sort these posts by tag, something like this:

 "tags" : {
     "tag1": [
         { "id": "9951",
           "title": "title"
          },
          {
          "id": "9553",
          "title": "title3"
          }
     ],
     "tag2": [
        {
        "id": "9551",
        "title": "title"
       }
      ]
    }

I'm not sure if the above is valid, but essentially I want a valid JSON output of the original data, only sorted by tag.

Thanks for any suggestions!

You could use reduce to group the tags. Create an accumulator with each tag as a key . Inside reduce, loop through each tags . If a tag is already added, push the { id, title } object to the array. Else, create a new array for that tag and then push it.

 const topics=[{"id":"9551","tags":["tag1","tag2"],"title":"title"},{"id":"9552","tags":["tag3"],"title":"title2"},{"id":"9553","tags":["tag1"],"title":"title3"}] const group = topics.reduce((acc, { id, tags, title }) => { tags.forEach(t => { acc[t] = acc[t] || []; acc[t].push({ id, title }) }) return acc; }, {}) console.log({ tags: group }) 

Another version, which doesn't fix the output structure to just {id, title} , keeping what's there and removing only tags from each object might look like this:

 const byTag = (topics) => topics.reduce( (a, {tags = [], ...rest}) => tags.reduce( (a, tag) => ({...a, [tag] : [...(a[tag] || []), rest]}), a ), {} ) const topics= [{id: "9551", tags: ["tag1", "tag2"], title: "title"}, {id: "9552", tags: ["tag3"], title: "title2"}, {id: "9553", tags: ["tag1"], title: "title3"}]; log ( byTag (topics) ) console .log ( byTag (topics) ) 
 <script> const log = (obj) => console.log(JSON.parse(JSON.stringify(obj))) </script> 

But if you're going to do this, it's barely a step further to make this generic, so that it works on any common property holding an array of strings:

 const groupByProp = (name) => (xs) => xs.reduce( (a, {[name]: n = [], ...rest}) => n.reduce( (a, x) => ({...a, [x] : [...(a[x] || []), rest]}), a ), {} ) const byTag = groupByProp ('tags') const topics= [{id: "9551", tags: ["tag1", "tag2"], title: "title"}, {id: "9552", tags: ["tag3"], title: "title2"}, {id: "9553", tags: ["tag1"], title: "title3"}]; log ( byTag (topics) ) console .log ( byTag (topics) ) 
 <script> const log = (obj) => console.log(JSON.parse(JSON.stringify(obj))) </script> 

The other advantage of this technique is that the items in the new data structure are not repeated when they have multiple tags. Instead, each is a reference to the same object. You can see what I'm talking about by comparing the two different logged outputs in each sample. One of them shows the data structure as JSON.stringify might display it. The second, the default for the StackOverflow script editor, notes shared references between objects.

Here we can see that tags1 and tags3 both contain a reference to the single output object with id 9551. That may or may not be important to your code, but it's nice to know that this is done here.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM