Reputation: 20438
For example the following
var data = {
'States': ['NSW', 'VIC'],
'Countries': ['GBR', 'AUS'],
'Capitals': ['SYD', 'MEL']
}
for (var item in data) {
console.log(item);
}
prints
States
Countries
Capitals
Is there a way to sort alphabetically so that it prints
Capitals
Countries
States
Upvotes: 123
Views: 150295
Reputation: 112
This is old but I just wanted to give what I believe is a thorough, clean, and modern answer.
For those that say that objects are unordered, that is not true.
So let's say I create an object like so:
const obj = {};
obj.c = 'c';
obj.b = 'b';
obj[1] = 1;
obj[-1] = -1;
obj.a = 'a';
If I stringify the object it will look like:
{
"1": 1,
"c": "c",
"b": "b",
"a": "a",
"-1": -1
}
Notice how 1
is at the beginning despite not being the first key inserted; a
, b
, and c
are next in insertion order; and -1
is last. Given that -1 is less than 0, it is treated as a string key since it cannot be used to index a zero-indexed array. It was inserted last so it is in the last position.
A very clean way to sort your object in-place is by one-by-one deleting and reinserting properties in alphabetical order. This will change the insertion order of the properties.
const sortObjectProperties = (obj) => {
Object.entries(obj)
.sort(([a], [b]) => (a < b ? -1 : 1))
.forEach(([key, value]) => {
delete obj[key];
obj[key] = value;
});
return obj;
};
Keep in mind that index-like keys will still be first no matter what. -1 will always be after 0 for example despite "-" coming before "0" lexicographically.
Upvotes: 0
Reputation: 3217
function orderKeys(obj: {}) {
var keys = (Object.keys(obj) as Array<keyof typeof obj>).sort((k1, k2) => {
if (k1 < k2) return -1;
else if (k1 > k2) return +1;
else return 0;
});
var helpArr = {};
for (var elem of keys) {
helpArr[elem] = obj[elem];
delete obj[elem];
obj[elem] = helpArr[elem]
}
return obj;
}
var data = {
'States': ['NSW', 'VIC'],
'Countries': ['GBR', 'AUS'],
'Capitals': ['SYD', 'MEL']
}
reduce
const sorted = (Object.keys(data) as Array<keyof typeof data>).sort().reduce((r: any, k) => ({ ...r, [k]: data[k] }), {});
console.log(orderKeys(data)) # similar: console.log(sorted)
{
Capitals: [ 'SYD', 'MEL' ],
Countries: [ 'GBR', 'AUS' ],
States: [ 'NSW', 'VIC' ]
}
Upvotes: 0
Reputation: 7970
Yes, there is. Not within ECMAScript standard, but supported across browsers and Node.js, and apparently stable. See https://stackoverflow.com/a/23202095/645715.
EDIT: This returns an object in which the keys are ordered. You can use Object.keys(...)
to get the ordered keys from the object.
Why worry about object key order? The difference can matter in some applications, such as parsing XML with xml2js which represents XML as nested objects, and uses XML tags as hash keys.
There are a couple of notes:
Object.keys(obj)
for (var key in obj) {...}
may differ in Safari, FirefoxThe function returns an object with sorted keys inserted in alphabetic order:
function orderKeys(obj) {
var keys = Object.keys(obj).sort(function keyOrder(k1, k2) {
if (k1 < k2) return -1;
else if (k1 > k2) return +1;
else return 0;
});
var i, after = {};
for (i = 0; i < keys.length; i++) {
after[keys[i]] = obj[keys[i]];
delete obj[keys[i]];
}
for (i = 0; i < keys.length; i++) {
obj[keys[i]] = after[keys[i]];
}
return obj;
}
Here's a quick test:
var example = {
"3": "charlie",
"p:style": "c",
"berries": "e",
"p:nvSpPr": "a",
"p:txBody": "d",
"apples": "e",
"5": "eagle",
"p:spPr": "b"
}
var obj = orderKeys(example);
this returns
{ '3': 'charlie',
'5': 'eagle',
apples: 'e',
berries: 'e',
'p:nvSpPr': 'a',
'p:spPr': 'b',
'p:style': 'c',
'p:txBody': 'd' }
You can then get the ordered keys as:
Object.keys(obj)
Which returns
["3", "5", "apples", "berries", "p:nvSpPr", "p:spPr", "p:style", "p:txBody"]
Upvotes: 21
Reputation: 133
Here is a nice solution if you have a more complex object where some of the properties are also objects.
const sortObject = (obj: any) => {
const sorted = Object.keys(obj)
.sort()
.reduce((accumulator, key) => {
if (typeof obj[key] === "object") {
// recurse nested properties that are also objects
if (obj[key] == null) {
accumulator[key] = null;
} else if (isArray(obj[key])) {
accumulator[key] = obj[key].map((item: any) => {
if (typeof item === "object") {
return sortObject(item);
} else {
return item;
}
});
} else {
accumulator[key] = sortObject(obj[key]);
}
} else {
accumulator[key] = obj[key];
}
return accumulator;
}, {});
return sorted;
};
Upvotes: 3
Reputation: 556
You can also sort it in this way.
const data = {
States: ['NSW', 'VIC'],
Countries: ['GBR', 'AUS'],
Capitals: ['SYD', 'MEL']
}
const sortedObject = Object.fromEntries(Object.entries(data).sort())
I cannot explain in detail why it works but apparently it's working flawlessly :) Try yourself if you're not believing me :D
Note that your browser or Node.js version should support Object.fromEntries
For browser compatibility check bottom of this page
For Node, it will work for versions from 12 and higher. For Deno from the v1.
Upvotes: 26
Reputation: 138
I would add this as a comment to @prototype's post, but my rep isn't high enough.
If anyone needs a version of orderKeys
that @prototype wrote that is compliant with eslint-config-airbnb
:
/**
* Returns and modifies the input object so that its keys are returned in sorted
* order when `Object.keys(obj)` is invoked
*
* @param {object} obj The object to have its keys sorted
*
* @returns {object} The inputted object with its keys in sorted order
*/
const orderKeys = (obj) => {
// Complying with `no-param-reassign`, and JavaScript seems to assign by reference here
const newObj = obj;
// Default `.sort()` chained method seems to work for me
const keys = Object.keys(newObj).sort();
const after = {};
// Add keys to `after` in sorted order of `obj`'s keys
keys.forEach((key) => {
after[key] = newObj[key];
delete newObj[key];
});
// Add keys back to `obj` in sorted order
keys.forEach((key) => {
newObj[key] = after[key];
});
return newObj;
};
Using @prototype's tests:
const example = {
3: 'charlie',
'p:style': 'c',
berries: 'e',
'p:nvSpPr': 'a',
'p:txBody': 'd',
apples: 'e',
5: 'eagle',
p:spPr: 'b'
}
const obj = orderKeys(example);
console.log(obj);
console.log(Object.keys(obj));
Outputs the following:
Babel Compiler v6.4.4
Copyright (c) 2014-2015 Sebastian McKenzie
{ 3: 'charlie',
5: 'eagle',
apples: 'e',
berries: 'e',
'p:nvSpPr': 'a',
'p:spPr': 'b',
'p:style': 'c',
'p:txBody': 'd' }
[ '3',
'5',
'apples',
'berries',
'p:nvSpPr',
'p:spPr',
'p:style',
'p:txBody' ]
For whatever it's worth, I needed this in my React app so that I could sort the options of a dropdown that was based on a state
object, assigned after a response from my API.
Initially I did
return (
// ...
<Select
options={Object.keys(obj).sort()}
// ...
/>
// ...
);
But realized the .sort()
method would be invoked on each re-render, hence needing @prototype's implementation of orderKeys
.
https://stackoverflow.com/users/645715/prototype
Upvotes: 6
Reputation: 71
Here's a one-liner to sort an object's keys using lodash
_.chain(obj).toPairs().sortBy(0).fromPairs().value()
With your example data:
var data = {
'States': ['NSW', 'VIC'],
'Countries': ['GBR', 'AUS'],
'Capitals': ['SYD', 'MEL']
}
data = _.chain(data)
.toPairs() // turn the object into an array of [key, value] pairs
.sortBy(0) // sort these pairs by index [0] which is [key]
.fromPairs() // convert array of pairs back into an object {key: value}
.value() // return value
/*
{
Capitals: [ 'SYD', 'MEL' ],
Countries: [ 'GBR', 'AUS' ],
States: [ 'NSW', 'VIC' ]
}
*/
Upvotes: 3
Reputation: 649
here's a nice functional solution:
basically,
Object.keys
sort
the keysES5 Solution:
not_sorted = {b: false, a: true};
sorted = Object.keys(not_sorted)
.sort()
.reduce(function (acc, key) {
acc[key] = not_sorted[key];
return acc;
}, {});
console.log(sorted) //{a: true, b: false}
ES6 Solution:
not_sorted = {b: false, a: true}
sorted = Object.keys(not_sorted)
.sort()
.reduce((acc, key) => ({
...acc, [key]: not_sorted[key]
}), {})
console.log(sorted) //{a: true, b: false}
Upvotes: 64
Reputation: 417
If conversion to an array does not suit your template and you know the keys of your object you can also do something like this:
In your controller define an array with the keys in the correct order:
this.displayOrder = [
'firstKey',
'secondKey',
'thirdKey'
];
In your template repeat the keys of your displayOrder and then use ng-init to reference back to your object.
<div ng-repeat="key in ctrl.displayOrder" ng-init="entry = ctrl.object[key]">
{{ entry.detail }}
</div>
Upvotes: -3
Reputation:
Not within the object itself: the property collection of an object is unordered.
One thing you could do is use Object.keys()
, and sort the Array, then iterate it.
Object.keys(data)
.sort()
.forEach(function(v, i) {
console.log(v, data[v]);
});
Patches (implementations) for browsers that do not support ECMAScript 5th edition:
Upvotes: 171