一道统计题到reduce方法的学习

404 阅读3分钟

题目:请输出当前页面出现最频繁的3个html标签及出现的次数!

引出题目

// 我的解答
console.log('标签的数量: '
+ new Set([...document.getElementsByTagName('*')].map(tag => tag.tagName)).size)
/**
* num 统计的html数量
* arr 需要被统计的对象数组
*/
let htmlArr = [...document.getElementsByTagName('*')].map(tag => tag.tagName)
function getHtmlInfo (arr, num) {
    let len = arr.length
    let obj = {}
    let newArr = []
    let tag
    for(let i = 0; i < len; i++) {
        tag = arr[i]
        if (obj[tag]) {
            obj[tag]++
        } else {
            obj[tag] = 1
        }
    }
    for (let key in obj) {
        newArr.push({
            tag: key,
            count: obj[key]
        })
    }
    // 降序
    newArr.sort((a, b) => {
        return b.count - a.count
    })
    let newNum = num || newArr.length
    return newArr.slice(0, newNum)
}
let newHtmlArr = getHtmlInfo(htmlArr, 3)
console.log(`出现最多的是${newHtmlArr[0].tag}标签,共${newHtmlArr[0].count}次`)
console.log(`出现次数第二的是${newHtmlArr[1].tag}标签,共${newHtmlArr[1].count}次`)
console.log(`出现次数第三的是${newHtmlArr[2].tag}标签,共${newHtmlArr[2].count}次`)
// 比较low
// 大佬的解答
Object.entries([...document.querySelectorAll("*")].map(tag=>tag.tagName).reduce((ret, i)=>{
  ret[i] = (ret[i] || 0) + 1;
  return ret;
}, {})).sort((a, b)=>(b[1] - a[1])).slice(0, 3)
.map(a=>(`${a[0]}: ${a[1]}次`)).join(', ')

Array.prototype.reduce() 该方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。 MDN链接

语法

arr.reduce(callback(accumulator, currentValue[, currentIndex[, sourceArray]])[, initialValue])

  • accumulator 累计器累计回调的返回值
  • currentValue 当前值
  • currentIndex 可选当前索引
  • sourceArray 可选源数组

callback需要注意的点

  • accumulator 默认值为initialValue,此时currentValue为数组的0号索引的值,currentIndex为0;如果initialValue没有提供,则initialValue取数组的0号索引位置的值,currentValue取1号索引的值,currentIndex为1。
  • 如果数组为空数组且没有提供initialValue,则会报类型错误。
  • 如果数组只有一个元素(任意位置),且没有提供initialValue,或提供了initialValue但数组为空,则callback不会被执行,该元素会被作为返回值。
  • 值会被累计到accumulator,并最终与最后索引的值处理后合并为单一值返回。
  • 建议,为安全着想,每次提供initialValue

使用场景

计算数组中所有值的和

const sum = [1, 2, 3, 4].reduce((acc, cur) => acc + cur, 0)
console.log(sum) // 10

累加对象数组中的值

const sum = [{x: 1}, {x: 2}, {x: 3}].reduce((acc, cur) => acc + cur.x, 0)
console.log(sum) // 6

将二维数组转化为一维数组

const arr = [[0, 1], [2, 3], [4, 5]].reduce((acc, cur) => acc.concat(cur), [])
console.log(arr) // [0, 1, 2, 3, 4, 5]

计算数组中每个元素出现的次数

 const htmls = ['html', 'span', 'body', 'div', 'div']
 const countedHtmls = htmls.reduce((acc, cur) => {
   if (cur in acc) {
     acc[cur]++
   } else {
     acc[cur] = 1
   }
   return acc
 }, {})
 console.log(countedHtmls) // { html: 1, span: 1, body: 1, div: 2 }
 // 计算当前页面出现次数最多的3个标签
 Object.entries([...document.querySelectorAll('*')].map(tag => tag.tagName).reduce((acc,cur) => {
    acc[cur] = (acc[cur] || 0) + 1
    return acc
}, {})).sort((a, b) => b[1] - a[1]).slice(0, 3).map(a => `${a[0]}: ${a[1]}次`).join(', ')

按属性对object分类

const arr = [
  {
    name: 'css',
    age: 18
  },
  {
    name: 'Javascript',
    age: 20
  },
  {
    name: 'html',
    age: 18
  }
]
function groupBy (arr, property) {
  return arr.reduce((acc, cur) => {
    const key = cur[property]
    if (!acc[key]) {
      acc[key] = []
    }
    acc[key].push(cur)
    return acc
  }, {})
}
console.log(groupBy(arr, 'age'))
// {
//   '18': [ { name: 'css', age: 18 }, { name: 'html', age: 18 } ],
//   '20': [ { name: 'Javascript', age: 20 } ]
// }

合并对象数组中的数组

const friends = [
  {
    name: 'javascript',
    books: ['es6入门', 'javascript高级程序设计', '你不知道的javascript', 'Dom编程艺术', 'Javascript设计模式']
  },
  {
    name: 'css',
    books: ['css世界']
  }
]
const allBooks = friends.reduce((acc, cur) => {
  return [...acc, ...cur.books]
}, [])
console.log(allBooks)
// [
//   'es6入门',
//   'javascript高级程序设计',
//   '你不知道的javascript',
//   'Dom编程艺术',
//   'Javascript设计模式',
//   'css世界'
// ]

数组去重

const arr = [1,2,1,2,3,5,4,5,3,4,4,4,4]
const result = arr.sort().reduce((acc, cur) => {
  if (!acc.includes(cur)) {
    acc.push(cur)
  }
  return acc
}, [])
console.log(result)
// [ 1, 2, 3, 4, 5 ]

按顺序运行promise

function runPromiseInSequence(arr, input) {
  return arr.reduce((promiseChain, currentFunction) => {
    return promiseChain.then(currentFunction)
  }, Promise.resolve(input))
}
// function runPromiseInSequence(arr, input) {
//   return arr.reduce((promiseChain, currentFunction) => {
//     return promiseChain.then(currentFunction)
//   }, new Promise(resolve => {
//     resolve(input)
//   }))
// }
function p1 (a) {
  return new Promise((resolve, reject) => {
    resolve(a * 5)
  })
}
function p2 (a) {
  return new Promise((resolve, reject) => {
    resolve(a * 2)
  })
}
function f3 (a) {
  return a * 3
}
function p4 (a) {
  return new Promise((resolve, reject) => {
    resolve(a * 4)
  })
}
const promiseArr = [p1, p2, f3, p4]
runPromiseInSequence(promiseArr, 10).then(console.log)
// 1200

管道机制/功能型函数管道

// 管道机制/功能型函数管道
// 定义:前一个函数的输出是后一个函数的输入
// const pipeLine = (...funcs) => input => {
//   return funcs.reduce((acc, curFun) => curFun(acc), input)
// }
const pipeLine = (...funcs) => input => funcs.reduce((acc, curFn) => curFn(acc), input)

const fun1 = num => num + num
const fun2 = num => num * num
const fun3 = num => num * 10

const pipeResult = pipeLine(fun1, fun2, fun3)
console.log(pipeResult(1)) // 40

实现map

if (!Array.prototype.mapUsingReduce) {
  Array.prototype.mapUsingReduce = function (callback, thisArg) {
    return this.reduce(function (mappedArray, currentValue, index, orignArray) {
      mappedArray[index] = callback.call(thisArg, currentValue, index, orignArray)
      return mappedArray
    }, [])
  }
}
const newArr = [1, 2, , 3].mapUsingReduce((currentValue, index, array) => currentValue + index + array.length)
console.log(newArr)  // [5, 7, , 10]