0

I am having a hell of a time finding an answer to this one.

I would like to have a hash in JavaScript, like so:

const hash = {
   a: 'A',
   c: 'C',
   b: 'B'
}

and I would like to sort the object by the value, such that it might become:

const hash = {
   a: 'A',
   b: 'B',
   c: 'C',
}

now, I realize that POJSOs don't guarantee the order of their keys, so I was thinking of using immutableJS or some object that will guarantee the order of the keys in the hash.

so I am looking for something like:

var newHash = Immutable.Map(hash).sortBy(function(key,val){
       return val;
});

But I don't believe this exists. is this too much to ask? How can I find this functionality?

Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
  • Possible duplicate of [Sort JavaScript object by key](http://stackoverflow.com/questions/5467129/sort-javascript-object-by-key) – Andreas Mar 18 '16 at 08:28
  • Except, LOL I want to sort by the value – Alexander Mills Mar 18 '16 at 08:30
  • Why do you want to do this? I feel like if we knew the underlying motivation, there might be something more useful we could point you toward... – T.J. Crowder Mar 18 '16 at 08:31
  • It's not that complicated to change the key for the value... ;) – Andreas Mar 18 '16 at 08:32
  • the reason I want to do this is because a hash is the most convenient data structure to use for a user to enter input for configuration for a library I am writing, unfortunately to explain this would go way beyond the scope of the question – Alexander Mills Mar 18 '16 at 08:33
  • guys, this is like the simplest f-ing question on earth, and it really is not that much to ask. I just want to sort a hash by value. That's it. There must be a good way to do this with JS, if not, shame on JS, not me. – Alexander Mills Mar 18 '16 at 08:34
  • Um, not sure what you're reacting to above. Re your reason: Again, what's the point? Why is it being sorted useful? For serialization so changes aren't chaotic? – T.J. Crowder Mar 18 '16 at 08:42
  • The reason why I am sorting - this is for an optimization algorithm, I am sorting the object based off the length of an array property in the value. The sorting process is part of the process to seed the algorithm. Simply consider the reason for my sorting of the object to be similar to any reason to sort an array. – Alexander Mills Mar 18 '16 at 09:13
  • sorting is worst if you just need some highest or lowest value or reference. – Nina Scholz Mar 18 '16 at 09:17
  • Well, in any case, the closest you can come to this is as shown in my answer and georg's. – T.J. Crowder Mar 18 '16 at 09:20

2 Answers2

3

now, I realize that POJSOs don't guarantee the order of their keys

They do now, as of ES2015. You still can't access that order via for-in or Object.keys, but you can via Object.getOwnPropertyNames. The order is given by the specification operation [[OwnPropertyKeys]]. Ignoring inherited properties, it's basically: Keys that are strings that qualify as integer indexes, in numeric order; followed by remaining keys that are strings, in the order the properties were created; followed by keys that are Symbols, in the order they were created.

With that information, if your keys are all strings that don't qualify as integer indexes, then on an ES2015-compliant engine, we can do this:

  1. Start with the properties in any order (e.g., a, c, b)

  2. Use Object.keys to get those keys as an array

  3. Use Array#map to create an array of objects with properties for the key and value (I've used k and v below)

  4. Sort the array by value

  5. Use Array#reduce to create a new object, adding the properties to it in the sorted order. (Some, including myself, may argue that this is a slight misuse of reduce, because the accumulator never actually changes, but hey, it lets us be all l33t [is that still a thing?] and avoid an intermediate variable. Um, because that's a goal? :-) )

The end result is an object with the properties in order of creation, which is the order of their values because we created them in that order. We can access that order via Object.getOwnPropertyNames.

// The unsorted data
let data = {
  a: 'A',
  c: 'C',
  b: 'B'
};

// Create it sorted
const hash = Object.keys(data)
  .map(key => ({k: key, v: data[key]}))
  .sort((a, b) => a.v.localeCompare(b.v))
  .reduce((o, e) => {
    o[e.k] = e.v;
    return o;
  }, {});

// Display the sorted result
Object.getOwnPropertyNames(hash).forEach(n => {
  console.log("hash['" + n + "'] = " + hash[n]);
});

All of that aside, as I said in a comment on the question, I'm not sure why you'd want to do this. It seems like if we knew what your ultimate goal was, we might be able to point you in a more useful direction...

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
3

Lodash one-liner:

sorted = _(hash).toPairs().sortBy(1).fromPairs().value();

Live Example:

var data = {a: 'A', c: 'C', b: 'B'};
var hash = _(data).toPairs().sortBy(1).fromPairs().value();
Object.getOwnPropertyNames(hash).forEach(n => {
  snippet.log("hash['" + n + "'] = " + hash[n]);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.6.1/lodash.min.js"></script>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
georg
  • 211,518
  • 52
  • 313
  • 390
  • I respect your points, but I have to say yikes, this is so nasty, why is there not a better way to do this with JS is beyond me. – Alexander Mills Mar 18 '16 at 09:15
  • @AlexMills: Because JavaScript objects are not hash maps, and there is virtually never any reason to want the object properties to be in a particular order. – T.J. Crowder Mar 18 '16 at 09:19
  • @AlexMills: what you mean better way? There's no built-in function for that because it's a silly idea. What are you trying to do, ultimately? – georg Mar 18 '16 at 09:22
  • 1
    Good answer, using the power of the lib the OP already has. I would recommend listing the caveats or linking to my answer saying "See the caveats here." – T.J. Crowder Mar 18 '16 at 09:24
  • Actually, I have also needed Promise.all to work with objects with values as promises and not just arrays of promises, but it is not implemented. I am not doing web programming, I am creating a fancy test library for node.js and that's why my use cases are a bit out there. – Alexander Mills Mar 18 '16 at 10:05
  • @AlexMills: so, `Promise.all(Object.values(hash))` is what you're looking for? – georg Mar 18 '16 at 10:44
  • no, unfortunately I wanted Promise.all to map the values object of the object to a new object with the same keys, – Alexander Mills Mar 18 '16 at 18:30
  • @AlexMills: ok, how about `Promise.all(Object.values(hash)).then(values => Object.keys(hash).reduce((o, k, i) => Object.assign(o, {k:values[i]}), {}))` then? – georg Mar 18 '16 at 18:50