0

I want to loop through a nested object and modify each key (delete first character).

The following code iterates through the whole object but does not modify the key. The Object still looks the same after running the function.

const removeFirstCharacterOfKey = (obj) => {
    Object.keys(obj).forEach((key) => {
        if (typeof obj[key] === 'object') {
            if(Array.isArray(obj[key])) {
                return;
            }
            return removeFirstCharacterOfKey (obj[key]);
        }
        key = key.substring(1);
    });
}

A possibility would also be to create a new object with modified keys. Is it possible to achieve this?

maidi
  • 3,219
  • 6
  • 27
  • 55

2 Answers2

2

https://codesandbox.io/embed/cool-golick-vdf6k?fontsize=14&hidenavigation=1&previewwindow=tests&theme=dark

The array.reduce is your best bet here. Basic implementation in your case would be

function serializer(obj) {
  return Object.entries(obj).reduce((obj, [key, entry]) => ({
      ...obj,
      [key.substr(1)]: entry
    }),{});
}

However you have some additional check for array so we need to modify the method to accept custom modifier

function serializer(obj, modifier) {
  return Object.entries(obj).reduce(
    (obj, [key, entry]) => ({
      ...obj,
      [modifier(key, entry)]: entry
    }),
    {}
  );
}

and use it as

const myObj = {
  $a: "hello a",
  $b: "hello b",
  $c: [],
  $d: "hello d",
  $e: "hello e"
};

function serializer(obj, modifier) {
  return Object.entries(obj).reduce(
    (obj, [key, entry]) => ({
      ...obj,
      [modifier(key, entry)]: entry
    }),
    {}
  );
}

const serialized = serializer(myObj, (key, entry) =>
  Array.isArray(entry) ? key : key.substr(1)
);

test("", () => {
  expect(serialized).toEqual({
    a: "hello a",
    b: "hello b",
    $c: [],
    d: "hello d",
    e: "hello e"
  });
});

Eduard Jacko
  • 1,974
  • 1
  • 16
  • 29
1

In your current code, you're never mutating the existing object (or creating a new object), you're only reassigning the key parameter, which won't have any side-effects.

Consider using a recursive Object.fromEntries mapping function instead:

const obj = {
  foo: {
    bar: 'val',
    baz: {
      buzz: 'val',
      buzz2: 'val'
    }
  }
};

const objWithSlicedKeys = obj => Object.fromEntries(
  Object.entries(obj).map(
    ([key, val]) => [
      key.slice(1),
      typeof val === 'object' && val !== null ? objWithSlicedKeys(val) : val
    ]
  )
);
console.log(objWithSlicedKeys(obj));
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • 1
    Depending on preference `typeof val === 'object' && val !== null` could be shortened to `val && typeof val === 'object'`. – 3limin4t0r Jan 17 '20 at 11:09