如何使用Lodash合并类数组对象时避免自动排序?
如何避免Lodash合并类数组对象时的索引排序问题?
我来帮你搞定这个问题~其实这个现象不全是Lodash的锅,本质是JavaScript本身处理对象属性的机制导致的:当对象的键是可以被解析为整数的字符串时,JS引擎会自动把这些键当作类数组的索引,按数值大小排序,而Lodash的_.merge方法默认遵循了这个规则,所以才会出现你示例里的情况。
先看你的示例验证一下
// 非数字字符串键,合并后保持添加顺序 const response1 = { nested: { obj: { 'b': 2 } }}; const response2 = { nested: { obj: { 'a': 2 } }}; const merge1 = _.merge({}, response1, response2); console.log(merge1); // { nested: { obj: { b: 2, a: 2 } } } // 数字字符串键,被JS自动按数值排序 const response3 = { nested: { obj: { 2: 2 } }}; const response4 = { nested: { obj: { 1: 1 } }}; const merge2 = _.merge({}, response3, response4); console.log(merge2); // { nested: { obj: { '1': 1, '2': 2 } } }
看到没?非数字的键'b'和'a'会保持它们被添加到对象的顺序(ES6+规范里的特性),但数字字符串键就会被JS强制排序,不管你合并的顺序是啥。
给你几个实用的解决办法
1. 最简单的:修改键的格式,避开纯数字字符串
把数字键改成带前缀或者后缀的形式,让JS不把它们当成索引处理,比如加个key-前缀:
const response3 = { nested: { obj: { 'key-2': 2 } }}; const response4 = { nested: { obj: { 'key-1': 1 } }}; const merge2 = _.merge({}, response3, response4); console.log(merge2); // { nested: { obj: { 'key-2': 2, 'key-1': 1 } } }
这样合并后就能完美保留原来的顺序啦。
2. 自定义合并逻辑:用_.mergeWith或者自己写合并函数
如果你不想改键的格式,可以用Lodash的_.mergeWith自定义合并规则,或者自己写一个能保留顺序的合并函数。比如用Map来存储键值对(Map会严格保持插入顺序),最后再转回对象:
// 自定义一个保留顺序的合并函数 const mergePreserveOrder = (target, ...sources) => { const resultMap = new Map(); // 先把目标对象的键值对存入Map Object.keys(target).forEach(key => resultMap.set(key, target[key])); // 遍历所有源对象,逐个处理键值对 sources.forEach(source => { Object.keys(source).forEach(key => { if (resultMap.has(key)) { // 如果是嵌套对象,递归合并 const targetVal = resultMap.get(key); const sourceVal = source[key]; if (typeof targetVal === 'object' && targetVal !== null && typeof sourceVal === 'object' && sourceVal !== null) { resultMap.set(key, mergePreserveOrder(targetVal, sourceVal)); } else { // 非对象属性,覆盖成源对象的值(和_.merge行为一致) resultMap.set(key, sourceVal); } } else { // 不存在的键,直接添加 resultMap.set(key, source[key]); } }); }); // 把Map转回普通对象 return Object.fromEntries(resultMap); }; // 测试一下 const response3 = { nested: { obj: { 2: 2 } }}; const response4 = { nested: { obj: { 1: 1 } }}; const merge2 = mergePreserveOrder({}, response3, response4); console.log(merge2); // { nested: { obj: { '2': 2, '1': 1 } } }
这个函数会严格按照键的插入顺序来合并,完全不会打乱顺序。
3. 换数据结构:用数组存储键值对(如果场景允许)
如果你的业务场景可以调整数据结构,直接用数组来存键值对数组,这样顺序是完全可控的,合并的时候也不会乱:
const response3 = { nested: { obj: [['2', 2]] }}; const response4 = { nested: { obj: [['1', 1]] }}; const merge2 = _.merge({}, response3, response4); console.log(merge2); // { nested: { obj: [['2', 2], ['1', 1]] } }
之后使用的时候可以遍历数组,或者转成Map来操作,非常灵活。
内容的提问来源于stack exchange,提问作者ahmehri
相关产品推荐
相关产品推荐

