4
\$\begingroup\$

During an interview I was given this interesting JavaScript task:

Given the code:

const testStr = "bar.baz.foo:111222",
      testObj = {
        bar: {
          baz: {
            foo: 333444,
            foo2: 674654
          },
          boo: {
            faa: 11
          }
        },
        fee: 333
      };

function replaceInObj(obj, str) {
  // your code
}

write the implementation of the replaceInObj function

I was not allowed to use a 3rd-party library. I kinda solved the task, but I'm not satisfied with my solution and I'm struggling with my complex implementation. At the same time I cannot find another solution by myself.

My implementation of the replaceInObj function

const testStr = 'bar.baz.foo:111222';
const testObj = {
  bar: {
    baz: {
      foo: 333444,
      foo2: 674654,
    },
    boo: {
      faa: 11,
    },
  },
  fee: 333,
};

console.log('[before] testObj.bar.baz =', testObj.bar.baz);

// run
replaceInObj(testObj, testStr);

console.log('[after] testObj.bar.baz =', testObj.bar.baz);

function replaceInObj(obj, str) {
  const [path, valueToChange] = str.split(':');

  path.split('.').reduce((acc, prop, index, arr) => {
    const isLastProp = arr.length === index + 1;
    if (isLastProp) {
      acc[prop] = valueToChange;
    }

    return acc[prop];
  }, obj);
}

\$\endgroup\$
4
\$\begingroup\$

Neither of your solutions handles the type of the replacement correctly. For instance "bar.baz.foo:111222" replaces foo:333444 with foo:"111222" where I would expect it to result in foo:111222.

Further they can handle if the replacement is an array, but not if it is an object due to the use of split(':'). You should use str.indexOf(':') together with str.substr() instead:

function replaceInObj(obj, str) {
  let splitIndex = str.indexOf(':');
  let [path, replacement] = [str.substr(0, splitIndex), str.substr(splitIndex + 1)];
  let sections = path.split('.');
  let sub = obj;
  for (var i = 0; i < sections.length - 1; i++) {
    sub = sub[sections[i]];
  }

  sub[sections[i]] = JSON.parse(replacement);
  return obj;
}

Here JSON.parse() is used to interpret the value to the correct type. I don't know if the JSON api is considered as "third-party" in this context.


There is a more "quick and dirty" approach using eval:

function replaceInObj(obj, str) {
  let splitIndex = str.indexOf(':');
  let [path, replacement] = [str.substr(0, splitIndex), str.substr(splitIndex + 1)];
  eval("obj." + path + "=" + replacement);
  return obj;
}

Some test cases:

  let testObj = {
    bar: {
      baz: {
        foo: 333444,
        foo2: 674654
      },
      boo: {
        faa: 11
      }
    },
    fee: 333
  };

    let replacements = [
      "bar.baz.foo:111222",
      "bar.baz.foo:\"05-31-2020\"",
      "bar.baz.foo:false",
      "bar.baz.foo:\"abcdefg\"",
      "bar.baz.foo:[111222, 21341234, 234243]",
      "bar.baz.foo:{ \"aaa\": 1234, \"bbb\": \"hello world\" }"
    ];

    for (let replacement of replacements) {
      let result = replaceInObj(testObj, replacement);
      console.log(result.bar.baz.foo);
    }


function replaceInObj(obj, str) {
  let splitIndex = str.indexOf(':');
  let [path, replacement] = [str.substr(0, splitIndex), str.substr(splitIndex + 1)];
  eval("obj." + path + "=" + replacement);
  return obj;
}

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Liked your solution and explanation! How incredibly you handled those edge-cases with possibility to pass any JSON-valid string \$\endgroup\$ – java-man-script Jul 9 at 21:28
  • \$\begingroup\$ Great work! 👍👍 \$\endgroup\$ – java-man-script Jul 9 at 21:34
0
\$\begingroup\$

I think I found solution much cleaner and simpler to understand: https://codereview.stackexchange.com/a/240907/227075

That solution adjusted to my task

const testStr = 'bar.baz.foo:111222';
const testObj = {
  bar: {
    baz: {
      foo: '333444',
    },
  },
};

console.log('[before] testObj.bar.baz =', testObj.bar.baz);

// run
replaceInObj(testObj, testStr);

console.log('[after] testObj.bar.baz =', testObj.bar.baz);

function replaceInObj(obj, str) {
  const [path, valueToChange] = str.split(':');
  const properties = path.split('.');
  const lastProperty = properties.pop();
  const lastObject = properties.reduce((a, prop) => a[prop], obj);
  lastObject[lastProperty] = valueToChange;
}

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Welcome to Code Review! You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process. \$\endgroup\$ – Vogel612 Jul 3 at 17:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.