按键对 JavaScript 对象进行排序

IT技术 javascript sorting
2020-12-16 16:42:07

我需要按键对 JavaScript 对象进行排序。

因此有以下几点:

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

会成为:

{ 'a' : 'dsfdsfsdf', 'b' : 'asdsad', 'c' : 'masdas' }
6个回答

这个问题的其他答案已经过时,从未符合实现现实,并且现在 ES6 / ES2015 规范已经发布,正式变得不正确。


请参见上一节物业迭代顺序探索ES6属于Axel Rauschmayer先生

所有迭代属性键的方法都以相同的顺序执行:

  1. 首先是所有数组索引,按数字排序。
  2. 然后是所有字符串键(不是索引),按照它们的创建顺序。
  3. 然后是所有符号,按照它们的创建顺序。

所以,是的,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().reduce(
  (obj, key) => { 
    obj[key] = unordered[key]; 
    return obj;
  }, 
  {}
);

console.log(JSON.stringify(ordered));
// → '{"a":"baz","b":"foo","c":"bar"}'

使用var而不是const为了与 ES5 引擎兼容。

除了使用略有不同的循环技术之外,我真的看不出您的代码和此处的最佳答案有什么不同。是否可以说对象是“有序的”似乎无关紧要。这个问题的答案还是一样的。发布的规范只是确认了这里的答案。
2021-02-11 16:42:07
@Phil_1984_ 这个答案和最高投票的答案是非常不同的。OP 的问题是如何对对象文字进行排序。最高投票的答案说你不能,然后通过将排序的键存储在数组中,然后从排序的数组中迭代和打印键值对来提供解决方法。这个答案声称对象文字的顺序现在是可排序的,他的示例将排序的键值存储回对象文字,然后直接打印排序的对象。附带说明一下,如果链接站点仍然存在,那就太好了。
2021-02-21 16:42:07
这两个答案都简单地使用 Array.sort 函数首先对键进行排序。OP 要求对“JavaScript 对象”进行排序,并且仅使用 JavaScript 对象表示法 (JSON) 来描述这两个对象。这两个对象在逻辑上是相同的,这意味着排序无关。我认为最重要的答案更好地解释了这一点。说“所有其他答案现在都正式不正确”对我来说太极端了,尤其是链接已损坏。我认为问题是“这个新的 ES6 规范”给人一种错觉,即 Javascript 对象具有一个有序的键列表,这根本不是真的。
2021-02-23 16:42:07
仅供参考,已澄清属性枚举顺序仍未定义:esdiscuss.org/topic/...
2021-03-02 16:42:07
请补充一点,该解决方案不受各种现代浏览器(谷歌“caniuse ES6”)的支持。使用此答案可能仅在某些浏览器中存在难以发现的错误(例如,Chrome 一切正常时的 Safari)。
2021-03-06 16:42:07

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 对象这样的东西

@MarcelKorpel 有一个 JSON 对象。检查官方 JSON RFC 的第 8 节:ietf.org/rfc/rfc4627.txt
2021-02-11 16:42:07
既然 ES6/ES2015 已经敲定,这个答案就正式变得不正确了。有关更多信息,请参阅我的答案。
2021-02-13 16:42:07
@MattBall IMO JSON Object 是一个非常精确的字符串术语,就像{"a", 1}JSON Array 是一个字符串的精确术语,如[1]. 当你有字符串时[{x: 1}, {y: 2}, {z: 3}]能够通过说我数组中的第三个对象之类的东西来进行交流是很有用的,所以在评论 Javascript 文字时我更喜欢“那不是 JSON 对象”,而不是“没有这样的作为 JSON 对象的东西”,当 OP 实际使用 JSON 时,这只会导致更多的混乱和通信困难。
2021-02-15 16:42:07
@Paulpro 我必须不同意。{"a", 1}是对象文字或 JSON 文本(也称为 JSON 字符串,如果您真的喜欢)。它取决于上下文,前者如果它在 JavaScript 源代码中逐字出现,后者如果它是需要传递给 JSON 解析器以进一步使用的字符串。在允许的语法、正确使用和序列化方面,两者之间存在真正的差异。
2021-02-28 16:42:07
@Paulpro 关于该 RFC 有两点需要注意:第一,此时它已经 7 岁了,词汇量随着时间的推移而变化;第二,“这份备忘录为互联网社区提供信息。它没有指定任何类型的互联网标准。” 给定的字符序列是代表 JavaScript 对象文字还是 JSON 文本取决于上下文/用法。术语“JSON 对象”混淆并以有害的方式使区别变得模棱两可。让我们摆脱不精确的术语,并在可能的情况下使用更精确的术语。我认为没有理由不这样做。言语很重要。
2021-03-09 16:42:07

很多人都提到“对象无法排序”,但在那之后他们给了你一个有效的解决方案。悖论,不是吗?

没有人提到为什么这些解决方案有效。它们是,因为在大多数浏览器的实现中,对象中的值是按照它们添加的顺序存储的。这就是为什么如果您从排序的键列表创建新对象,它会返回预期的结果。

