0

I have an object like this:

const data = {
  val5: 1231,
  val3: 1232,
  val1: 1233,
  val2: 1234,
  val4: 1235,
};

And its has to be sorted by it keys but in specific order:

const sortOrder = ["val1", "val3", "val2"];

If the key is not present in the sortOrder then it has to go below the keys that are present. And the non present keys order should (potentially) stay the same.

The result object should look like this:

{
  val1: 1233, // present in sortOrder at position 0
  val3: 1232, // present in sortOrder at position 1
  val2: 1234, // present in sortOrder at position 2
  val5: 1231, // NOT present in sortOrder
  val4: 1235, // NOT present in sortOrder
}

I've been playing with sorting the keys only for now and can achieve some order but the non present keys are appearing at the top

const ordered = Object.keys(data).sort((a, b) => {
  return sortOrder.indexOf(a) - sortOrder.indexOf(b);
});
[ 'val5', 'val4', 'val1', 'val3', 'val2' ]

As you can see val5 and val4 are the beginning and they should be at the end (after val2)

VLAZ
  • 26,331
  • 9
  • 49
  • 67
Stefan Stoichev
  • 4,615
  • 3
  • 31
  • 51
  • 1
    You almost had it, the only issue is that you're not checking if sortOrder actually contains the key you're iterating or not. If you do `sortOrder.indexOf(x)` but `x` is not in `sortOrder`, then `indexOf` will return `-1`, explaining why the non-existent keys are always on top (`--1 === +1`). See [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#return_value) for more info. The fix is to add `if (!sortOrder.includes(b)) return -1` before the rest of the evaluation in the sorting function. – Lorik May 03 '22 at 09:44
  • While it is possible to have order for objects (see [here](https://stackoverflow.com/q/5525795) and [here](https://stackoverflow.com/q/30076219)) I wouldn't recommend it. The ordering is very easy to disturb and might be for any number of reasons. One very notable example would be sending a network request - JSON handling libraries might change to any other order of the properties. Consider using a different data structure if the order of keys matters. A Map might work. – VLAZ May 03 '22 at 09:46
  • @lorikku to be honest ive reached to the same conclusion but when add the `includes` part then the result order is a bit odd `[ 'val4', 'val1', 'val3', 'val2', 'val5' ]`. Not sure why `val4` is now at the front. – Stefan Stoichev May 03 '22 at 09:50
  • @VLAZ at the moment (and it foreseeable future) the source data is a static json and it loaded as it is – Stefan Stoichev May 03 '22 at 09:51
  • 1
    @StefanStoichev maybe try to add: `if (!sortOrder.includes(a)) return 1` as well (also before the actual evaluation). – Lorik May 03 '22 at 09:52
  • @lorikku thats make a lot of sense :) thanks. if you put it as an answer im happy to accept it – Stefan Stoichev May 03 '22 at 09:58
  • @StefanStoichev How do you accomplish ? Can you please post answer. – Jaydeep Mor May 03 '22 at 11:29
  • 1
    @JaydeepMor for some reason cant post an answer but the whole example is: ```const data = { val5: 1231, val3: 1232, val1: 1233, val2: 1234, val4: 1235, }; const sortOrder = ["val1", "val3", "val2"]; const ordered = Object.keys(data).sort((a, b) => { if (!sortOrder.includes(b)) return -1; if (!sortOrder.includes(a)) return 1; return sortOrder.indexOf(a) - sortOrder.indexOf(b); }); console.log(ordered); ``` – Stefan Stoichev May 03 '22 at 12:12
  • 1
    @StefanStoichev Thank you! It's working. [fiddle](https://jsfiddle.net/Jaydeep_Mor/sqj9mrtf/53/) – Jaydeep Mor May 04 '22 at 06:58
  • @StefanStoichev I'm happy it worked for you, however, I'm not able to put it as an answer because this question was closed by a moderator since it's been 'answered before'. The decision was too impulsive imo, but yeah I'm glad nonetheless that I was able to help you out :) – Lorik May 04 '22 at 14:28
  • @lorikku yea agree that it was to impulsive to close the question. its a different problem to the other questions. I'm trying to re-open it :) – Stefan Stoichev May 04 '22 at 15:27
  • 1
    @lorikku the question is now open and can be answered :) – Stefan Stoichev May 09 '22 at 05:40

1 Answers1

1

You almost had it, the only issue is that you're not checking if sortOrder actually contains the key you're iterating or not.

If you do sortOrder.indexOf(x) but x is not in sortOrder, then indexOf will return -1, explaining why the non-existent keys are always on top (--1 === +1). See reference for more info.

The fix is to manually put all keys that are not present in sortOrder at the end of the array:

if (!sortOrder.includes(a)) return 1 
if (!sortOrder.includes(b)) return -1

So in the end, you'd have this:

const ordered = Object.keys(data).sort((a, b) => {
  if (!sortOrder.includes(a)) return 1 
  if (!sortOrder.includes(b)) return -1
  return sortOrder.indexOf(a) - sortOrder.indexOf(b);
});
Lorik
  • 438
  • 2
  • 9