-1

I need to deep reverse the order of all keys in a JavaScript object.

For example:

{
  a: 'val1',
  b: 'val2',
  c: {
    ca: 'val3',
    cb: 'val4'
  }
}

Must become:

{
  c: {
    cb: 'val4',
    ca: 'val3'
  },
  b: 'val2',
  a: 'val1'
}

I have tried writing a recursive function, but I cannot figure out how to fix it:

function reverseKeys (obj, newObj = {}) {
  Object.keys(obj).reverse().forEach(key => {
    if(typeof obj[key] === 'object') {
      newObj = reverseKeys(obj[key], {})
    }
    newObj[key] = obj[key]
  })
  return newObj
}

Solution inspired by @Unmitigated's input

function reverseKeys (obj) {
  return Object.fromEntries(
    Object.entries(obj)
      .reverse()
      .map(
        ([k, v]) => {
          if (v.constructor === Object && v.constructor !== Array && v !== null) {
            return [k, reverseKeys(v)]
          } else if (v.constructor === Array && v !== null) {
            const a = []
            v.forEach(elem => {
              if ((elem.constructor === Object || elem.constructor === Array) && elem !== null) {
                a.push(reverseKeys(elem))
              } else {
                a.push(elem)
              }
            })
            return [k, a]
          } else {
            return [k, v]
          }
        }
      )
  )
}
Ben
  • 44
  • 8
  • A note on "order" in JS objects: https://stackoverflow.com/questions/30076219/does-es6-introduce-a-well-defined-order-of-enumeration-for-object-properties – Anurag Srivastava Feb 09 '23 at 16:51
  • 1
    *I need to deep reverse the order of all keys in a JavaScript object.* - that's somewhat hard to believe. Why do you want that? – tevemadar Feb 09 '23 at 16:56
  • 1
    It's an object (something that's safest assumed to have no key ordering, even if the spec these days says there is a key ordering). Are you treating it as a tree where traversal order indicates node ordering? If so, look at using an actual tree data structure instead, because JS key ordering is based on insertion/declaration order. You'd have to unset `a` and `b` and then readd them, rather than "moving `c`", which just gets weirder and weirder the more keys you have... use a proper data structure =) – Mike 'Pomax' Kamermans Feb 09 '23 at 16:57
  • Understood - unfortunately the library json-schema-faker reverses the properties when we generate JSON samples from JSON schema. Users. of our tool is not so happy so I had to quickly write a reverser for the reversed structure... I think I will raise an issue as well in the json-schema-faker Github repo. Thanks for your comment! – Ben Feb 09 '23 at 17:16

1 Answers1

1

You can map over Object.entries recursively.

let o = {
  a: 'val1',
  b: 'val2',
  c: {
    ca: 'val3',
    cb: 'val4'
  },
  d: [{a : 1, b : 2}, {c: 3}]
};
function reverseKeys(obj) {
  if (Array.isArray(obj)) return [...obj].reverse().map(reverseKeys);
  if (Object.getPrototypeOf(obj) === Object.prototype) 
    return Object.fromEntries(Object.entries(obj).reverse()
      .map(([k, v]) => [k, reverseKeys(v)]));
  return obj;
}
console.log(reverseKeys(o));
Unmitigated
  • 76,500
  • 11
  • 62
  • 80
  • Wow! Thank you so much for the fast answer, it seems to work :) – Ben Feb 09 '23 at 16:50
  • It works, but it's also crazy inefficient, so you probably still want to actually use the right data structure for the job ;) – Mike 'Pomax' Kamermans Feb 09 '23 at 17:01
  • Understood - unfortunately the library `json-schema-faker` reverses the properties when we generate JSON samples from JSON schema. Users. of our tool is not so happy so I had to quickly write a reverser for the reversed structure... I think I will raise an issue as well in the `json-schema-faker` Github repo. Thanks for your comment! – Ben Feb 09 '23 at 17:04
  • @Unmitigated any idea why it does this to an array: `{ a: [ { a1: 'val1' }, { a2: 'val2' } ] }` -> `{ a: { '0': { a1: 'val1' }, '1': { a2: 'val2' } } }` – Ben Feb 09 '23 at 17:09
  • @Ben You may need to update the condition for reversing to `v.constructor === Object` or `Object.getPrototypeOf(v) === Object.prototype`. – Unmitigated Feb 09 '23 at 17:11
  • @Unmitigated yup that certainly took away the `0` and `1` etc. but keys inside and array then do not get reversed - maybe not possible after all? `{ a: [ { a1: 'val1', a2: 'val2' } ] }` to become `{ a: [ { a2: 'val2', a1: 'val1' } ] }` – Ben Feb 09 '23 at 17:38
  • @Ben I've updated my answer to support arrays. If you do not want arrays themselves to be reversed, remove the `.reverse()` in `[...obj].reverse().map(...)`. – Unmitigated Feb 09 '23 at 19:30