跳到主要内容

JavaScript 数组排序

在JavaScript编程中,数组排序是一项常见且重要的操作。无论是对数字列表进行排序,还是按字母顺序排列名称,甚至是根据对象的特定属性进行排序,JavaScript都提供了多种方法来满足这些需求。本文将介绍JavaScript中数组排序的基本概念和常用技术。

基础概念

在JavaScript中,数组提供了一个内置的sort()方法,它可以对数组元素进行排序。默认情况下,sort()方法将数组元素转换为字符串,然后按照Unicode编码进行排序。

基本语法

javascript
array.sort([compareFunction])
  • array:要排序的数组
  • compareFunction(可选):定义排序顺序的函数。如果省略,数组元素会被转换为字符串并按Unicode编码进行排序。

默认排序

当不提供比较函数时,sort()方法会按照字符串Unicode码点进行排序:

javascript
const fruits = ['banana', 'apple', 'orange', 'pear'];
fruits.sort();
console.log(fruits); // 输出: ['apple', 'banana', 'orange', 'pear']

看起来这样的排序结果符合我们的预期,但当我们对数字进行排序时,可能会得到意外的结果:

javascript
const numbers = [10, 5, 40, 25, 1000, 1];
numbers.sort();
console.log(numbers); // 输出: [1, 10, 1000, 25, 40, 5]
警告

默认情况下,JavaScript的sort()方法将数组元素视为字符串进行排序,这在处理数字数组时通常不符合预期!

使用比较函数

为了解决上述问题,我们需要提供一个比较函数作为sort()方法的参数。比较函数接收两个参数,通常命名为ab,代表要比较的两个元素。

数字排序

要正确排序数字数组,可以使用以下比较函数:

javascript
const numbers = [10, 5, 40, 25, 1000, 1];

// 升序排列
numbers.sort((a, b) => a - b);
console.log('升序:', numbers); // 输出: 升序: [1, 5, 10, 25, 40, 1000]

// 降序排列
numbers.sort((a, b) => b - a);
console.log('降序:', numbers); // 输出: 降序: [1000, 40, 25, 10, 5, 1]

比较函数的工作原理

比较函数的返回值决定了排序的结果:

  • 如果返回负数,则a排在b前面
  • 如果返回正数,则b排在a前面
  • 如果返回0,则保持ab的原有顺序

字符串排序进阶

区分大小写的排序

默认的排序是区分大小写的,大写字母排在小写字母之前:

javascript
const names = ['John', 'alice', 'Bob', 'david'];
names.sort();
console.log(names); // 输出: ['Bob', 'John', 'alice', 'david']

不区分大小写的排序

如果需要不区分大小写的排序,可以在比较函数中将字符串转换为相同的大小写:

javascript
const names = ['John', 'alice', 'Bob', 'david'];
names.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
console.log(names); // 输出: ['alice', 'Bob', 'david', 'John']

使用localeCompare方法

localeCompare()是String对象的一个方法,它提供了考虑语言特定规则的字符串比较:

javascript
const cities = ['München', 'Berlin', 'Zürich', 'Amsterdam', 'Paris'];
cities.sort((a, b) => a.localeCompare(b, 'de'));
console.log(cities); // 输出会根据德语排序规则排列这些城市

对象数组排序

在实际开发中,我们经常需要根据对象的某个属性进行排序。例如,对人员列表按照年龄排序:

javascript
const people = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 32 },
{ name: 'Charlie', age: 19 },
{ name: 'David', age: 40 }
];

// 按年龄升序排序
people.sort((a, b) => a.age - b.age);
console.log('按年龄升序:');
people.forEach(person => console.log(`${person.name}: ${person.age}`));
// 输出:
// 按年龄升序:
// Charlie: 19岁
// Alice: 25岁
// Bob: 32岁
// David: 40岁

// 按姓名字母顺序排序
people.sort((a, b) => a.name.localeCompare(b.name));
console.log('按姓名排序:');
people.forEach(person => console.log(`${person.name}: ${person.age}`));
// 输出:
// 按姓名排序:
// Alice: 25岁
// Bob: 32岁
// Charlie: 19岁
// David: 40岁

稳定性和排序算法

ECMAScript 2019(ES10)规范要求sort()方法的实现必须是稳定的。稳定排序算法保证相等元素的原始顺序不变。

备注

在ES10之前,不同的JavaScript引擎可能使用不同的排序算法,有些可能是不稳定的。但从ES10开始,所有符合规范的JavaScript引擎都应该提供稳定的排序算法。

实际应用案例

案例1:产品列表排序

假设我们有一个电子商务网站,需要根据不同的标准对产品列表进行排序:

