0

I have a dictionary of objects like this:

originalJson = {
  "0":{
    "width":55,
    "offset":152
  },
  "A":{
    "width":66,
    "offset":0
  },
  "B":{
    "width":66,
    "offset":76
  }
};

I want to sort them by the offset property, so that the resulting object should look like this:

sortedJson = {
  "A":{
    "width":66,
    "offset":0
  },
  "B":{
    "width":66,
    "offset":76
  },
  "0":{
    "width":55,
    "offset":152
  }
};

Note that "A" and "B" would come before "0" because their offset is lower.

I believe that I'd need to chain a few iterations together in lodash to accomplish this. Here's where I've got:

var originalJson = {
  "0":{
    "width":55,
    "offset":152
  },
  "A":{
    "width":66,
    "offset":0
  },
  "B":{
    "width":66,
    "offset":76
  }
};

var sortedJson = _.chain(originalJson)
.map(function (val, key) {
  return { character: key, details: val };
})
.sortBy(function(o) {
  return o.details.offset;
})
.keyBy('character')
.mapValues('details')
.value();

$('#console').append(JSON.stringify(sortedJson,false,2));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
<pre id="console">
</pre>

Unfortunately, it just returns the objects in the same order as before. Thanks for any help in advance!

antun
  • 2,038
  • 2
  • 22
  • 34

1 Answers1

2

Objects are inherently unordered. You're sorting into an array (which works), but then you're converting back to an object (key/value pairs), so the result loses its ordering.

If you stop before the keyBy and mapValues, you'll see that you do have a sorted result:

var originalJson = {
  "0":{
    "width":55,
    "offset":152
  },
  "A":{
    "width":66,
    "offset":0
  },
  "B":{
    "width":66,
    "offset":76
  }
};

var sortedJson = _.chain(originalJson)
.map(function (val, key) {
  return { character: key, details: val };
})
.sortBy(function(o) {
  return o.details.offset;
})
.value();

$('#console').append(JSON.stringify(sortedJson,false,2));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
<pre id="console">
</pre>
user94559
  • 59,196
  • 6
  • 103
  • 103
  • @antun Luck? Keys in a JavaScript object have no order, so the order you see when you print them out or convert them to JSON is undefined (up to whoever wrote the implementation you're using). – user94559 May 24 '17 at 06:47
  • :) I don't think it can be luck. I expanded that other example to have 10 keys (A-J) and it always returns them sorted in the order I want: https://jsfiddle.net/karlovac/dtzjyfLj/5/ . The odds of scattering 10 letters and having them appear in alphabetical order have to be infinitesimally small. – antun May 24 '17 at 13:08
  • In that case, the luck is probably that you happen to be using an implementation (e.g. a browser) with some predictable behavior. See https://stackoverflow.com/questions/30076219/does-es6-introduce-a-well-defined-order-of-enumeration-for-object-properties/30919039#30919039 for a pretty long treatment of this for one version of JavaScript. – user94559 May 24 '17 at 14:54
  • I think you're right that it's to do with the implementation. I updated my stripped-down example to include a dictionary key that was a number as a string ("0"), and that ended up first in the sorted dictionary. So it looks like although lodash (or JS?) will sort the keys, it'll put the strings that convert to numbers first: https://jsfiddle.net/karlovac/dtzjyfLj/6/ – antun May 24 '17 at 16:51