js中数组push对象,前面的值总是被最后一次的值覆盖的问题

今天一同学问了我一个问题,说他js中定义了一个数组,每次往里面push对象,结果最后一次push的对象会覆盖之前push进去的值,问我怎么解决。如果你也遇到了相同的问题,请继续看下去---------------------------------------

下面是代码模拟:

var data = { a:'123', b: '456', c: '789'};
var dataObj = {};//在外面定义对象
var dataArr = [];
for(var i in data) {
    dataObj.name= data[i];
    dataArr.push(dataObj);
}
console.log(dataArr)
结果输出的结果是:789,789,789,而不是期望的123,456,789

原因:每次取出来的值都放在dataObj.name中,因为是在外面的定义的对象,所以每次dataObj的地址
是一样的,dataArr中保存的是dataObj的地址,当最后一次给dataObj.name赋值为789时,由于
是同一个dataObj,所以最后一次给dataObj.name赋值给讲前两次的值给覆盖掉,其实第二次已经
把第一次的值覆盖过了
解决方法:
var data = { a:'123', b: '456', c: '789'};
var dataArr = [];
for(var i indata){
var dataObj = {};//在里面定义对象
    dataObj.name= data[i];
    dataArr.push(dataObj);
}
console.log(dataArr)
这样每次都是新的对象,dataObj.name指向的地址都不一样,数据就对了
这样输出的结果就是:123,456,789


