对象\数组\Map按属性值排序迭代器
在JavaScript中,对对象(包括对象数组和键值对对象)按值排序需结合Array.prototype.sort()
与自定义比较函数。以下是具体方法及示例:
📌 一、对象数组按属性值排序
对象数组指元素为对象的数组(如[{name: 'Alice', age: 25}, ...]
)。
1. 数值属性排序
- 升序:
a.prop - b.prop
- 降序:
b.prop - a.prop
const users = [{ name: 'Alice', age: 25 },{ name: 'Bob', age: 30 },{ name: 'Charlie', age: 20 }
];
// 按age升序
users.sort((a, b) => a.age - b.age);
// 输出: Charlie(20), Alice(25), Bob(30) // 按age降序
users.sort((a, b) => b.age - a.age);
// 输出: Bob(30), Alice(25), Charlie(20)
2. 字符串属性排序
使用localeCompare()
确保语言正确的排序:
users.sort((a, b) => a.name.localeCompare(b.name));
// 升序: Alice, Bob, Charlie
3. 多条件排序
当主属性相同时,按次属性排序:
users.sort((a, b) => {if (a.age !== b.age) return a.age - b.age; // 先按年龄升序return a.name.localeCompare(b.name); // 年龄相同按姓名升序
});
// 示例:若Alice(25)和Jane(25)共存,则Alice在前
🔑 二、键值对对象按值排序
普通对象(如{a: 24, b: 12}
)本身无序,需转换为数组再排序:
1. 转换为键值对数组后排序
const obj = { a: 24, b: 12, c: 21, d: 15 };
// 按值升序
const sortedEntries = Object.entries(obj).sort((a, b) => a[1] - b[1]);
// 输出: [['b',12], ['d',15], ['c',21], ['a',24]] // 按值降序
Object.entries(obj).sort((a, b) => b[1] - a[1]);
2. 结果处理
- 获取排序后的键数组:
const sortedKeys = Object.keys(obj).sort((a, b) => obj[a] - obj[b]); // 输出: ['b', 'd', 'c', 'a']
- 转换为有序对象(ES2019+保证
Object.fromEntries
顺序):const sortedObj = Object.fromEntries(sortedEntries); // 输出: {b:12, d:15, c:21, a:24}
⚠️ 三、注意事项
- 稳定性:
ECMAScript 2019规定sort()
为稳定排序(相同值保持原顺序)。 - 性能优化:
- 避免在比较函数中执行复杂操作(如数据库查询)。
- 大数组优先使用数值比较(比字符串比较更快)。
- 特殊值处理:
undefined
或null
属性可能需在比较函数中单独处理。- 混合类型属性需明确类型转换逻辑。
💎 总结
- 对象数组:直接用
sort()
+ 属性比较函数。 - 键值对对象:先转
Object.entries()
,排序后再转回对象或取键数组。 - 多条件排序:在比较函数中分级判断。
测试代码可在Chrome控制台或Node.js中运行。实际业务中,结合具体场景(如价格排序、用户年龄分组)选择升/降序逻辑即可。
在JavaScript中,Map
本身不直接支持按值排序,但可通过转换为数组、排序后再转回 Map
实现。以下是具体方法及示例(兼容ES6+环境):
🔑 一、核心步骤
- 转换为数组:使用
Array.from(map)
或扩展运算符[...map]
将Map
转为[key, value]
数组。 - 按值排序数组:用
sort()
方法指定比较函数,根据value
排序。 - 重建有序Map:将排序后的数组传入
new Map()
构造函数。
⚙️ 二、代码示例
1. 数值升序排序(如商品价格、分数)
const map = new Map([['apple', 3],['banana', 1],['orange', 2]
]);// 1. 转为数组并按值升序
const sortedArray = [...map].sort((a, b) => a[1] - b[1]); // a[1]和b[1]为值
// 2. 重建Map
const sortedMap = new Map(sortedArray);console.log(sortedMap);
// 输出:Map(3) {'banana' => 1, 'orange' => 2, 'apple' => 3}
2. 数值降序排序(如排行榜)
const sortedArray = [...map].sort((a, b) => b[1] - a[1]); // 改为b[1] - a[1]
const sortedMap = new Map(sortedArray);
console.log(sortedMap);
// 输出:Map(3) {'apple' => 3, 'orange' => 2, 'banana' => 1}
3. 字符串排序(如名称、标签)
const map = new Map([['dog', 'Charlie'],['cat', 'Whiskers'],['bird', 'Tweety']
]);// 按值(宠物名)字母升序
const sortedArray = [...map].sort((a, b) => a[1].localeCompare(b[1]) // 字符串比较
);
const sortedMap = new Map(sortedArray);console.log(sortedMap);
// 输出:Map(3) {'dog' => 'Charlie', 'bird' => 'Tweety', 'cat' => 'Whiskers'}
⚠️ 三、注意事项
- 保留排序结果:
- 排序后需通过
new Map(sortedArray)
重建Map
,因原Map
顺序不变。
- 排序后需通过
- 复杂数据类型:
- 若值为对象,需自定义比较函数,例如:
// 按对象属性排序 const sortedArray = [...map].sort((a, b) => a[1].age - b[1].age // 假设值为{age: number} );
- 若值为对象,需自定义比较函数,例如:
- 性能优化:
- 大数组排序可能影响性能,建议在必要时操作。
💎 四、总结
- 核心流程:
Map → 数组 → 排序 → 新Map
。 - 比较函数:
- 数值:
(a, b) => a[1] - b[1]
(升序) - 字符串:
(a, b) => a[1].localeCompare(b[1])
- 数值:
- 适用场景:数据展示(如排行榜、商品列表)、预处理分析等。
完整代码可直接在Chrome控制台或Node.js中运行测试。若需更复杂排序(如多级条件),可扩展比较函数逻辑。
JavaScript迭代器是ES6引入的核心机制,用于统一数据结构的遍历方式,支持按需生成值和惰性计算。以下从底层机制到应用场景的系统性解析:
🔰 一、迭代器核心概念
-
迭代器协议(Iterator Protocol)
迭代器是实现了next()
方法的对象,每次调用返回{ value: any, done: boolean }
:value
:当前迭代值(若done: true
可省略)。done
:迭代是否结束的标志。
const customIterator = {data: [10, 20],index: 0,next() {return this.index < this.data.length ? { value: this.data[this.index++], done: false } : { done: true };} }; console.log(customIterator.next()); // { value: 10, done: false }
-
可迭代协议(Iterable Protocol)
对象需实现Symbol.iterator
方法,返回一个迭代器,使其支持for...of
等语法:const iterableObj = {{return customIterator; // 返回上述迭代器} }; for (const val of iterableObj) { ... } // 支持遍历
- 内置可迭代对象:数组、字符串、Map、Set、NodeList等。
⚙️ 二、迭代器工作原理
for...of
循环的底层流程:
- 调用对象的
Symbol.iterator()
获取迭代器。 - 循环调用
next()
直到done: true
。
此过程解构了const arr = [1, 2]; const it = arr; let result = it.next(); while (!result.done) {console.log(result.value);result = it.next(); }
for...of
的内部机制。
🛠️ 三、创建自定义迭代器
方式1:手动实现迭代器协议
class Range {constructor(start, end) {this.start = start;this.end = end;}{let current = this.start;return {next: () => {return current <= this.end ? { value: current++, done: false }: { done: true };}};}
}
const range = new Range(1, 3);
[...range]; // [1, 2, 3]
方式2:生成器函数(简化版)
生成器(function*
)自动返回迭代器,通过yield
暂停执行:
const obj = {* {yield 1;yield 2;}
};
console.log([...obj]); // [1, 2]
生成器避免了手动维护迭代状态。
⚡ 四、高级特性与应用场景
-
惰性求值(Lazy Evaluation)
迭代器按需生成值,适合处理大数据或无限序列:function* idGenerator() {let id = 0;while (true) yield id++; // 无限序列 } const ids = idGenerator(); ids.next().value; // 0 (仅当需要时计算)
-
异步迭代器
结合for await...of
处理异步数据流:async function* asyncData() {await new Promise(resolve => setTimeout(resolve, 1000));yield 1; } (async () => {for await (const num of asyncData()) {console.log(num); // 1 (1秒后输出)} })();
-
复杂结构遍历
树形结构的自定义迭代:class TreeNode {constructor(value, children = []) {this.value = value;this.children = children;}* {yield this.value;for (const child of this.children) {yield* child; // 委托子节点迭代}} }
📊 五、迭代器 vs 传统遍历
特性 | forEach /for 循环 | 迭代器 |
---|---|---|
内存占用 | 高(需预加载所有数据) | 低(按需生成) |
中断能力 | break 支持 | 支持break |
适用场景 | 小数据集 | 大数据流/无限序列 |
⚠️ 六、注意事项
- 单次消费
多数迭代器不可重用(如生成器),需重新创建。 - 错误处理
在迭代器中用try...finally
确保资源释放:function* safeIterator() {try { yield 1; } finally { console.log("清理资源"); } } const it = safeIterator(); it.next(); it.return(); // 触发finally块
- 性能优化
避免在热代码路径中频繁创建迭代器。
💎 总结
迭代器通过标准化协议(Symbol.iterator
+ next()
)统一了数据结构遍历方式,其核心优势在于:
- 惰性计算:高效处理大数据或无限序列。
- 协议统一:使自定义对象支持
for...of
/扩展运算符等语法。 - 流程可控:结合生成器实现复杂状态管理。
掌握迭代器是深入JavaScript异步编程(如async/await
)和函数式编程的基础,建议通过实现https://blog.csdn.net/qqrrjj2011/article/details/145824498等场景加深理解。