javascript
const products = [
{ id: 1, name: '笔记本电脑', price: 6999, rating: 4.5 },
{ id: 2, name: '智能手机', price: 3999, rating: 4.7 },
{ id: 3, name: '蓝牙耳机', price: 799, rating: 4.2 },
{ id: 4, name: '平板电脑', price: 2999, rating: 4.8 }
];

// 按价格从低到高排序
function sortByPrice() {
return products.slice().sort((a, b) => a.price - b.price);
}

// 按评分从高到低排序
function sortByRating() {
return products.slice().sort((a, b) => b.rating - a.rating);
}

// 按名称字母顺序排序
function sortByName() {
return products.slice().sort((a, b) => a.name.localeCompare(b.name));
}

console.log('按价格排序:', sortByPrice());
console.log('按评分排序:', sortByRating());
console.log('按名称排序:', sortByName());

案例2:表格数据排序

在Web应用中,表格数据的排序是一个常见需求:

javascript
const tableData = [
{ id: 101, name: '张三', department: '市场', salary: 8000 },
{ id: 102, name: '李四', department: '技术', salary: 12000 },
{ id: 103, name: '王五', department: '人事', salary: 7500 },
{ id: 104, name: '赵六', department: '技术', salary: 15000 }
];

// 多级排序:先按部门,再按薪资降序
function complexSort() {
return tableData.slice().sort((a, b) => {
if (a.department === b.department) {
return b.salary - a.salary; // 同部门按薪资降序
}
return a.department.localeCompare(b.department); // 先按部门名称排序
});
}

console.log('多级排序结果:', complexSort());
// 输出会先按部门名称排序,同部门内再按薪资降序排列

高级技巧

1. 随机排序(洗牌算法)

有时候我们需要随机打乱数组元素的顺序,例如在游戏中洗牌:

javascript
function shuffleArray(array) {
// 创建一个副本,避免修改原数组
const shuffled = array.slice();

// Fisher-Yates 洗牌算法
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; // 交换元素
}

return shuffled;
}

const deck = ['A♥', 'K♥', 'Q♥', 'J♥', '10♥', 'A♠', 'K♠', 'Q♠', 'J♠', '10♠'];
console.log('洗牌后:', shuffleArray(deck));

2. 自定义排序函数

针对特殊需求,可以创建自定义的排序逻辑:

javascript
// 例如:将特定元素排在最前面,其余按正常顺序排序
function customSort(array, priority) {
return array.slice().sort((a, b) => {
if (a === priority) return -1;
if (b === priority) return 1;
return a.localeCompare(b);
});
}

const fruits = ['apple', 'banana', 'orange', 'pear', 'grape'];
console.log(customSort(fruits, 'banana'));
// 输出: ['banana', 'apple', 'grape', 'orange', 'pear']

性能考虑

当处理大型数组时,排序操作可能会对性能产生影响。以下是一些优化建议:

  1. 避免在排序函数中重复计算:如果比较函数中包含复杂计算,可以预先计算并缓存结果。
javascript
// 低效的方法
array.sort((a, b) => complexCalculation(a) - complexCalculation(b));

// 更高效的方法
const cache = array.map(item => ({
original: item,
value: complexCalculation(item)
}));
cache.sort((a, b) => a.value - b.value);
const result = cache.map(item => item.original);
  1. 考虑使用其他排序算法:对于特定情况,可能有比JavaScript内置排序更高效的算法。

总结

JavaScript数组排序是一个强大而灵活的功能,让我们能够根据各种需求整理数据。本文介绍了:

  • 默认的sort()方法及其工作原理
  • 如何使用比较函数进行自定义排序
  • 数字和字符串排序的不同方法
  • 如何对对象数组进行排序
  • 实际应用案例和高级技巧

掌握这些技术后,你将能够在JavaScript应用中轻松实现各种排序需求。

练习题

  1. 编写一个函数,对数字数组进行降序排列。
  2. 创建一个函数,对字符串数组进行不区分大小写的排序。
  3. 有一个学生数组,每个学生有姓名、年龄和成绩属性。编写函数分别按这三个条件排序。
  4. 实现一个函数,可以根据指定的对象属性名对对象数组进行排序。
  5. 编写一个函数,实现自然排序(例如:"item1", "item2", "item10" 应该按 "item1", "item2", "item10" 排序,而不是 "item1", "item10", "item2")。

扩展阅读

要深入了解JavaScript数组排序,你可以进一步学习:

  • 各种排序算法的原理和实现(冒泡排序、快速排序、归并排序等)
  • 浏览器对sort()方法的不同实现
  • 更多的本地化排序选项(Intl.Collator API)
  • 高性能排序库的使用

掌握这些知识将帮助你在处理复杂数据排序时更加得心应手。