而且我认为我们可以再添加一种解决方案——ES5 函数式方式:

function sortObject(obj) {
    return Object.keys(obj).sort().reduce(function (result, key) {
        result[key] = obj[key];
        return result;
    }, {});
}

ES2015 以上版本(格式为“one-liner”):

const sortObject = o => Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {})

上述示例的简短说明(如评论中所问):

Object.keys正在给我们提供的对象(objo中的键列表,然后我们使用默认排序算法对它们进行排序,下一步.reduce用于将该数组转换回对象,但这次所有键都已排序。

我的 linter 抱怨那个单行(返回一个任务),但这个对我有用,对我来说更容易阅读: Object.keys(dict).sort().reduce((r, k) => Object.assign(r, { [k]: dict[k] }), {});
2021-02-12 16:42:07
@Pointy 此行为在所有主要浏览器中始终可用,并已在 ES6/ES2015 中标准化我会说这是个好建议。
2021-02-21 16:42:07
这是在 EcmaScript v5 中完成任务的最简单、最简洁的方式。聚会有点晚,但应该是公认的答案。
2021-02-23 16:42:07
“它们是,因为在大多数浏览器的实现中,对象中的值是按照它们添加的顺序存储的”,但仅适用于非数字属性。另请注意,仍然无法保证属性迭代的顺序(例如 via for...in)。
2021-03-02 16:42:07
很高兴你解释了为什么它有效,它为我上了一课。谢谢!
2021-03-06 16:42:07

伙计们,我比喻性地震惊了!当然所有的答案都有些老了,但甚至没有人提到排序的稳定性!所以请耐心等待,我会尽力回答问题本身,并在此处详细介绍。所以我现在要道歉,这将是很多阅读。

由于是 2018 年,我将只使用 ES6,Polyfills 都可以在 MDN 文档中找到,我将在给定部分链接。


回答问题:

如果您的键只是数字,那么您可以安全地使用Object.keys()withArray.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), {}));

如果有人想知道 reduce 有什么作用:

// 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.
  ,{}
);

为什么排序有点复杂:

简而言之,Object.keys()将返回一个与我们使用普通循环获得的顺序相同的数组:

const object1 = {
  a: 'somestring',
  b: 42,
  c: false
};

console.log(Object.keys(object1));
// expected output: Array ["a", "b", "c"]

Object.keys() 返回一个数组,其元素是与直接在对象上找到的可枚举属性相对应的字符串。属性的顺序与通过手动循环对象的属性给出的顺序相同。

旁注 - 您也可以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() - 文档:https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

如果你能提供一个在最后使用的该死的方法。很好的答案!
2021-02-12 16:42:07
你轻弹摇滚,谢谢你的这个令人难以置信的回答。我在 SO 上见过的最好的之一。干杯。
2021-02-14 16:42:07
@momomo 问题是,根据您对排序的期望,没有正确的方法。然而,你可以简单地使用这段代码从我的回答:Object.keys(testObj).sort().reduce((a, c) => (a[c] = testObj[c], a), {})
2021-02-26 16:42:07
排序的稳定性是否不适用于这个问题:我们按对象键排序,并且键是唯一的。(扩展您的卡片示例:包中永远不会有两个 5,即使它们的花色不同。)
2021-03-05 16:42:07
@DarrenCook 好问题!您是对的,一个对象中永远不会有相同的键。然而,稳定性比键的唯一性更复杂。大多数情况下,数组包含在排序对象中。这就是一切都可能变得不稳定的时候。想象一个有 5 个玩家的纸牌游戏。每只手都由一个数组表示(以保持单独的手排序)和卡片作为对象。如果你的卡片看起来像这样,{5: 'hearts'}或者{5: 'spades'}你开始对数组进行排序,它在拉米等游戏中可能会变得不稳定。更不用说不同的语言了。
2021-03-09 16:42:07

现在是 2019 年,我们有 2019 年的方法来解决这个问题:)

Object.fromEntries(Object.entries({b: 3, a:8, c:1}).sort())
@a2441918 与使用 sort() 函数的方式相同。看看:developer.mozilla.org/de/docs/Web/JavaScript/Reference/... Object.fromEntries(Object.entries({b: 3, a:8, c:1}).sort((k, j ) => k - j))
2021-02-15 16:42:07
我们如何按键对嵌套对象进行排序?
2021-02-18 16:42:07
@theMaxx 使用自定义函数进行排序,例如:``` Object.fromEntries(Object.entries({b: 3, a:8, c:1}).sort(([key1, val1], [key2, val2]) => key1.toLowerCase().localeCompare(key2.toLowerCase()))) ```
2021-03-01 16:42:07
对于 TypeScript 用户,我们尚不可用,请参阅:github.com/microsoft/TypeScript/issues/30933
2021-03-02 16:42:07
不错的单行,但如何以不区分大小写的方式对键进行排序?
2021-03-06 16:42:07