<think>我们需要从数组中删除那些出现超过两次的元素。也就是说,如果某个元素出现的次数大于2,那么我们就删除多余的部分,使得该元素最多只出现两次。 思路: 1. 我们可以遍历数组,同时记录每个元素出现的次数。 2. 在遍历过程中,我们需要判断当前元素已经出现的次数,如果小于2,则保留,并且计数加一;如果已经出现两次或以上,则跳过(即删除)。 但是,由于数组的删除操作(特别是从中间删除)会导致索引变化,通常我们会考虑使用新数组来存储结果,或者从后往前遍历进行删除。 另一种思路是使用两个指针,一个用于遍历,一个用于指向新数组的当前位置,同时利用一个对象(或Map)来记录每个元素出现的次数。 具体步骤: 方法一:使用额外空间(新数组)和哈希表计数 - 创建一个空数组(结果数组)和一个空对象(用于计数)。 - 遍历原数组的每个元素: 如果该元素在对象中的计数不存在或小于2,则将其推入结果数组,并将计数加1(如果不存在则初始化为1)。 如果计数已经达到2,则跳过(即不推入结果数组)。 - 返回结果数组。 方法二:原地修改数组(使用两个指针) - 使用一个对象记录每个元素出现的次数。 - 使用两个指针:i(遍历指针)和j(指向下一个要保留元素的位置)。 - 遍历数组,对于每个元素: 如果该元素的计数小于2,则将其复制到j位置,同时j向后移动,并将计数加1。 否则,跳过该元素(即只保留最多两次出现的元素)。 - 最后,将数组长度设置为j(即删除多余元素)。 注意:方法二需要修改原数组,并且由于是原地修改,我们需要确保在遍历过程中不会影响计数。 我们选择方法二来实现,因为题目要求删除元素,通常意味着修改原数组。但需要注意的是,在JavaScript中,数组是引用类型,我们可以修改原数组。 步骤详细说明(方法二): 1. 初始化一个空对象countMap用于记录每个元素出现的次数。 2. 初始化j=0(指向下一个保留元素的位置)。 3. 遍历数组,i从0到arr.length-1: 令当前元素为arr[i]。 如果countMap[arr[i]]为undefined(即未出现过),则将其设为1,并将arr[j]设置为arr[i],然后j++。 如果countMap[arr[i]]已经存在且小于2,则将其加1,并将arr[j]设置为arr[i],然后j++。 如果countMap[arr[i]]已经大于等于2,则跳过(即不做任何操作,i继续向后遍历)。 4. 遍历结束后,将数组的长度设置为j(即arr.length = j),这样就删除了多余的元素。 但是,注意:我们是在遍历的同时修改数组,并且将后面的元素向前移动(覆盖)。由于我们是从左向右遍历,并且j始终不会超过i,所以不会覆盖掉未处理的元素。 然而,这种方法会改变原数组中元素的相对顺序(保留的元素顺序不变,只是删除了多余的元素,并且后面的元素会覆盖前面的位置,但因为我们只保留前两次出现的,所以实际上顺序不会乱,因为我们是按顺序遍历的,每个元素第一次出现和第二次出现都会被保留,之后出现的都会被跳过)。 另一种方法:使用filter方法(更简洁,但需要额外空间?) - 我们可以使用filter方法,同时利用一个计数对象,但是注意filter会遍历所有元素,并且返回一个新数组,不会改变原数组。如果要求修改原数组,我们可以将原数组的内容替换掉。 使用filter的方法: let countMap = {}; let newArr = arr.filter(item => { countMap[item] = (countMap[item] || 0) + 1; return countMap[item] <= 2; }); 然后,如果我们想修改原数组,可以这样: arr.length = 0; arr.push(...newArr); 但是,这种方法会创建新数组,并且改变原数组的引用(但这里我们通过清空并重新填充的方式修改了原数组)。 根据要求,我们可以选择一种方法。这里我们提供两种方法供选择。 考虑到性能,如果数组很大,方法二(原地修改)可能更节省空间,但filter方法更简洁。 我们分别实现: 方法1:使用filter(非原地,但最后替换原数组) 步骤: 1. 创建一个空对象countMap。 2. 使用filter方法,对每个元素,更新计数,如果计数<=2则保留。 3. 将原数组清空,并将filter后的数组元素全部加入。 方法2:使用双指针(原地修改) 我们分别写一下代码: 方法1代码: function removeMoreThanTwo(arr) { let countMap = {}; let filtered = arr.filter(item => { countMap[item] = (countMap[item] || 0) + 1; return countMap[item] <= 2; }); // 清空原数组并加入过滤后的元素 arr.length = 0; filtered.forEach(item => arr.push(item)); // 或者直接 arr.push(...filtered); return arr; // 返回原数组,此时已修改 } 注意:上面的filter方法中,计数是按顺序进行的,所以会保留前两次出现的元素,而删除之后出现的。但是,如果同一个元素分散在数组的不同位置,我们希望保留前两次出现的,那么这种方法就是正确的。 方法2代码(双指针): function removeMoreThanTwo(arr) { let countMap = {}; let j = 0; for (let i = 0; i < arr.length; i++) { let item = arr[i]; // 如果还没有记录,初始化为0 if (countMap[item] === undefined) { countMap[item] = 0; } if (countMap[item] < 2) { // 保留该元素 arr[j] = arr[i]; j++; countMap[item]++; } // 如果已经达到2次,则跳过,j不变 } // 修改数组长度 arr.length = j; return arr; } 两种方法都可以,我们选择双指针方法,因为它是原地修改,且只需要遍历一次,不需要额外空间(除了countMap)。 但是,注意:双指针方法会改变数组元素的顺序吗? 不会,它只是将后面保留的元素覆盖前面,而跳过的元素会被后面的保留元素覆盖,但每个元素被保留时,其相对顺序没有改变(因为遍历顺序是从前往后)。 测试: 输入:[1, 2, 3, 1, 2, 3, 1, 2, 3] 期望输出:[1, 2, 3, 1, 2, 3] (前两次出现的1,2,3都被保留,第三个1,2,3被删除) 在双指针方法中: 遍历到第一个1:countMap[1]=0 -> 保留,arr[0]=1, j=1, countMap[1]=1 遍历到2:保留,arr[1]=2, j=2, countMap[2]=1 遍历到3:保留,arr[2]=3, j=3, countMap[3]=1 遍历到1:countMap[1]=1 -> 保留,arr[3]=1, j=4, countMap[1]=2 遍历到2:保留,arr[4]=2, j=5, countMap[2]=2 遍历到3:保留,arr[5]=3, j=6, countMap[3]=2 遍历到1:countMap[1]=2 -> 跳过 遍历到2:countMap[2]=2 -> 跳过 遍历到3:countMap[3]=2 -> 跳过 最后,arr.length=6,数组为[1,2,3,1,2,3] 所以,符合预期。 但是,注意:如果数组中有不同的数据类型,比如数字和字符串,或者对象,那么使用对象作为计数Map可能会出现问题,因为对象的键会被转换为字符串。例如,数字1和字符串'1'会被视为同一个键。所以我们需要考虑数组元素的类型。 如果数组中包含不同类型的相同,比如1和'1',我们希望它们被当作不同的元素,那么使用对象作为Map就不合适了。我们可以使用Map数据结构,它可以保存键的类型。 改进:使用Map来计数,这样键可以是任意类型。 修改双指针方法: function removeMoreThanTwo(arr) { let countMap = new Map(); // 使用Map let j = 0; for (let i = 0; i < arr.length; i++) { let item = arr[i]; // 获取当前计数,如果没有则为0 let count = countMap.get(item) || 0; if (count < 2) { arr[j] = arr[i]; j++; countMap.set(item, count + 1); } } arr.length = j; return arr; } 这样,不同的类型(比如1和'1')会被视为不同的键。 测试:数组[1, '1', 1, 2, 2, 1] 期望:保留前两个1(第一个1和第三个1?注意:第二个是'1',所以第一个1出现两次,第三个1是第三次出现,所以第三个1被删除?) 但是,注意遍历顺序: 第一个元素:1 -> countMap(1)为0 -> 保留,j=1,countMap.set(1,1) 第二个元素:'1' -> 是一个不同的键,countMap.get('1')为0 -> 保留,j=2,countMap.set('1',1) 第三个元素:1 -> 此时countMap.get(1)为1 -> 保留,j=3,countMap.set(1,2) 第四个元素:2 -> 保留,j=4,countMap.set(2,1) 第五个元素:2 -> 保留,j=5,countMap.set(2,2) 第六个元素:1 -> countMap.get(1)为2 -> 跳过 结果:[1, '1', 1, 2, 2] 符合预期(每个最多两次,1出现了两次,'1'一次,2两次)。 因此,我们使用Map来计数,以支持不同类型的元素。 最终代码:</think>在 JavaScript 中,要从数组中删除出现超过两次的元素,可以使用以下两种方法。核心思路是**使用哈希表记录元素出现次数,通过过滤或双指针原地修改数组**: ### 方法一:使用 `filter()` 和 Map(推荐) ```javascript function removeExcessDuplicates(arr) { const countMap = new Map(); return arr.filter(item => { const count = countMap.get(item) || 0; countMap.set(item, count + 1); return count < 2; // 只保留出现次数小于2的元素 }); } // 示例 const arr = [1, 2, 3, 2, 1, 4, 1, 5]; console.log(removeExcessDuplicates(arr)); // [1, 2, 3, 2, 1, 4, 5] ``` **原理**: 1. 创建 `Map` 记录每个元素出现次数[^1] 2. 遍历数组时更新计数 3. 只保留计数小于2的元素(即最多保留两次出现) --- ### 方法二:双指针原地修改(节省空间) ```javascript function removeExcessDuplicatesInPlace(arr) { const countMap = new Map(); let j = 0; // 慢指针(保留元素的位置) for (let i = 0; i < arr.length; i++) { const item = arr[i]; const count = countMap.get(item) || 0; if (count < 2) { arr[j] = item; // 保留有效元素 j++; countMap.set(item, count + 1); } } arr.length = j; // 截断多余元素 return arr; } ``` ### 关键点说明 1. **使用 `Map` 而非普通对象** 确保正确处理特殊(如 `NaN`、对象引用等): ```javascript // 普通对象无法区分字符串和数字 const obj = {}; obj[1] = "num"; obj["1"] = "string"; // 会覆盖 // Map 能区分类型 const map = new Map(); map.set(1, "num"); map.set("1", "string"); // 两个独立键 ``` 2. **时间复杂度** 两种方法均为 $O(n)$ 时间复杂度,仅需一次遍历[^2] 3. **稳定性** 保留元素的**原始顺序**,只删除第三次及后续出现的重复项 --- ### 使用场景示例 ```javascript // 清理用户输入(如重复提交) const userInputs = ["save", "save", "undo", "save", "redo"]; console.log(removeExcessDuplicates(userInputs)); // ["save", "save", "undo", "redo"] // 处理传感器数据(允许短暂波动) const sensorData = [23, 23, 23, 24, 24, 23, 25]; console.log(removeExcessDuplicates(sensorData)); // [23, 23, 24, 24, 25] ```
评论 8
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值