Hone logo
Hone
Problems

Normalize Nested User Data in React

You're building a feature to display a list of users, where each user has a list of their associated posts. Your application's state management strategy requires data to be normalized. This challenge will test your ability to transform a nested data structure into a normalized format, making it easier to manage and query within your React application.

Problem Description

Your task is to create a function that takes a deeply nested array of user objects, where each user object contains an array of their posts, and transforms it into a normalized state structure. This normalized structure should consist of two main parts:

  1. users object: An object where keys are user IDs and values are user objects (without their posts).
  2. posts object: An object where keys are post IDs and values are post objects.

This normalization is crucial for efficient data fetching, updates, and rendering in React applications, as it avoids data duplication and simplifies relationships.

Key Requirements:

  • The function should accept an array of User objects.
  • Each User object has an id, name, and an array of Post objects.
  • Each Post object has an id, title, and userId (linking it back to the user).
  • The output should be an object with two properties: users and posts.
  • The users property should be an object mapping user IDs to user data (excluding the posts array from the original user object).
  • The posts property should be an object mapping post IDs to post data.
  • Ensure that no data is lost during the transformation.

Expected Behavior:

Given an input array of users, the function should return a normalized object that can be easily accessed and manipulated in a React component.

Edge Cases:

  • An empty input array of users.
  • Users with no posts.
  • Posts that might be missing their userId (though for this challenge, assume valid userId linkage).

Examples

Example 1:

Input: [
  {
    id: 1,
    name: "Alice",
    posts: [
      { id: 101, title: "First Post", userId: 1 },
      { id: 102, title: "Another Post", userId: 1 }
    ]
  },
  {
    id: 2,
    name: "Bob",
    posts: [
      { id: 103, title: "Bob's Thoughts", userId: 2 }
    ]
  }
]
Output: {
  users: {
    "1": { id: 1, name: "Alice" },
    "2": { id: 2, name: "Bob" }
  },
  posts: {
    "101": { id: 101, title: "First Post", userId: 1 },
    "102": { id: 102, title: "Another Post", userId: 1 },
    "103": { id: 103, title: "Bob's Thoughts", userId: 2 }
  }
}

Explanation: User with ID 1 and their posts are extracted. User with ID 2 and their posts are extracted. The posts array is removed from the user objects in the users object. All posts are collected into the posts object with their respective IDs as keys.

Example 2:

Input: [
  {
    id: 3,
    name: "Charlie",
    posts: []
  }
]
Output: {
  users: {
    "3": { id: 3, name: "Charlie" }
  },
  posts: {}
}

Explanation: Charlie has no posts. The users object contains Charlie, and the posts object is empty.

Example 3:

Input: []
Output: {
  users: {},
  posts: {}
}

Explanation: An empty input array results in empty users and posts objects.

Constraints

  • The input will be an array of User objects.
  • User IDs and Post IDs are unique within their respective collections.
  • User IDs and Post IDs will be positive integers.
  • The function should have a time complexity of O(N + M), where N is the number of users and M is the total number of posts.
  • The function should have a space complexity of O(N + M) to store the normalized state.

Notes

  • Consider using Array.prototype.reduce or for...of loops for iteration.
  • The keys in the users and posts objects should be strings, as object keys are implicitly strings in JavaScript/TypeScript.
  • Think about how you'll collect and organize the user and post data separately.
  • You'll need to define TypeScript interfaces for User and Post to ensure type safety.
Loading editor...
typescript