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: [] },
    }]
  }
}
tutts
  • 2,373
  • 2
  • 20
  • 24
  • Did that not work? This looks like a newer syntax than I have used, but I don't see why it would not work. – kwelch Jan 06 '17 at 14:20
  • I wouldn't have posted a question to SO if it did work! :D – tutts Jan 06 '17 at 15:09
  • Good point, could it be because the session names are the same? Have you tried defining and using self in shape. – kwelch Jan 06 '17 at 15:11

2 Answers2

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
}
Paul Armstrong
  • 7,008
  • 1
  • 22
  • 36
  • 1
    Thanks Paul, totally overlooked `schema.Array`. Sorry for posting in Gitub issues too! Appreciate the quick response, have an awesome weekend – tutts Jan 06 '17 at 16:08
  • 1
    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. – zarcode Aug 14 '18 at 13:57
  • @EliBaird check the answer I added, hope it helps. – zarcode Dec 10 '18 at 20:51
  • 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. – kuhr Jul 16 '19 at 10:35
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
   ]
}
zarcode
  • 2,465
  • 17
  • 31