按键排序JavaScript对象

839
我需要按键名对JavaScript对象进行排序。
因此以下内容:
{ 'b' : 'asdsad', 'c' : 'masdas', 'a' : 'dsfdsfsdf' }

将变成:

{ 'a' : 'dsfdsfsdf', 'b' : 'asdsad', 'c' : 'masdas' }

3
可能是重复的问题:在Javascript中对JSON对象进行排序 - abraham
71
在2011年(发布此问题时),ECMAScript规范指出JavaScript对象没有内在顺序——观察到的顺序取决于具体实现,因此不能依赖它。然而,事实证明所有JavaScript引擎都实现了更多或更少相同的语义,因此这些语义现在已在ES6中标准化。因此,许多下面的答案过去是正确的,符合规范,但现在不再正确。 - Mathias Bynens
1
简单来说,当您需要比较或哈希结果时,请使用排序的 stringify:https://www.npmjs.com/package/json-stable-stringify - exebook
7
请注意,自从 Object.entriesObject.fromEntries 被添加到 JS 中以来,可以使用一个漂亮的一行代码实现此操作:Object.fromEntries(Object.entries(obj).sort()) - Mike 'Pomax' Kamermans
递归按键对JavaScript对象进行排序 - https://dev59.com/64Hba4cB1Zd3GeqPPlKn#73160202 - 即对对象及其包含的对象进行排序 - danday74
显示剩余5条评论
38个回答

5

有一个由@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}

4

如果您有嵌套对象或嵌套数组对象,请使用此代码。

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;
};

6
作者并未说明他使用jQuery,问题也没有标记jQuery。如果您能提供一个纯JavaScript的解决方案就更好了。 - rusmus
谢谢@Phani,这正是我所需要的。 - Will Sheppard

4

如前所述,对象是无序的。

然而...

您可能会发现这个习语很有用:

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' ] ]

1
在现实生活中和 ES6 对象中,对象是有序的。 - Mathias Bynens

3
递归排序键并保留引用。
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}

不错的代码片段!delete o[e[0]]; 的目的是什么?我们不能只赋值吗? - Boyang
似乎只有对不存在的键进行新分配才会将该键附加到末尾。如果删除被注释掉,那么键就不会被排序,至少在Chrome中是这样。 - brunettdan
无论如何,这似乎是一种徒劳的尝试,因为根据规范,js对象键没有顺序。我们只是在利用obj和log函数的实现细节。 - Boyang
是的,当键的顺序很重要时,我使用 Map:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map - brunettdan

3
这适用于 2023年1月 以后的时间。

var obj = {
'b':'cVal',
'a':'aVal',
'c':'bVal'
}
console.log(JSON.stringify(obj))
obj = Object.fromEntries(Object.entries(obj).sort())
console.log(JSON.stringify(obj));


3

只需使用lodash解压缩map并按对的第一个值进行排序,然后再次压缩,它将返回已排序的键。

如果要按值排序,请将对的索引更改为1而不是0

var o = { 'b' : 'asdsad', 'c' : 'masdas', 'a' : 'dsfdsfsdf' };
console.log(_(o).toPairs().sortBy(0).fromPairs().value())

enter image description here


请使用[编辑]链接来解释这段代码的工作原理,而不仅仅是提供代码,因为解释更有可能帮助未来的读者。另请参见[答案]。来源 - Jed Fox

3
这是一个轻量级的解决方案,满足我在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;
    }, {});
}

注意:这个排序不仅会对键进行排序,还会对数组项进行排序! - Venryx

2
使用lodash编写简单易懂的代码片段。
调用sortBy时,只需要将键放在引号中。在数据本身中,它不必用引号括起来。
_.sortBy(myObj, "key")

此外,您对map的第二个参数是错误的。它应该是一个函数,但使用pluck更容易。
_.map( _.sortBy(myObj, "key") , "value");

_.sortBy(myObj, "key") 将按照集合项的键(即 myObj.item.key)而不是 myObj 本身(其中 'item' 是一个对象,myObj.item)对集合进行排序。 - matharden

2

我将一些Java枚举转换为javascript对象。

这些对象对我来说返回了正确的数组。但是如果对象键是混合类型(字符串、整数、字符),就会出现问题。

