1

Before I ask; there are a bunch of discussions on this particular subject, most of which pertain to ES5 and do not necessarily hold truth for ES6. I'm attempting to get some clarification, and maybe help the next person that is scouring the internet for an answer. This is in reference to ES6 specifically.

QUESTIONS:

Consider the following object structure:

const unsorted_object = {
    'C': '0003',
    'D': '0004',
    'A': '0001',
    'B': '0002',
    'F': '0005',
}; 
  1. How can I sort a JavaScript object by key? (Answered here)

    const sorted_object = {};
    Object.keys(unsorted_object).sort().forEach(function(key) {
        sorted_object[key] = unsorted_object[key];
    });
    
  2. How can I sort a JavaScript object by the key value?

EDIT #1

I might not have been totally clear on question #2. The idea is to sort the JavaScript object by the value of the key, not by key and value.

EDIT #2

const unsorted_object = {
    '0001': '13.1666',
    '0002': '11.0001',
    '0003': '10.6664',
    '0004': '13.1666',
    '0005': '7.3331',
};

Output:

'0001': '13.1666'
'0004': '13.1666'
'0002': '11.0001'
'0003': '10.6664'
'0005': '7.3331'
Community
  • 1
  • 1
artomason
  • 2,475
  • 1
  • 16
  • 29
  • Why do you want to sort an Object (they are unordered by design)? Can you explain the bigger picture of what you want to do? – JBallin Mar 23 '19 at 22:51
  • @JBallin In ES6+, object properties *are* ordered - it's only in ES5 and before that they have no defined order. – CertainPerformance Mar 23 '19 at 22:51
  • Possible duplicate of [Sorting JavaScript Object by property value](https://stackoverflow.com/questions/1069666/sorting-javascript-object-by-property-value) – JBallin Mar 23 '19 at 22:55
  • @JBallin I disagree. I feel that the question, asked in 2010, was answered under the premise of ES5 and thus is not ES6 specific. – artomason Mar 23 '19 at 22:59
  • @artomason some [answers](https://stackoverflow.com/a/39442287/4722345) use ES6. If you truly think it's different, you should edit your title to be ES6 specific. – JBallin Mar 23 '19 at 23:01
  • @CertainPerformance interesting, didn't realize. But still, I think OP should explain the use case. – JBallin Mar 23 '19 at 23:02
  • @JBallin in most cases, the need to sort is an inherent fundamental of working with data structures. With the release of the ES6 specification; this can now be accomplished in more optimal ways than detailed in previous questions. In my specific use scenario, I will be deleting the bottom `n` keys (lowest). – artomason Mar 23 '19 at 23:06
  • @JBallin I would also like to point out that answers in this question that are similar to the "duplicate" answer have been down voted here. – artomason Mar 23 '19 at 23:13
  • @artomason well your thinking is not irrational but nothing really changed other than codifying that object keys are supposed to be presented in the order in which they were added to the object. That's not directly under your control without some really fragile code. It's much safer to enforce your own ordering by explicitly sorting the keys (as an array) when you need them sorted. – Pointy Mar 23 '19 at 23:17
  • @artomason Why wouldn't you remove the lowest keys before turning it back into an object? `Object.entries(obj).sort().slice(n).reduce((o, [k, v]) => ({ ...o, [k]: v }), {})` – JBallin Mar 23 '19 at 23:37
  • The [most upvoted answer here](https://stackoverflow.com/a/55319133/4722345) is quite similar to the answer I linked. – JBallin Mar 23 '19 at 23:40
  • Here's an interesting use case where sorting an object by value could make sense: https://stackoverflow.com/a/53661894/4722345 – JBallin Mar 25 '19 at 00:23

3 Answers3

7

Objects keys in ES6 have a traversal order. Integer keys are always first, and are sorted in an ascending order (0 -> 9). In non integer keys the order of assignment is preserved (see this article). To sort the object's keys we need to recreate the object, and add the keys in the required order.

Note: This means that sorting will only work on non integer keys, because integers are always 1st, and always sorted in an ascending order.

To sort and recreate the object:

  • Use Object.entries() to get an array of key/value pairs - [[key, value], [key, value]]
  • Sort them by the value (the 2nd item in the pair) using array destructuring - [, v1]. Cast the strings to number using the + operator,
  • Reduce back to an object. Take the key and value using destructuring [k , v], and add them to the accumulator object using computed property names - ({ [k]: v }), and object spread - ({ ...r, [k]: v })

const unsorted_object = {
    '0001': '13.1666',
    '0002': '11.0001',
    '0003': '10.6664',
    '0004': '13.1666',
    '0005': '7.3331',
};

const sorted_object = Object.entries(unsorted_object)
  .sort(([,v1], [,v2]) => +v2 - +v1)
  .reduce((r, [k, v]) => ({ ...r, [k]: v }), {});

console.log(sorted_object);

If supported you can create the object from the entries using Object.fromEntries() instead of Array.reduce():

const unsorted_object = {
    '0001': '13.1666',
    '0002': '11.0001',
    '0003': '10.6664',
    '0004': '13.1666',
    '0005': '7.3331',
};

const sorted_object = Object.fromEntries(
  Object.entries(unsorted_object)
    .sort(([,v1], [,v2]) => +v2 - +v1)
);

console.log(sorted_object);

Edge friendly version, that uses Object.assign() instead of spread:

const unsorted_object = {
    '0001': '13.1666',
    '0002': '11.0001',
    '0003': '10.6664',
    '0004': '13.1666',
    '0005': '7.3331',
};

const sorted_object = Object.entries(unsorted_object)
  .sort(([,v1], [,v2]) => +v2 - +v1)
  .reduce((r, [k, v]) => Object.assign(r, { [k]: v }), {});

console.log(sorted_object);
Ori Drori
  • 145,770
  • 24
  • 170
  • 162
  • I'm a pretty new to JavaScript, could you break down your answer a little more. there is some syntax that I don't understand. For instance, `.sort(([,v1], [,v2]) =>` and `...`. – artomason Mar 23 '19 at 23:16
  • Thank you for the update. I'm still trying to process the information a bit more, but is it possible to sort in descending order (value) using the same method? You talked about keys, but I'm not sure if that applies to value as well. – artomason Mar 23 '19 at 23:27
  • 1
    You sort the pairs using the values, and then you use the pairs to recreate the object in the sorted order. See updated answer (ordered by values descending). – Ori Drori Mar 23 '19 at 23:30
  • I couldn't get this working properly, but I also noticed that this is actually not what I was looking for, I think lol. I'm looking to sort the Object by JUST the value for question #2. It sounds like I was asking for key and value. I'm not sure if that is how you interpreted it. – artomason Mar 24 '19 at 00:39
  • Please add an example of the requested result. – Ori Drori Mar 24 '19 at 10:25
  • I have made an update to the question. Please see Edit #2. Thanks! – artomason Mar 24 '19 at 14:40
  • … and it seems that is actually the expected behavior because the numbers are not integers, they are decimals. Would it be better to drop the decimal before comparing? Although I would suspect that a decimal such as 13.1 might end up higher than 13.4 if that is the case. – artomason Mar 24 '19 at 15:08
  • If the keys were integer the behavior would have been different (read the article in the link). Don't drop the decimals, cast to numbers. See updated answer. – Ori Drori Mar 24 '19 at 16:41
  • Thanks for the update. The code seems to be throwing an error. Here is the jsfiddle http://jsfiddle.net/8yxf376p/3/; the same issue is happening in browser (edge & firefox). – artomason Mar 24 '19 at 17:04
  • 1
    It works fine on firefox. Edge doesn't support object spread, so I've replaced it with Object.assign. See update. – Ori Drori Mar 24 '19 at 17:14
  • @Winston - thanks, but `Object.fromEntries()` is used in the 2nd solution. In addition, `Object.fromEntries()` is not part of ES6, it was released with ECMAScript 2019 . – Ori Drori Sep 16 '20 at 14:38
3

Extract the entries instead of the keys, then sort by the difference of each value:

const unsorted_object = {
    'C': '0003',
    'D': '0004',
    'A': '0001',
    'B': '0002',
    'F': '0005',
};

const sorted_object = {};
Object.entries(unsorted_object)
  .sort((a, b) => a[1] - b[1])
  .forEach(([key, val]) => {
    sorted_object[key] = val;
  });
console.log(sorted_object);

Note that it's probably more appropriate to use reduce to construct an object from an array:

const unsorted_object = {
    'C': '0003',
    'D': '0004',
    'A': '0001',
    'B': '0002',
    'F': '0005',
};

const sorted_object = Object.entries(unsorted_object)
  .sort((a, b) => a[1] - b[1])
  .reduce((a, [key, val]) => {
    a[key] = val;
    return a;
  }, {});
console.log(sorted_object);
CertainPerformance
  • 260,466
  • 31
  • 181
  • 209
2

You can sort entries and use a reduce() to create new object.

With that said there is likely something wrong in your app design for needing to even do such an operation

const unsorted_object = {
  'C': '0003',
  'D': '0004',
  'A': '0001',
  'B': '0002',
  'F': '0005',
};

const sorted = Object.entries(unsorted_object)
  .sort((a, b) => a[1] - b[1])
  .reduce((a, c) => (a[c[0]] = c[1], a), {})

console.log(sorted)
charlietfl
  • 164,229
  • 13
  • 110
  • 143
  • I'm not sure why this was down voted, some insight would be nice. – artomason Mar 23 '19 at 23:28
  • @artomason someone went through and downvoted all the initial answers very quickly. It happens...not much can do about it. If you feel it is wrong you have power to upvote – charlietfl Mar 23 '19 at 23:30