skip to Main Content

I’m trying to learn Normalization concept using object. I’m having doubt in that

Normal Array of Object:

const state = {
users: [
    { id: 1, name: "Alice", posts: [{ id: 101, title: 'Post 1', comments: [{ id: 202, text: 'comment 2' }, { id: 201, text: 'comment 1' }] }] },
    { id: 2, name: "Bob", posts: [{ id: 102, title: 'Post 2', comments: [{ id: 202, text: 'comment 2' }] }] }
],
tags: [
    { id: 301, name: 'Tech', posts: [{ id: 101, title: 'Post 1' }, { id: 102, title: 'Post 2' }] },
     
]}

Normalized structure

const state1 = {
users: {
    byIds: {
        1: { id: 1, name: "Alice", posts: [101] },
        2: { id: 2, name: "Bob", posts: [102] }
    },
    allIds: [1, 2]
},
posts: {
    byIds: {
        101: { id: 101, title: 'Post 1', comments: [201] },
        102: { id: 102, title: 'Post 2', comments: [202] }
    },
    allIds: [101, 102]
},
comments: {
    byIds: {
        201: { id: 201, text: 'comment 1' },
        202: { id: 202, text: 'comment 2' }
    },
    allIds: [201, 202]
},
tags: {
    byIds: {
        301: { id: 301, name: 'Tech', posts: [101, 102] },
        302: { id: 302, name: 'Travel', posts: [102] }
    },
    allIds: [301, 302]
}}

Now, inside normalized structure

state1.tags.byIds[‘302’] = { id : 302, name: ‘Travel’ , posts:[102] }

inside posts [102] value it has,

state1.posts.byIds[‘102’] = { id : 102 , title: ‘Post 2’, comments: [202] }

inside comments[202] value will be like this

state1.posts.comments[‘202’] = { id : 202, title: ‘comment 2’ }


so the normalised output of tags[302] value will be

{ id: 302, name: 'Travel', posts: { id: 102, title: 'Post 2', comments: { id: 202, text: 'comment 2' } } }

but in the non-normalized structure tags 302 value will be

{ id: 302, name: 'Travel', posts: [{ id: 101, title: 'Post 1' }] } 

This both output is not same, how to normalize it correctly to get the same output ?

2

Answers


  1. You can use normalizr package, it

    Normalizes nested JSON according to a schema

    This package is mentioned by Normalizing Nested Data documentation. For more info, see api docs.

    The solution for your question:

    import { schema, normalize, denormalize } from 'normalizr';
    
    const state = {
      users: [
        {
          id: 1,
          name: 'Alice',
          posts: [
            {
              id: 101,
              title: 'Post 1',
              comments: [
                { id: 202, text: 'comment 2' },
                { id: 201, text: 'comment 1' },
              ],
            },
          ],
        },
        { id: 2, name: 'Bob', posts: [{ id: 102, title: 'Post 2', comments: [{ id: 202, text: 'comment 2' }] }] },
      ],
      tags: [
        {
          id: 301,
          name: 'Tech',
          posts: [
            { id: 101, title: 'Post 1' },
            { id: 102, title: 'Post 2' },
          ],
        },
        {
          id: 302,
          name: 'Travel',
          posts: [{ id: 102, title: 'Post 2' }],
        },
      ],
    };
    
    const comment = new schema.Entity(
      'comments',
      {},
      {
        processStrategy: (value, parent) => {
          return { ...value, post: parent.id };
        },
      },
    );
    const post = new schema.Entity('posts', { comments: new schema.Array(comment) });
    const user = new schema.Entity('users', { posts: new schema.Array(post) });
    const tag = new schema.Entity('tags', { posts: new schema.Array(post) });
    
    const responseSchema = new schema.Object({ users: new schema.Array(user), tags: new schema.Array(tag) });
    
    const normalizedData = normalize(state, responseSchema);
    
    console.log('normalizedData: ', JSON.stringify(normalizedData, null, 2), 'n');
    
    const tagId = '302';
    const denormalizedData = denormalize({ tags: [tagId] }, responseSchema, normalizedData.entities);
    console.log('denormalizedData: ', JSON.stringify(denormalizedData, null, 2), 'n');
    console.log('tag[302]: ', JSON.stringify(denormalizedData.tags[0], null, 2));
    

    Logs:

    normalizedData:  {
      "entities": {
        "comments": {
          "201": {
            "id": 201,
            "text": "comment 1",
            "post": 101
          },
          "202": {
            "id": 202,
            "text": "comment 2",
            "post": 102
          }
        },
        "posts": {
          "101": {
            "id": 101,
            "title": "Post 1",
            "comments": [
              202,
              201
            ]
          },
          "102": {
            "id": 102,
            "title": "Post 2",
            "comments": [
              202
            ]
          }
        },
        "users": {
          "1": {
            "id": 1,
            "name": "Alice",
            "posts": [
              101
            ]
          },
          "2": {
            "id": 2,
            "name": "Bob",
            "posts": [
              102
            ]
          }
        },
        "tags": {
          "301": {
            "id": 301,
            "name": "Tech",
            "posts": [
              101,
              102
            ]
          },
          "302": {
            "id": 302,
            "name": "Travel",
            "posts": [
              102
            ]
          }
        }
      },
      "result": {
        "users": [
          1,
          2
        ],
        "tags": [
          301,
          302
        ]
      }
    }
    
    denormalizedData:  {
      "tags": [
        {
          "id": 302,
          "name": "Travel",
          "posts": [
            {
              "id": 102,
              "title": "Post 2",
              "comments": [
                {
                  "id": 202,
                  "text": "comment 2",
                  "post": 102
                }
              ]
            }
          ]
        }
      ]
    }
    
    tag[302]:  {
      "id": 302,
      "name": "Travel",
      "posts": [
        {
          "id": 102,
          "title": "Post 2",
          "comments": [
            {
              "id": 202,
              "text": "comment 2",
              "post": 102
            }
          ]
        }
      ]
    }
    
    Login or Signup to reply.
  2. You could build by having a look to objects and arrays as properties.

    const
        normalize = (object, result = {}, target = {}) => {
            Object
                .entries(object)
                .forEach(([key, value]) => {
                    if (Array.isArray(value)) {
                        result[key] ??= { byIds: {}, allIds: [] };
                        target[key] = value.map(({ id, ...o }) => {
                            if (!(id in result[key].byIds)) {
                                normalize(o, result, result[key].byIds[id] = { id });
                                result[key].allIds.push(id);
                            }
                            return id;
                        });
                    } else {
                        target[key] = value;
                    }
                });
            return result;
        },
        state = { users: [{ id: 1, name: "Alice", posts: [{ id: 101, title: 'Post 1', comments: [{ id: 202, text: 'comment 2' }, { id: 201, text: 'comment 1' }] }] }, { id: 2, name: "Bob", posts: [{ id: 102, title: 'Post 2', comments: [{ id: 202, text: 'comment 2' }] }] }], tags: [{ id: 301, name: 'Tech', posts: [{ id: 101, title: 'Post 1' }, { id: 102, title: 'Post 2' }] } ]},
        result = normalize(state);
    
    console.log(result);
    .as-console-wrapper { max-height: 100% !important; top: 0; }
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search