var Helper = {
    isEmpty: function (obj) {
        return !obj || obj === null || obj === undefined || Array.isArray(obj) && obj.length === 0;
    },

    isObject: function (obj) {
        return (typeof obj === 'object');
    },

    sortObjectKeys: function (object) {
        return Object.keys(object)
            .sort(function (a, b) {
                c = a - b;
                return c
            });
    },
    containsItem: function (arr, item) {
        if (arr && Array.isArray(arr)) {
            return arr.indexOf(item) > -1;
        } else {
            return arr === item;
        }
    },

    pushArray: function (arr1, arr2) {
        if (arr1 && arr2 && Array.isArray(arr1)) {
            arr1.push.apply(arr1, Array.isArray(arr2) ? arr2 : [arr2]);
        }
    }
};

function TypeHelper() {
    var _types = arguments[0],
        _defTypeIndex = 0,
        _currentType,
        _value;

    if (arguments.length == 2) {
        _defTypeIndex = arguments[1];
    }

    Object.defineProperties(this, {
        Key: {
            get: function () {
                return _currentType;
            },
            set: function (val) {
                _currentType.setType(val, true);
            },
            enumerable: true
        },
        Value: {
            get: function () {
                return _types[_currentType];
            },
            set: function (val) {
                _value.setType(val, false);
            },
            enumerable: true
        }
    });

    this.getAsList = function (keys) {
        var list = [];
        Helper.sortObjectKeys(_types).forEach(function (key, idx, array) {
            if (key && _types[key]) {

                if (!Helper.isEmpty(keys) && Helper.containsItem(keys, key) || Helper.isEmpty(keys)) {
                    var json = {};
                    json.Key = key;
                    json.Value = _types[key];
                    Helper.pushArray(list, json);
                }
            }
        });
        return list;
    };

    this.setType = function (value, isKey) {
        if (!Helper.isEmpty(value)) {
            Object.keys(_types).forEach(function (key, idx, array) {
                if (Helper.isObject(value)) {
                    if (value && value.Key == key) {
                        _currentType = key;
                    }
                } else if (isKey) {
                    if (value && value.toString() == key.toString()) {
                        _currentType = key;
                    }
                } else if (value && value.toString() == _types[key]) {
                    _currentType = key;
                }
            });
        } else {
            this.setDefaultType();
        }
        return isKey ? _types[_currentType] : _currentType;
    };

    this.setTypeByIndex = function (index) {
        var keys = Helper.sortObjectKeys(_types);
        for (var i = 0; i < keys.length; i++) {
            if (index === i) {
                _currentType = keys[index];
                break;
            }
        }
    };

    this.setDefaultType = function () {
        this.setTypeByIndex(_defTypeIndex);
    };

    this.setDefaultType();
}


var TypeA = {
    "-1": "Any",
    "2": "2L",
    "100": "100L",
    "200": "200L",
    "1000": "1000L"
};

var TypeB = {
    "U": "Any",
    "W": "1L",
    "V": "2L",
    "A": "100L",
    "Z": "200L",
    "K": "1000L"
};
console.log('keys of TypeA', Helper.sortObjectKeys(TypeA));//keys of TypeA ["-1", "2", "100", "200", "1000"]

console.log('keys of TypeB', Helper.sortObjectKeys(TypeB));//keys of TypeB ["U", "W", "V", "A", "Z", "K"]

var objectTypeA = new TypeHelper(TypeA),
    objectTypeB = new TypeHelper(TypeB);

console.log('list of objectA = ', objectTypeA.getAsList());
console.log('list of objectB = ', objectTypeB.getAsList());

Types:

var TypeA = {
    "-1": "Any",
    "2": "2L",
    "100": "100L",
    "200": "200L",
    "1000": "1000L"
};

var TypeB = {
    "U": "Any",
    "W": "1L",
    "V": "2L",
    "A": "100L",
    "Z": "200L",
    "K": "1000L"
};


Sorted Keys(output):

Key list of TypeA -> ["-1", "2", "100", "200", "1000"]

Key list of TypeB -> ["U", "W", "V", "A", "Z", "K"]

2

解决方案:

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 的方法,但是这里提供了一个适用于旧版浏览器的 polyfill。

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;
  };
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接