How to define schema for recursive model with Normalizr
Asked Answered
I

2

17

Having a bit of an issue trying to normalise a payload, that contains a nested schema of the same type as the parent using Normalizr

For example I have the initial object (menu) which has a child (sections) which is an array of objects (section), which can go n deep.

{
  id: 123,
  sections: [{
    id: 1,
    sections:[{ id: 4, sections: [ id: 5, sections: [] ] }]
  }, {
    id: 2,
    sections:[]
  }, {
    id: 3,
    sections:[]
  }]
}

I started by creating a menu schema, that had sections in the definition that linked to a sections schema, that worked for the first pass, but then wouldn't handle children of sections, so I added a subsequent definition within the section schema with the same name (was worth a shot) but it didn't work.

const section = new schema.Entity('sections')

const sections = new schema.Entity('sections', {
  sections: section
})

const menu = new schema.Entity('menu', { 
  sections: [ sections ]
})

section.define({ sections })

I'm hoping to end up with the object below:

{
  entities: {
    menu: {
      sections: [1, 2, 3]
    },
    sections: [{
      1: { id: 1, sections: [4] },
      2: { id: 2, sections: [] },
      3: { id: 3, sections: [] },
      4: { id: 4, sections: [5] },
      5: { id: 5, sections: [] },
    }]
  }
}
Iseult answered 6/1, 2017 at 13:13 Comment(3)
Did that not work? This looks like a newer syntax than I have used, but I don't see why it would not work. – Liederkranz
I wouldn't have posted a question to SO if it did work! :D – Iseult
Good point, could it be because the session names are the same? Have you tried defining and using self in shape. – Liederkranz
E
29

Your sections schema should be an Array.

const section = new schema.Entity('sections')
const sections = new schema.Array(section);
section.define({ sections });
const menu = new schema.Entity('menu', { sections });

Then, in using it...

const data = {
  id: 123,
  sections: [{
    id: 1,
    sections:[{ id: 4, sections: [ { id: 5, sections: [] } ] }]
  }, {
    id: 2,
    sections:[]
  }, {
    id: 3,
    sections:[]
  }]
};

normalize(data, menu)

Will return:

{
  "entities": {
    "sections": {
      "1": { "id": 1, "sections": [ 4 ] },
      "2": { "id": 2, "sections": [] }, 
      "3": { "id": 3, "sections": [] },
      "4": { "id": 4, "sections": [ 5 ] },
      "5": { "id": 5, "sections": [] }
    },
    "menu": {
      "123": { "id": 123, "sections": [ 1, 2, 3 ] }
    }
  },
  "result": 123
}
Enswathe answered 6/1, 2017 at 15:46 Comment(4)
Thanks Paul, totally overlooked schema.Array. Sorry for posting in Gitub issues too! Appreciate the quick response, have an awesome weekend 🍻 – Iseult
Thank you for this useful post. What if top-level object data is an array of sections, how to merge that array into a same 'entities.sections' object? Would not like to have menu in my case. – Spectrograph
@EliBaird check the answer I added, hope it helps. – Spectrograph
Awesome example! However, I have some custom section ids, let's call them sectionId instead of just id. On schema.Entity() it is possible to add an option parameter with ìdAttribute` to specify a custom id, but it doesn't seem possible on schema.Array, which results in your example giving undefined id values. Does anyone know how to get your example of recursive data to work with custom ids? So far I've accomplished it by sections.schema._idAttribute = "sectionId" but it seems kind of hacky to access the private prop this way. – Incardination
S
0

If someone has the case of nested objects of same "type", for example "sections" and top level structure is array of "sections" too, like this:

const data = [
    {
      id: 1,
      sections:[{ id: 4, sections: [ { id: 5, sections: [] } ] }]
    }, 
    {
      id: 2,
      sections:[]
    }, 
    {
      id: 3,
      sections:[]
    }
  ]

here one way to "unnest" them:

import {schema, normalize} from "normalizr";

const child = new schema.Entity("sections");
const sections = new schema.Array(child);
child.define({sections});

const topLevel = new schema.Entity("sections", {
    sections
});

const customSchema = [topLevel];

console.log(normalize(data, customSchema));

What you will get is:

{
   "entities":{
      "sections":{
         "1":{
            "id":1,
            "sections":[
               4
            ]
         },
         "2":{
            "id":2,
            "sections":[

            ]
         },
         "3":{
            "id":3,
            "sections":[

            ]
         },
         "4":{
            "id":4,
            "sections":[
               5
            ]
         },
         "5":{
            "id":5,
            "sections":[

            ]
         }
      }
   },
   "result":[
      1,
      2,
      3
   ]
}
Spectrograph answered 10/12, 2018 at 20:31 Comment(0)

© 2022 - 2024 β€” McMap. All rights reserved.