按键排序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个回答

15

我其实很惊讶,这个问题已经有30多个答案了,但是没有一个给出了完整的深度解决方案。一些答案提供了浅显的解决方案,而另一些则提供了深入但是有缺陷的解决方案(如果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((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));

2
对我来说也是一样的!在发布我的答案之前,我浏览了一遍以找到解决问题的深层方法(与你的答案相同)。但后来我看到了你的答案,于是就给它点了赞。 :) - Venryx
1
你的答案是合法的。+1 - Junaid Atari

9

Underscore版本

function order(unordered)
{
return _.object(_.sortBy(_.pairs(unordered),function(o){return o[0]}));
}

如果您不信任浏览器来保持键的顺序,我强烈建议依靠按顺序排列的键值对数组。
_.sortBy(_.pairs(c),function(o){return o[0]})

1
更好的写法:_.object(_.sortBy(_.pairs(unordered), _.first)) - Afanasii Kurakin

9
// 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)

8
function sortObjectKeys(obj){
    return Object.keys(obj).sort().reduce((acc,key)=>{
        acc[key]=obj[key];
        return acc;
    },{});
}

sortObjectKeys({
    telephone: '069911234124',
    name: 'Lola',
    access: true,
});

也许添加一些关于你的代码的解释会更好。 - aristotll
1
获取对象的键,因此它是字符串数组,我对它们进行排序(sort()),由于它是字符串数组(键),所以我使用js数组的reduce()方法来减少它们。初始的acc是一个空对象{}(reducer的最后一个参数),回调函数(reducer)将有序键序列的源对象的值分配给空对象。 - user3286817

8

const sortObjectByKeys = (object, {desc = false} = {}) => Object.fromEntries(
  Object.entries(object).sort(([k1], [k2]) => k1 < k2 ^ desc ? -1 : 1),
)

const object = { b: 'asdsad', c: 'masdas', a: 'dsfdsfsdf' }

const orderedObject    = sortObjectByKeys(object)
const orderedObjectRev = sortObjectByKeys(object, {desc: true})

console.log({orderedObject, orderedObjectRev})


1
简单而美妙! - 1antares1

8
这里有一个一行解决方案(虽然不是最有效的,但是对于像你的示例中那样的薄物体,我宁愿使用本机JS函数而不是搞砸循环)。

const unordered = { 'b' : 'asdsad', 'c' : 'masdas', 'a' : 'dsfdsfsdf' }

const ordered = Object.fromEntries(Object.entries(unordered).sort())

console.log(ordered); // a->b->c


8
也许更加优雅的形式:

 /**
     * 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  
}));

附带说明,ES6+语法也是如此:

function ksort( src ) {
  const keys = Object.keys( src );
  keys.sort();
  return keys.reduce(( target, key ) => {
        target[ key ] = src[ key ];
        return target;
  }, {});
};

它并没有排序任何东西。你仍然返回一个对象。你不能保证目标中的对象实际上会有任何顺序。你需要返回一个数组。天哪。 - mjs
你在这里做的唯一事情就是确保它是一个集合,但方式并不是最优的。 - mjs

6

递归排序,用于嵌套对象和数组

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!'
            }
        },
    ]
});

我认为你需要在那里加一个 else -- 这对于 Array.isArray(obj[key])typeof obj[key] === 'object' 都可能成立。 - tadasajon
当您有一个原始数组时,您的函数将原始值(字符串/数字)转换为空对象。为了捕获这种情况,我在您的函数开头添加了以下内容:function sortObjectKeys(obj){if(typeof obj != 'object'){ /* 它是一个原始类型:数字/字符串(在数组中) */ return obj; }为了实现鲁棒性,在完全开始处我还添加了:if(obj == null || obj == undefined){ return obj; } - isgoed
var sortKeys = (unordered) => { function srt(obj){ const isAr = Array.isArray(obj) return !isAr && typeof obj === 'object' ? Object.keys(obj).sort().reduce((a,k)=>{ let vl = obj[k] let isArr = Array.isArray(vl) if (isArr) vl = vl.map(srt) if (vl && typeof vl === 'object' && !isArr) a[k]=srt(vl) else a[k]=vl return a; },{}) : (isAr ? obj.map(srt) : obj); } console.log(JSON.stringify(srt(unordered), null, 4)) } - Akhilesh Kumar
以上可以帮助保留数组,在您的解决方案中,数组被转换为类似数组的对象。 - Akhilesh Kumar
在某些情况下,此解决方案可以将数组转换为对象(例如,数组中包含另一个数组)。 - Venryx

5
Object.keys(unordered).sort().reduce(
    (acc,curr) => ({...acc, [curr]:unordered[curr]})
    , {}
)

1
如果您不为了清晰度而将其分开,那么这可以适合在SO上放在一行中。在循环的海洋中,这个一行代码应该得到更多的赞誉。很好地使用了展开运算符进行合并,我之前总是先赋值再返回,所以通过这个解决方案学到了新东西 :) - Steve Goossens
我该如何解决 TypeScript 中的错误:Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '.. - Timo

5
这是一个基于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;
};

如果Lodash有一个名为toObject()的方法,那将更加简洁...

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