Hone logo
Hone
Problems

Set Nested Object Property by Path

This challenge focuses on a common JavaScript task: dynamically setting a value within a nested object structure. Often, you'll encounter data where the "path" to a specific property is not fixed but determined at runtime. Your goal is to create a function that can navigate this structure and update the target property.

Problem Description

You need to implement a JavaScript function setNestedProperty(obj, path, value) that takes three arguments:

  1. obj: The target JavaScript object.
  2. path: A string representing the path to the property to be set. The path segments are separated by dots (e.g., "user.address.city").
  3. value: The new value to assign to the property at the specified path.

The function should:

  • Modify the original object obj in place.
  • Create intermediate objects or arrays if they don't exist along the path.
  • Handle paths that refer to array indices (e.g., "items.0.name").
  • If obj is not an object or null, or if path is not a non-empty string, the function should do nothing or throw an error (your choice, but be consistent).

Expected Behavior:

  • If the path exists and the property can be set, the value should be assigned.
  • If any part of the path leads to a non-object (and not an array index), the function should ideally create the necessary structure. For example, if the path is "a.b.c" and obj.a is undefined, obj.a should become an object {}. If obj.a is 5, this could be problematic and you might choose to overwrite it with an object.
  • If a path segment is a valid array index (a non-negative integer string), and the parent is an array, it should set the element at that index. If the parent is not an array, it should potentially create an array.

Examples

Example 1:

let obj = {
  user: {
    name: "Alice"
  }
};
let path = "user.address.city";
let value = "New York";

setNestedProperty(obj, path, value);

// Expected Output:
// {
//   user: {
//     name: "Alice",
//     address: {
//       city: "New York"
//     }
//   }
// }

Explanation: The path user.address.city did not fully exist. The function created the address object within user and then set the city property to "New York".

Example 2:

let obj = {
  data: [
    { id: 1, name: "Apple" }
  ]
};
let path = "data.0.price";
let value = 1.99;

setNestedProperty(obj, path, value);

// Expected Output:
// {
//   data: [
//     { id: 1, name: "Apple", price: 1.99 }
//   ]
// }

Explanation: The path data.0.price referred to an element within an array. The function navigated to the first element of the data array and added a price property.

Example 3: (Edge Case - Overwriting a non-object)

let obj = {
  settings: 123
};
let path = "settings.theme";
let value = "dark";

setNestedProperty(obj, path, value);

// Expected Output:
// {
//   settings: {
//     theme: "dark"
//   }
// }

Explanation: The property settings was initially a number. The function detected that it needed to be an object to set the nested theme property, so it replaced the number 123 with an object { theme: "dark" }.

Example 4: (Edge Case - Path with array creation)

let obj = {
  items: {}
};
let path = "items.0.name";
let value = "Banana";

setNestedProperty(obj, path, value);

// Expected Output:
// {
//   items: [
//     { name: "Banana" }
//   ]
// }

Explanation: The items property was an object, but the path indicated an array index (0). The function converted items to an array and created the first element as an object with the name property.

Constraints

  • The path string will only contain alphanumeric characters, dots (.), and digits.
  • The path string will not start or end with a dot.
  • There will not be consecutive dots in the path string.
  • The obj will be a valid JavaScript object, array, or primitive value that might need to be converted.
  • The value can be any JavaScript data type.
  • The function should be reasonably efficient for paths up to 10 segments deep and objects with up to 1000 properties/elements.

Notes

  • Consider how you will split the path string into individual segments.
  • Pay close attention to differentiating between object properties and array indices. A segment that is a string representing a non-negative integer should be treated as an array index if the parent is an array, or potentially trigger an array conversion if the parent is not an array but needs to become one.
  • Think about the initial state of the object and how to handle cases where intermediate paths don't exist.
  • You might find it helpful to use a loop and keep track of the current level in the object structure.
  • Consider using hasOwnProperty or checking typeof to ensure you are working with objects or arrays correctly.
Loading editor...
javascript