Сортировать объект JavaScript по ключу
Мне нужно отсортировать объекты JavaScript по ключу.
Отсюда следующее:
{ 'b' : 'asdsad', 'c' : 'masdas', 'a' : 'dsfdsfsdf' }
Станет:
{ 'a' : 'dsfdsfsdf', 'b' : 'asdsad', 'c' : 'masdas' }
40 ответов
Другие ответы на этот вопрос устарели, никогда не соответствовали реальности реализации и официально стали неверными после публикации спецификации ES6/ES2015.
См. Раздел, посвященный порядку итераций свойств в " Изучении ES6 " Акселя Раушмайера:
Все методы, которые перебирают ключи свойств, делают это в том же порядке:
- Сначала все индексы Array, отсортированные по номерам.
- Затем все строковые ключи (которые не являются индексами), в том порядке, в котором они были созданы.
- Затем все символы в том порядке, в котором они были созданы.
Так что да, объекты JavaScript на самом деле упорядочены, и порядок их ключей / свойств может быть изменен.
Вот как вы можете отсортировать объект по ключам / свойствам в алфавитном порядке:
const unordered = {
'b': 'foo',
'c': 'bar',
'a': 'baz'
};
console.log(JSON.stringify(unordered));
// → '{"b":"foo","c":"bar","a":"baz"}'
const ordered = {};
Object.keys(unordered).sort().forEach(function(key) {
ordered[key] = unordered[key];
});
console.log(JSON.stringify(ordered));
// → '{"a":"baz","b":"foo","c":"bar"}'
использование var
вместо const
для совместимости с двигателями ES5.
JavaScript объекты 1 не упорядочены. Бессмысленно пытаться "сортировать" их. Если вы хотите перебрать свойства объекта, вы можете отсортировать ключи и затем получить связанные значения:
var myObj = {
'b': 'asdsadfd',
'c': 'masdasaf',
'a': 'dsfdsfsdf'
},
keys = [],
k, i, len;
for (k in myObj) {
if (myObj.hasOwnProperty(k)) {
keys.push(k);
}
}
keys.sort();
len = keys.length;
for (i = 0; i < len; i++) {
k = keys[i];
console.log(k + ':' + myObj[k]);
}
Альтернативная реализация с использованием Object.keys
крутость:
var myObj = {
'b': 'asdsadfd',
'c': 'masdasaf',
'a': 'dsfdsfsdf'
},
keys = Object.keys(myObj),
i, len = keys.length;
keys.sort();
for (i = 0; i < len; i++) {
k = keys[i];
console.log(k + ':' + myObj[k]);
}
1 Не быть педантичным, но нет такого понятия, как объект JSON.
Многие люди упоминают, что "объекты не могут быть отсортированы", но после этого они дают вам решение, которое работает. Парадокс, не правда ли?
Никто не упоминает, почему эти решения работают. Они есть, потому что в большинстве реализаций браузера значения в объектах хранятся в том порядке, в котором они были добавлены. Поэтому, если вы создаете новый объект из отсортированного списка ключей, он возвращает ожидаемый результат.
И я думаю, что мы могли бы добавить еще одно решение - функциональный путь ES5:
function sortObject(obj) {
return Object.keys(obj).sort().reduce(function (result, key) {
result[key] = obj[key];
return result;
}, {});
}
ES2015 версия выше (отформатированная в "однострочник"):
function sortObject(o) {
return Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {});
}
Краткое объяснение приведенных выше примеров (как указано в комментариях):
Object.keys
дает нам список ключей в предоставленном объекте (obj
или же o
), затем мы сортируем те, используя алгоритм сортировки по умолчанию, затем .reduce
используется для преобразования этого массива обратно в объект, но на этот раз со всеми отсортированными ключами.
Ребята, я образно шокирован! Конечно, все ответы несколько устарели, но никто не упомянул стабильность сортировки! Так что терпите меня, я сделаю все возможное, чтобы ответить на сам вопрос и уточнить детали здесь. Поэтому я собираюсь извиниться, сейчас будет много читать.
Поскольку это 2018 год, я буду использовать только ES6, все полифилы доступны в документах MDN, которые я приведу в данной части.
Ответ на вопрос:
Если ваши ключи только цифры, то вы можете безопасно использовать Object.keys()
вместе с Array.prototype.reduce()
вернуть отсортированный объект:
// Only numbers to show it will be sorted.
const testObj = {
'2000': 'Articel1',
'4000': 'Articel2',
'1000': 'Articel3',
'3000': 'Articel4',
};
// I'll explain what reduces does after the answer.
console.log(Object.keys(testObj).reduce((accumulator, currentValue) => {
accumulator[currentValue] = testObj[currentValue];
return accumulator;
}, {}));
/**
* expected output:
* {
* '1000': 'Articel3',
* '2000': 'Articel1',
* '3000': 'Articel4',
* '4000': 'Articel2'
* }
*/
// if needed here is the one liner:
console.log(Object.keys(testObj).reduce((a, c) => (a[c] = testObj[c], a), {}));
Однако, если вы работаете со строками, я настоятельно рекомендую цепочку Array.prototype.sort()
во все это:
// String example
const testObj = {
'a1d78eg8fdg387fg38': 'Articel1',
'z12989dh89h31d9h39': 'Articel2',
'f1203391dhj32189h2': 'Articel3',
'b10939hd83f9032003': 'Articel4',
};
// Chained sort into all of this.
console.log(Object.keys(testObj).sort().reduce((accumulator, currentValue) => {
accumulator[currentValue] = testObj[currentValue];
return accumulator;
}, {}));
/**
* expected output:
* {
* a1d78eg8fdg387fg38: 'Articel1',
* b10939hd83f9032003: 'Articel4',
* f1203391dhj32189h2: 'Articel3',
* z12989dh89h31d9h39: 'Articel2'
* }
*/
// again the one liner:
console.log(Object.keys(testObj).sort().reduce((a, c) => (a[c] = testObj[c], a), {}));
Если кому-то интересно, что делает сокращение:
// Will return Keys of object as an array (sorted if only numbers or single strings like a,b,c).
Object.keys(testObj)
// Chaining reduce to the returned array from Object.keys().
// Array.prototype.reduce() takes one callback
// (and another param look at the last line) and passes 4 arguments to it:
// accumulator, currentValue, currentIndex and array
.reduce((accumulator, currentValue) => {
// setting the accumulator (sorted new object) with the actual property from old (unsorted) object.
accumulator[currentValue] = testObj[currentValue];
// returning the newly sorted object for the next element in array.
return accumulator;
// the empty object {} ist the initial value for Array.prototype.reduce().
}, {});
Если необходимо, вот объяснение для одного лайнера:
Object.keys(testObj).reduce(
// Arrow function as callback parameter.
(a, c) =>
// parenthesis return! so we can safe the return and write only (..., a);
(a[c] = testObj[c], a)
// initial value for reduce.
,{}
);
- Документы для сокращения: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
- Зачем использовать круглые скобки в операторах возврата JavaScript: http://jamesknelson.com/javascript-return-parenthesis/
Почему сортировка немного сложна:
Короче Object.keys()
вернет массив с тем же порядком, что и обычный цикл:
const object1 = {
a: 'somestring',
b: 42,
c: false
};
console.log(Object.keys(object1));
// expected output: Array ["a", "b", "c"]
Object.keys () возвращает массив, элементы которого являются строками, соответствующими перечисляемым свойствам, найденным непосредственно на объекте. Порядок свойств такой же, как и при циклическом переключении свойств объекта вручную.
Sidenote - вы можете использовать Object.keys()
для массивов также имейте в виду, что индекс будет возвращен:
// simple array
const arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']
Но это не так просто, как показывают эти примеры, объекты реального мира могут содержать цифры и буквы алфавита или даже символы (пожалуйста, не делайте этого).
Вот пример со всеми ними в одном объекте:
// This is just to show what happens, please don't use symbols in keys.
const testObj = {
'1asc': '4444',
1000: 'a',
b: '1231',
'#01010101010': 'asd',
2: 'c'
};
console.log(Object.keys(testObj));
// output: [ '2', '1000', '1asc', 'b', '#01010101010' ]
Теперь, если мы используем Array.prototype.sort()
на массиве выше выходных изменений:
console.log(Object.keys(testObj).sort());
// output: [ '#01010101010', '1000', '1asc', '2', 'b' ]
Вот цитата из документов:
Метод sort() сортирует элементы массива на месте и возвращает массив. Сортировка не обязательно стабильна. Порядок сортировки по умолчанию соответствует строковым кодовым точкам Unicode.
Временная и пространственная сложность сортировки не может быть гарантирована, так как она зависит от реализации.
Вы должны убедиться, что один из них возвращает желаемый результат для вас. В реальных примерах люди склонны смешивать вещи, особенно если вы используете разные информационные входы, такие как API и базы данных вместе.
Так в чем же дело?
Ну, есть две статьи, которые должен понять каждый программист:
В компьютерной науке алгоритм на месте - это алгоритм, который преобразует входные данные без использования вспомогательной структуры данных. Однако для вспомогательных переменных допускается небольшое количество дополнительного пространства для хранения. Входные данные обычно перезаписываются выходными данными при выполнении алгоритма. Алгоритм на месте обновляет входную последовательность только путем замены или замены элементов. Алгоритм, который не на месте, иногда называют не на месте или не на месте.
Так что в основном наш старый массив будет перезаписан! Это важно, если вы хотите сохранить старый массив по другим причинам. Так что имейте это в виду.
Алгоритмы стабильной сортировки сортируют идентичные элементы в том же порядке, в котором они появляются во входных данных. При сортировке некоторых видов данных при определении порядка сортировки проверяется только часть данных. Например, в примере сортировки карт справа карты сортируются по их рангу, а их масть игнорируется. Это позволяет использовать несколько разных правильно отсортированных версий исходного списка. Алгоритмы стабильной сортировки выбирают один из них в соответствии со следующим правилом: если два элемента сравниваются как равные, как две 5 карты, то их относительный порядок будет сохранен, так что, если одна из них предшествовала другой на входе, она также будет опережать других в выводе.
Пример стабильной сортировки по игральным картам. Когда карты сортируются по рангу со стабильной сортировкой, две 5-ки должны оставаться в том же порядке в отсортированном выводе, в котором они были изначально. Когда они сортируются с нестабильной сортировкой, 5-и могут оказаться в противоположном порядок в отсортированном выводе.
Это показывает, что сортировка верна, но она изменилась. Таким образом, в реальном мире, даже если сортировка правильная, мы должны убедиться, что получаем то, что ожидаем! Это очень важно, имейте это в виду. Дополнительные примеры JavaScript можно найти в Array.prototype.sort() - docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
Это 2019 год, и у нас есть 2019 способ решить эту проблему:)
Object.fromEntries(Object.entries({b: 3, a:8, c:1}).sort())
Вот 1 лайнер
var data = { zIndex:99,
name:'sravan',
age:25,
position:'architect',
amount:'100k',
manager:'mammu'
};
console.log(Object.entries(data).sort().reduce( (o,[k,v]) => (o[k]=v,o), {} ));
Это работает для меня
/**
* Return an Object sorted by it's Key
*/
var sortObjectByKey = function(obj){
var keys = [];
var sorted_obj = {};
for(var key in obj){
if(obj.hasOwnProperty(key)){
keys.push(key);
}
}
// sort keys
keys.sort();
// create new array based on Sorted Keys
jQuery.each(keys, function(i, key){
sorted_obj[key] = obj[key];
});
return sorted_obj;
};
Это старый вопрос, но, следуя ответу Матиаса Биненса, я сделал короткую версию для сортировки текущего объекта без особых накладных расходов.
Object.keys(unordered).sort().forEach(function(key) {
var value = unordered[key];
delete unordered[key];
unordered[key] = value;
});
после выполнения кода для самого "неупорядоченного" объекта ключи будут отсортированы в алфавитном порядке.
Предположим, что это может быть полезно в отладчике VisualStudio, который показывает неупорядоченные свойства объекта.
(function(s){var t={};Object.keys(s).sort().forEach(function(k){t[k]=s[k]});return t})({b:2,a:1,c:3})
Используя lodash это будет работать:
some_map = { 'b' : 'asdsad', 'c' : 'masdas', 'a' : 'dsfdsfsdf' }
// perform a function in order of ascending key
_(some_map).keys().sort().each(function (key) {
var value = some_map[key];
// do something
});
// or alternatively to build a sorted list
sorted_list = _(some_map).keys().sort().map(function (key) {
var value = some_map[key];
// return something that shall become an item in the sorted list
}).value();
Просто пища для размышлений.
На самом деле я очень удивлен, что было дано более 30 ответов, но ни один из них не дал полного и глубокого решения этой проблемы. У некоторых было поверхностное решение, в то время как у других было глубокое, но ошибочное (он выйдет из строя, если undefined, функция или символ будут в json).
Вот полное решение:
function sortObject(unordered, sortArrays = false) {
if (!unordered || typeof unordered !== 'object') {
return unordered;
}
if (Array.isArray(unordered)) {
const newArr = unordered.map((item) => sortObject(item, sortArrays));
if (sortArrays) {
newArr.sort();
}
return newArr;
}
const ordered = {};
Object.keys(unordered)
.sort()
.forEach(function (key) {
ordered[key] = sortObject(unordered[key], sortArrays);
});
return ordered;
}
const json = {
b: 5,
a: [2, 1],
d: {
b: undefined,
a: null,
c: false,
d: true,
g: '1',
f: [],
h: {},
i: 1n,
j: () => {},
k: Symbol('a')
},
c: [
{
b: 1,
a: 1
}
]
};
console.log(sortObject(json, true));
function order(unordered)
{
return _.object(_.sortBy(_.pairs(unordered),function(o){return o[0]}));
}
Если вы не доверяете своему браузеру в сохранении порядка ключей, я настоятельно рекомендую полагаться на упорядоченный массив парных массивов ключ-значение.
_.sortBy(_.pairs(c),function(o){return o[0]})
// if keys are char/string
const sortObject = (obj) => Object.fromEntries(Object.entries(obj).sort( ));
let obj = { c: 3, a: 1 };
obj = sortObject(obj)
// if keys are numbers
const sortObject = (obj) => Object.fromEntries(Object.entries(obj).sort( (a,b)=>a-b ));
let obj = { 3: 'c', 1: 'a' };
obj = sortObject(obj)
function sortObjectKeys(obj){
return Object.keys(obj).sort().reduce((acc,key)=>{
acc[key]=obj[key];
return acc;
},{});
}
sortObjectKeys({
telephone: '069911234124',
name: 'Lola',
access: true,
});
Может быть, немного более элегантная форма:
/**
* Sorts a key-value object by key, maintaining key to data correlations.
* @param {Object} src key-value object
* @returns {Object}
*/
var ksort = function ( src ) {
var keys = Object.keys( src ),
target = {};
keys.sort();
keys.forEach(function ( key ) {
target[ key ] = src[ key ];
});
return target;
};
// Usage
console.log(ksort({
a:1,
c:3,
b:2
}));
PS и то же самое с синтаксисом ES6+:
function ksort( src ) {
const keys = Object.keys( src );
keys.sort();
return keys.reduce(( target, key ) => {
target[ key ] = src[ key ];
return target;
}, {});
};
Вот однострочное решение (не самое эффективное, но когда дело доходит до тонких объектов, как в вашем примере, я бы предпочел использовать собственные функции JS, а затем испортить неаккуратные циклы)
const unordered = { 'b' : 'asdsad', 'c' : 'masdas', 'a' : 'dsfdsfsdf' }
const ordered = Object.fromEntries(Object.entries(unordered).sort())
console.log(ordered); // a->b->c
Рекурсивная сортировка для вложенных объектов и массивов
function sortObjectKeys(obj){
return Object.keys(obj).sort().reduce((acc,key)=>{
if (Array.isArray(obj[key])){
acc[key]=obj[key].map(sortObjectKeys);
}
if (typeof obj[key] === 'object'){
acc[key]=sortObjectKeys(obj[key]);
}
else{
acc[key]=obj[key];
}
return acc;
},{});
}
// test it
sortObjectKeys({
telephone: '069911234124',
name: 'Lola',
access: true,
cars: [
{name: 'Family', brand: 'Volvo', cc:1600},
{
name: 'City', brand: 'VW', cc:1200,
interior: {
wheel: 'plastic',
radio: 'blaupunkt'
}
},
{
cc:2600, name: 'Killer', brand: 'Plymouth',
interior: {
wheel: 'wooden',
radio: 'earache!'
}
},
]
});
Вот чистая версия на основе lodash, которая работает с вложенными объектами
/**
* Sort of the keys of an object alphabetically
*/
const sortKeys = function(obj) {
if(_.isArray(obj)) {
return obj.map(sortKeys);
}
if(_.isObject(obj)) {
return _.fromPairs(_.keys(obj).sort().map(key => [key, sortKeys(obj[key])]));
}
return obj;
};
Было бы еще чище, если бы у Лодаша был toObject()
метод...
Есть замечательный проект @sindresorhus, называемый sort-keys, который работает потрясающе.
Вы можете проверить его исходный код здесь:
https://github.com/sindresorhus/sort-keys
Или вы можете использовать его с npm:
$ npm install --save sort-keys
Вот также примеры кода из его readme
const sortKeys = require('sort-keys');
sortKeys({c: 0, a: 0, b: 0});
//=> {a: 0, b: 0, c: 0}
sortKeys({b: {b: 0, a: 0}, a: 0}, {deep: true});
//=> {a: 0, b: {a: 0, b: 0}}
sortKeys({c: 0, a: 0, b: 0}, {
compare: (a, b) => -a.localeCompare(b)
});
//=> {c: 0, b: 0, a: 0}
Object.keys(unordered).sort().reduce(
(acc,curr) => ({...acc, [curr]:unordered[curr]})
, {}
)
Используйте этот код, если у вас есть вложенные объекты или если у вас есть вложенный массив obj.
var sortObjectByKey = function(obj){
var keys = [];
var sorted_obj = {};
for(var key in obj){
if(obj.hasOwnProperty(key)){
keys.push(key);
}
}
// sort keys
keys.sort();
// create new array based on Sorted Keys
jQuery.each(keys, function(i, key){
var val = obj[key];
if(val instanceof Array){
//do for loop;
var arr = [];
jQuery.each(val,function(){
arr.push(sortObjectByKey(this));
});
val = arr;
}else if(val instanceof Object){
val = sortObjectByKey(val)
}
sorted_obj[key] = val;
});
return sorted_obj;
};
Как уже упоминалось, объекты неупорядочены.
Тем не мение...
Вы можете найти эту идиому полезной:
var o = { 'b' : 'asdsad', 'c' : 'masdas', 'a' : 'dsfdsfsdf' };
var kv = [];
for (var k in o) {
kv.push([k, o[k]]);
}
kv.sort()
Затем вы можете перебирать kv и делать все, что пожелаете.
> kv.sort()
[ [ 'a', 'dsfdsfsdf' ],
[ 'b', 'asdsad' ],
[ 'c', 'masdas' ] ]
Просто используйте lodash, чтобы разархивировать map и sortBy первое значение пары и снова заархивировать, чтобы вернуть отсортированный ключ.
Если вы хотите, чтобы значение сортировки было изменено, измените индекс пары на 1 вместо 0
var o = { 'b' : 'asdsad', 'c' : 'masdas', 'a' : 'dsfdsfsdf' };
console.log(_(o).toPairs().sortBy(0).fromPairs().value())
Сортирует ключи рекурсивно, сохраняя ссылки.
function sortKeys(o){
if(o && o.constructor === Array)
o.forEach(i=>sortKeys(i));
else if(o && o.constructor === Object)
Object.entries(o).sort((a,b)=>a[0]>b[0]?1:-1).forEach(e=>{
sortKeys(e[1]);
delete o[e[0]];
o[e[0]] = e[1];
});
}
Пример:
let x = {d:3, c:{g:20, a:[3,2,{s:200, a:100}]}, a:1};
let y = x.c;
let z = x.c.a[2];
sortKeys(x);
console.log(x); // {a: 1, c: {a: [3, 2, {a: 1, s: 2}], g: 2}, d: 3}
console.log(y); // {a: [3, 2, {a: 100, s: 200}}, g: 20}
console.log(z); // {a: 100, s: 200}
Это легкое решение для всего, что мне нужно для сортировки JSON.
function sortObj(obj) {
if (typeof obj !== "object" || obj === null)
return obj;
if (Array.isArray(obj))
return obj.map((e) => sortObj(e)).sort();
return Object.keys(obj).sort().reduce((sorted, k) => {
sorted[k] = sortObj(obj[k]);
return sorted;
}, {});
}
Решение:
function getSortedObject(object) {
var sortedObject = {};
var keys = Object.keys(object);
keys.sort();
for (var i = 0, size = keys.length; i < size; i++) {
key = keys[i];
value = object[key];
sortedObject[key] = value;
}
return sortedObject;
}
// Test run
getSortedObject({d: 4, a: 1, b: 2, c: 3});
Объяснение:
Многие среды выполнения JavaScript хранят значения внутри объекта в порядке их добавления.
Чтобы отсортировать свойства объекта по их ключам, вы можете использовать функцию Object.keys, которая будет возвращать массив ключей. Затем массив ключей можно отсортировать с помощью метода Array.prototype.sort(), который сортирует элементы массива на месте (нет необходимости назначать их новой переменной).
Как только ключи отсортированы, вы можете начать использовать их один за другим, чтобы получить доступ к содержимому старого объекта, чтобы заполнить новый объект (который теперь отсортирован).
Ниже приведен пример процедуры (вы можете проверить ее в целевых браузерах):
/**
* Returns a copy of an object, which is ordered by the keys of the original object.
*
* @param {Object} object - The original object.
* @returns {Object} Copy of the original object sorted by keys.
*/
function getSortedObject(object) {
// New object which will be returned with sorted keys
var sortedObject = {};
// Get array of keys from the old/current object
var keys = Object.keys(object);
// Sort keys (in place)
keys.sort();
// Use sorted keys to copy values from old object to the new one
for (var i = 0, size = keys.length; i < size; i++) {
key = keys[i];
value = object[key];
sortedObject[key] = value;
}
// Return the new object
return sortedObject;
}
/**
* Test run
*/
var unsortedObject = {
d: 4,
a: 1,
b: 2,
c: 3
};
var sortedObject = getSortedObject(unsortedObject);
for (var key in sortedObject) {
var text = "Key: " + key + ", Value: " + sortedObject[key];
var paragraph = document.createElement('p');
paragraph.textContent = text;
document.body.appendChild(paragraph);
}
Примечание. Object.keys - это метод ECMAScript 5.1, но для старых браузеров используется полифил:
if (!Object.keys) {
Object.keys = function (object) {
var key = [];
var property = undefined;
for (property in object) {
if (Object.prototype.hasOwnProperty.call(object, property)) {
key.push(property);
}
}
return key;
};
}
Вы можете сортировать так:
Object.fromEntries(Object.entries(obj).sort(([a],[b]) => a.localeCompare(b)))
Простой и читаемый фрагмент кода, используя lodash.
Ключ нужно заключать в кавычки только при вызове sortBy. Это не должно быть в кавычках в самих данных.
_.sortBy(myObj, "key")
Кроме того, ваш второй параметр для отображения неверен. Это должна быть функция, но проще использовать pluck.
_.map( _.sortBy(myObj, "key") , "value");