0

I have an object with a sub-object with ids. I would like to order the subobject by a specific property but without loosing the id references.

I've tried ordering individually the subobject "options" by position using lodash and different vanilla js solutions I've found and reasigning it to the main object, but I loose the ids because in all cases it returns an array and I can't find a way to keep the same id structure.

Object example:

{
  name: 'User name',
  options: {
    '234aafg': {
      name: 'bar',
      position: 2
    },
    '543al22': {
      name: 'foo',
      position: 0
    },
    '437uaz2': {
      name: 'baz',
      position: 1
    },
  }
}

Expected Result:

{
  name: 'User name',
  options: {
    '543al22': {
      name: 'foo',
      position: 0
    },
    '437uaz2': {
      name: 'baz',
      position: 1
    },
    '234aafg': {
      name: 'bar',
      position: 2
    }
  }
}
plexus
  • 23
  • 5
  • 1
    Given that object key order is not guarenteed, and keys are hashed to make lookups super fast, what problem are you trying to solve? – Taplar Dec 26 '19 at 18:20
  • I'm receiving from an API an object that can't be ordered in origin (as I would like) and I need to show the items to the user ordered by that field position. It's important to keep the id references for future updates and other references we need to keep. I could go in more detail if you want, but mainly that's the problem I'm facing atm. – plexus Dec 26 '19 at 18:25
  • Does this answer your question? [Sorting a JavaScript object by property name](https://stackoverflow.com/questions/1359761/sorting-a-javascript-object-by-property-name) – Eldar Dec 26 '19 at 18:27
  • I would suggest making a secondary variable that is an array, that you could sort the objects into it by position, rather than trying to force order on an object. – Taplar Dec 26 '19 at 18:28

3 Answers3

0

let obj = {
  name: 'User name',
  options: {
    '234aafg': {
      name: 'bar',
      position: 2
    },
    '543al22': {
      name: 'foo',
      position: 0
    },
    '437uaz2': {
      name: 'baz',
      position: 1
    },
  }
}

let keys = Object.keys(obj.options).sort((a, b) => {
  if (obj.options[a].position < obj.options[b].position) return -1
  return 1
})

let options = obj.options
let newOptions = {}

keys.forEach(key => newOptions[key] = options[key])
obj.options = newOptions

console.log(obj)
kooskoos
  • 4,622
  • 1
  • 12
  • 29
0

I would suggest generating a sorted list of keys that you can later loop over whenever you need. The looping logic would take the key and lookup the original object to get the data it needed for processing, such as displaying the data in order on the page to the user.

var data = {
  name: 'User name',
  options: {
    '234aafg': {
      name: 'bar',
      position: 2
    },
    '543al22': {
      name: 'foo',
      position: 0
    },
    '437uaz2': {
      name: 'baz',
      position: 1
    },
  }
};

data.sortedOptions = Object.entries(data.options)
  .sort(function(a, b){
    return a[1].position - b[1].position;
  })
  .map(function(entry){
    return entry[0];
  });

console.log(data);
Taplar
  • 24,788
  • 4
  • 22
  • 35
0

This is a simple functional approach that should accomplish what you want. The object entries are converted into an array with an additional id property. After sorting, the id properties are stripped and used to re-construct a new object in the correct order.

However, I would personally skip the call to .reduce and just use the array with the additional id property.

data.options = Object.entries(data.options)
  .map(([id, value]) => ({ ...value, id }))
  .sort((a, b) => a.position - b.position)
  .reduce((rollup, entry) => {
    const { id, ...rest } = entry;
    rollup[id] = rest;
    return rollup;
  }, {});