按字符串属性对对象数组排序:解析一个热门的 Stack Overflow 问题
\n\n想象一下:你有一份用户、产品或文章列表。每个元素都是一个对象,包含一堆属性:姓名、价格、日期、评分。你需要将所有内容排序,比如按姓氏字母顺序排列。这个任务看似简单,但在实践中却会带来惊喜。难怪 Stack Overflow 上关于"按字符串属性值对对象数组排序"的问题已经积累了数千次浏览和几十个答案。
\n\n在本文中,我们不仅会给你现成的代码。我们将详细解析5种排序方法,从基础到高级,指出典型的陷阱并向你展示如何避免它们。读完本文,你就能轻松地对任何数据进行排序。
\n\n在 JavaScript 中排序对象数组是什么?
\n\nJavaScript 有一个内置方法 Array.prototype.sort()。但它在处理对象时的行为并不直观。如果你在无参数的情况下调用 sort(),它会尝试将元素作为字符串进行比较。对数字来说,这会带来意想不到的结果:[1, 20, 3].sort() 会返回 [1, 20, 3],而不是 [1, 3, 20]。对于对象——则完全混乱。
因此,我们需要一个比较函数。它接收两个元素并返回:
\n- \n
- 如果第一个元素小于第二个,返回一个负数; \n
- 如果相等,返回 0; \n
- 如果第一个更大,返回一个正数。 \n
以下是通过字符串属性对对象数组进行排序的基本结构:
\n\n// 初始数组\nconst users = [\n { name: 'Ivan', age: 25 },\n { name: 'Anna', age: 30 },\n { name: 'Boris', age: 22 }\n];\n\n// 按 name 字段排序\nusers.sort((a, b) => {\n if (a.name < b.name) return -1;\n if (a.name > b.name) return 1;\n return 0;\n});\n\nconsole.log(users);\n// [{ name: 'Anna', age: 30 }, { name: 'Boris', age: 22 }, { name: 'Ivan', age: 25 }]\n\n这段代码能用,但比较冗长。让我们看看如何让它更优雅。
\n\n5种按字符串对对象数组排序的方法
\n\n方法1:经典比较函数
\n\n最透明的方法。适用于任何字符串,包括西里尔字母,前提是你使用正确的字母顺序。通过 < 和 > 进行比较会考虑 Unicode 中的字符编码,这对俄语来说会带来意想不到的结果:"ё" 会出现在字母表的末尾,而 "я" 会靠近 "а"。
const products = [\n { title: 'Apple', price: 50 },\n { title: 'Fir Tree', price: 200 },\n { title: 'Pineapple', price: 150 }\n];\n\n// 问题:'Fir Tree' 会被排在 'Apple' 之后\nproducts.sort((a, b) => {\n if (a.title < b.title) return -1;\n if (a.title > b.title) return 1;\n return 0;\n});\n\n何时使用:用于英文字符串,其字母顺序与 Unicode 匹配。
\n\n方法2:localeCompare — 西里尔字母的救星
\n\nString.prototype.localeCompare() 方法考虑了特定语言的排序规则。对于俄语 — 传入 'ru'。
users.sort((a, b) => a.name.localeCompare(b.name, 'ru'));\n\nconsole.log(users);\n// [{ name: 'Anna', age: 30 }, { name: 'Boris', age: 22 }, { name: 'Ivan', age: 25 }]\n\n这段代码更简洁、更短,并且能正确处理 "ё"、"й" 和其他字母。如果你需要按降序排序 — 只需交换 a 和 b:
users.sort((a, b) => b.name.localeCompare(a.name, 'ru'));\n\n何时使用:在处理西里尔字母或多语言数据时始终使用。
\n\n方法3:区分大小写的排序
\n\n默认情况下,localeCompare 是区分大小写的:"apple" 会排在 "Banana" 之前,因为小写字母的