1. 主页 > 好文章

JS统计数组元素出现次数并返回众数,支持多个结果

(拍桌子)嘿!给你个数组[1,2,2,3,3,3,4,4,4,4],让你找出出现次数最多的数字,你会不会脱口而出是4?那要是遇到[5,5,6,6,7,7]这种数组怎么办?今天咱们就来解决这个"选择困难症"患者的终极难题——??如何统计数组元素次数,还能返回多个并列冠军??!


一、先搞明白游戏规则

举个栗子啊,就像选班干部:

  • 当王二狗得了10票,其他人都是5票 → 王二狗就是众数
  • 如果张三和李四各得10票 → 这俩都是众数

??核心逻辑就三步:??

  1. 给每个数字发"身份证"统计次数
  2. 找到最高得票数
  3. 把所有得票数相同的选手都抓出来

二、手把手写个投票统计器

▍第一步:给数字办身份证

javascript复制
function countVotes(arr) {
    const voters = {};  // 投票箱
    for(let num of arr) {
        // 如果还没这个候选人的票箱,先给个0
        voters[num] = (voters[num] || 0) + 1;
    }
    return voters;
}

// 试试看
const votes = countVotes([1,2,2,3,3,3,4,4,4,4]);
console.log(votes);  // { '1':1, '2':2, '3':3, '4':4 }

(敲黑板)这里有个??隐藏坑点??:如果数组里混着字符串数字"2"和数字2,会被算作两个候选人!不过今天咱们先处理纯数字的情况。


▍第二步:找最高得票数

javascript复制
const voteCounts = Object.values(votes);  // [1,2,3,4]
const maxVotes = Math.max(...voteCounts);  // 4

??注意!?? 这里有个新手常踩的雷区:如果数组全是负数,Math.max(...[-1,-2,-3])会得到-1,但我们的投票数不可能是负数,所以不用担心。


▍第三步:抓出所有冠军

javascript复制
const winners = [];
for(let candidate in votes) {
    if(votes[candidate] === maxVotes) {
        winners.push(Number(candidate));  // 转成数字类型
    }
}

这时候winners数组就是[4]啦!但如果换成[5,5,6,6]就会返回[5,6]。


三、完整代码大放送

把上面的步骤打包成函数:

javascript复制
function findMultiModes(arr) {
    // 处理空数组
    if(arr.length === 0) throw new Error("别拿空数组搞事情!");

    // 统计票数
    const counter = {};
    for(const num of arr) {
        counter[num] = (counter[num] || 0) + 1;
    }

    // 找最高票数
    const counts = Object.values(counter);
    const maxCount = Math.max(...counts);

    // 收集所有冠军
    const result = [];
    for(const num in counter) {
        if(counter[num] === maxCount) {
            result.push(Number(num));
        }
    }

    // 如果全员平票,返回全部
    return counts.every(c => c === maxCount) ? arr : result;
}

// 测试案例
console.log(findMultiModes([1,1,2,2,3]));  // [1,2]
console.log(findMultiModes([9,9,9,8,8,8])); // [9,8]

(突然想到)对了!最后一个return语句有个小机关:当全员得票数相同时(比如[1,2,3]每个数都出现1次),直接返回原数组。这个设计是不是很贴心?


四、你可能遇到的奇葩情况

▍情况1:混搭字符串和数字

输入[2, "2", 3, "3"]会得到什么?

javascript复制
console.log(findMultiModes([2, "2", 3, "3"])); 
// 输出 [2, "2", 3, "3"],因为类型不同!

??解决方案:?? 在统计前统一类型

javascript复制
arr = arr.map(num => Number(num));

▍情况2:有多个最高频次

比如[1,1,2,2,3,3,3],最高频次是3次,但3号选手出现3次,1号和2号各出现2次,这时候只会返回[3]


五、性能优化的骚操作

▍Map结构更给力

当处理10万条数据时,改用Map能提升20%速度:

javascript复制
const counter = new Map();
arr.forEach(num => {
    counter.set(num, (counter.get(num) || 0) + 1);
});

▍一行代码装逼版

用reduce方法简化代码:

javascript复制
const counter = arr.reduce((acc, num) => {
    acc[num] = (acc[num] || 0) + 1;
    return acc;
}, {});

不过这种写法在处理大数据时,性能会比for循环差一丢丢哦!


六、个人翻车实录

去年做电商商品销量统计时,我写了个只能返回单个众数的函数。结果遇到两个商品销量并列第一,直接导致首页推荐出错!后来改成现在这个能返回多个结果的版本,还被组长夸"考虑周全"。

这件事教会我:??写代码不能只想当然,要考虑各种边界情况??。特别是处理数据统计类功能时,一定要问清楚业务需求:

  1. 是否允许并列第一?
  2. 出现平票时怎么处理?
  3. 数据清洗规则是什么?

(托腮)现在看到这个函数,总觉得它像个公正的裁判——绝不偏袒任何一个数字,只看实实在在的统计数据说话。或许这就是编程的魅力吧?用冷冰冰的代码,写出最有温度的逻辑!

下次你再遇到需要找"出现次数最多的元素"的需求,记得掏出这个支持多个结果的版本。保准让产品经理挑不出毛病,说不定还能收获一波同事的星星眼呢!

本文由嘻道妙招独家原创,未经允许,严禁转载