JS 习题1000


  1. 传入任意数量的整数参数,求出其中满足被3和5整除的参数之和

    let total = 0;
    function myFun(...args) {
      //console.log(args);
      args.forEach((value) => {
        if ((value % 3) + (value % 5) === 0) return (total += value);
      });
    }
    myFun(15, 45, 30, 40);
    
  2. 十秒之内每秒返回一个不重复的两位随机小数num(0 <= **num **<= 60)

    const collection = [];
    let counter = 0;// 计数器
    let timer = setInterval(myFun, 1000);// 定时循环执行 myFun
    
    function myFun() {
      let num = (Math.random() * 61).toFixed(2);
      if (num > 60) num = 60;
      if (collection.indexOf(num) === -1) collection.push(num);
      console.log(collection[counter]);
      counter++;
      // 保证 timer 异步执行 10次
      if (counter === 10) {
        clearInterval(timer);
      }
    }
    myFun();
    
  3. 从一个长字符串中随机抽出4个不重复字符组成一个新字符串

    function get4str(str) {
      let newStr = "";
      const arr = str.split("");
      let len = arr.length;
      
      while (newStr.length !== 4) {
      let index = Math.round(Math.random() * len);// 放在while循环外会导致无限循环
        if (newStr.search(arr[index]) === -1) {
          newStr += arr[index];
        }
      }
      return newStr;
    }
    get4str("74357huisadb4354539780453higdfhj4398453");
    
  4. 输入一个随机正整数,将其重组为一个最大正整数(324 => 432)

    function bigger(num){
      // 判断是否个位数以及输入是否数字
      if (num < 10 && typeof(num) !== "number") return -1;
      // 获取参数转为数组
      const arr = String(num).split("");
      // 利用 new set(arr) 数组去重,若集合长度为 1 则说明每位的数一样,返回 -1
      const mySet = new Set(arr);
      if (mySet.length === 1) return -1;
      
      // 我们利用先arr.sort()方法排序得到从小到大排序,再利用reverse()方法得到从大到小排序。
      let bigArr = arr.sort().reverse();
      let bigNum = Number(bigArr.join(""));
      // 利用 三元运算符 判断用户输入数字和重组的最大数字是否相等,相等返回 -1,否则返回 bigNum
      let result =  (bigNum === num) ? -1 : bigNum;
      return result;
    }
    

    本题提供了一个小思路:先考虑过滤掉用户的无用输入和非法输入(一般输入和特殊输入为合法输入),再考虑使用哪几种方法得到目标数据,最后操作目标数据输出不同结果。

  5. 输入一个包含重复元素的数组,输出每个元素出现的次数,再按照次数从高到低排序,结果输出为以对象为元素的数组

    /* 
    我的解题思路:
    第一步,获取数组遍历迭代拿到对象 {元素: 出现次数, 元素: 出现次数, 元素: 出现次数, ...}
    第二步,将前面拿到的对象用 for in 遍历输出一个新数组 [{元素: 出现次数}, {元素: 出现次数}, {元素: 出现次数}, ...]
    最后一步,把第二步拿到的数组用 sort(比较函数) 进行排序输出最终结果
    */
    function myFun(arr){
      const obj = arr.reduce((init, index) => {
        // 利用数组的 reduce 方法可以很容易的返回一个 预期的对象,而不是像 map(callback) 一样只能返回一个数组
        if (index in init) {// if (init[index]) 效果一样; 这里的整个 if 语句可以替换成三元运算符
          init[index]++;// 键存在则键值+1
        } else {
          init[index] = 1;// 键不存在则键值初始化为1
        }
        return init// 必须 return 结果
      },{})// 初始值为空对象保存每个每个数组元素的出现次数
      const newArr = [];
      //获取对象作为元素的新数组
      for (let i in obj) {
        if (obj.hasOwnProperty(i)) {
          newArr.push({item: i, times: obj[i]})// 解构赋值
        }
      }
      return newArr.sort(function (a, b) {return a.times - b.times}).reverse();// 输出从大到小排序后的新数组,请注意 times 不要打成 tiems!!!
    }
    
    //优化版
    function myFun(arr){
      // for of 加上 三元运算符 轻松搞定第一步
      const obj = {};
      for (item of arr) {
       obj[item] = (obj[item]) ? obj[item] + 1 : 1;// 注意 obj[item] + 1 不能是 obj[item]++
      };
      // 注意 for in 遍历加一层 自有属性 判断
      const newArr = [];
      for (let i in obj) {
        if (obj.hasOwnProperty(i)) {
          newArr.push({item: i, times: obj[i]})// 解构赋值
        }
      }
      //最后一步弃用 reverse(),直接 b.times - a.times 搞定
      return newArr.sort((a, b) => b.times - a.times);
    }
    

    思路二:大同小异的办法

    /*
    第一步:获取数组再用 Array.from(new Set(arr)) 得到集合式数组
    第二步:利用 map(item) 遍历集合数组项,遍历一次 push 一个 {key: item, val: tiems} 到最终输出的结果数组中
    第三步:和我的思路一模一样, sort() 排序完事
    */
    function ansFun(arr) {
      let shortAry = Array.from(new Set(arr));// 不同点是:我第一步拿到一个对象,他这里拿到一个集合数组
      let endAry = [];
      shortAry.map((item) => {// 他第二部顺理成章用 map() 遍历数组项
        let obj = { key: null, val: null }; // key 元素,val 元素出现次数
        obj.key = item;
        while (arr.includes(item)) {
          obj.val++;
          arr.splice(arr.indexOf(item), 1);
        }
        endAry.push(obj);// 和我的想法差不多,最终得到 [{元素: 出现次数}, {元素: 出现次数}, {元素: 出现次数}, ...]
      });
      return endAry.sort((a, b) => b.val - a.val);
    }
    
  6. 输入一个字符串统计字符串中出现最多的字符(包含出现次数重复的字符)

    function getStrMax(str){
      const arr = str.split("");
      // for of 加上 三元运算符 轻松搞定第一步
      const obj = {};
      for (item of arr) {
       obj[item] = (obj[item]) ? obj[item] + 1 : 1;// 注意 obj[item] + 1 不能是 obj[item]++
      };
      // 注意 for in 遍历加一层 自有属性 判断
      const newArr = [];
      for (let i in obj) {
        if (obj.hasOwnProperty(i)) {
          newArr.push({item: i, times: obj[i]})// 解构赋值
        }
      }
      //最后一步弃用 reverse(),直接 b.times - a.times 搞定
      const decreaseArr = newArr.sort((a, b) => b.times - a.times);
      // 拿到次数递减数组后,变相的拿到了 最大次数字符项,再加一步判定后续项是不是相同次数
      let timesMax = decreaseArr[0].times;
      /* 
      这个时候用 arr.filter() 就非常合适出场了,
      因为此时 decreaseArr 的数组项是 {item: x, times: y} 并且 decreaseArr 是递减排序好了的结果
      */
      const endArr = decreaseArr.filter((obj) => obj.times === timesMax);
      let endItems = "";
      endArr.map((obj) => endItems += obj.item)
      return `出现最多的字符是${endItems.split("")},均出现了${timesMax}次`;// ${object} 会隐式转换object为string
    }
    

    思路二:考虑到字符和出现次数的关系不就是映射吗?那我们直接操作映射看看

    /*
    第一步:字符串直接利用索引访问,然后创建一个映射实例存储【字符出现次数】的数据。
    第二步:for循环遍历字符串,通过判断字符存在与否更新映射的数据
    第三步:通过 map.values() 得到所有字符出现次数的数组[3, 2, ...],再利用 Math.max 方法得到最大值
    第四步:for of遍历映射,通过判断次数是否等于最大次数得到满足条件的字符数组
    第五步:模板字符串输出结果
    */
    function findMax(str){
      const endKeys = [];
      const strMap = new Map();
      let len = str.length;
      // strMap[str[i]] = mapKeys.includes(str[i]) ? strMap[str[i]] + 1 : 1; 三元运算此处行不通
      for (let i = 0; i < len; i++ ) {
        if (strMap.has(str[i])) {
          strMap.set(str[i], strMap.get(str[i]) + 1); // 赋值更新映射的值是无效的,这里还得采用 map.set()方法
        } else {
          strMap.set(str[i], 1);// 新增映射key, value初始化为1
        }
      }; 
      //
      const mapVals = Array.from(strMap.values());
      let maxVal = (Math.max).apply(Math, mapVals);// 数组最大值
      //
      for (let [key, val] of strMap) {
        if (val === maxVal) endKeys.push(key)
      };
      return `出现最多的字符是${endKeys},均出现了${maxVal}次`;
    }
    

    思路三:利用正则表达式

    function regFind(str) {
      str = str.split('').sort(function (a, b) { return a.localeCompare(b) }).join('');// a.localeCompare(b) 如果a排在b的前面返回 -1或-2 ,否则返回 1或2。如果a和b的值一致则返回 0。
      const reg = /(\w)\1+/g; // 区分大小写全局匹配至少重复一次的字符
      let maxTimes = 0, endStr = '';
      // 这里使用 str.replace(reg, callback) 是因为只有 replace 方法支持操作【匹配的子字符串】,由于有修饰符 g 和 $0, $1,意思是全局匹配的子字符串,每个字符字串会单独输出一个数组,$0, $1分别是该数组第一个子字符串项(比如aaa)和第二个字符项(比如a)。
      str.replace(reg, function ($0, $1) {
        // console.log(arguments); 通过调试理解$0, $1
        if ($0.length > maxTimes) {// 出现次数大者,覆盖 endStr
          maxTimes = $0.length;
          endStr = $1; 
        } else if ($0.length === maxTimes) {// 出现次数相同,则拼接字符
          endStr += $1;
        }
        // 当$0.length < maxTimes 不做任何操作
      });
      endStr = endStr.split('');// 转换成数组,目的是给给输出的结果加逗号分隔
      return `出现最多的字符是${endStr},均出现了${maxTimes}次`;
    }
    // regFind("aaAAAAccccCCDDF");