JavaScript经典编程题(持续摘抄中。。。)

278 阅读6分钟

本文题目来源于leetcode、掘金等各大网站资源,纯属记录用来学习。一直记录更新中。。。

1.小蝌蚪按顺序找妈妈

有两组数组,第二组根据第一组顺序一一对应分配给第一组数组,结果返回对象数组形式。

const mom = ['张三', '李四', '老王', '李四', '张三', '老王']
const son = [1, 23, 5, 9, 12, 4]

结果:{ '张三': [ 1, 12 ], '李四': [ 23, 9 ], '老王': [ 5, 4 ] }
const findMom = (mon, son) => {
  return mom.reduce((pre, cur, index, arr) => { // pre: 上一次调用回调返回的值,或者是提供的初始值
-   if (!pre[cur]) {				// cur: 当前元素
-     pre[cur] = []				// index: 当前元素的索引
-   }
+   pre[cur] = pre[cur] || []
    for (let i = 0; i < son.length; i++) {
      if (index === i) {
        pre[cur].push(son[i])
      }
    }
    return pre
  }, {}) // 初始值
}
console.log(findMom(mom, son))

2.姓名和分数对应

const arr = [
  {name: '小明', score: 10},
  {name: '小红', score: 10},
  {name: '小明', score: 10},
  {name: '小红', score: 40}
]

结果:
[
  { name: '小明', value: [ 10, 10 ] },
  { name: '小红', value: [ 10, 40 ] }
]
const newArr = arr.reduce((ret, {name, score}) => {
  (ret[name] = ret[name] || {
    name: name,
    value: []
  }).value.push(score)
  return ret
}, {})
console.log(Object.values(newArr))

3.找出数组中的字符串公共因子

const strs = ['flower', 'flow', 'flight']

结果:fl
let longestCommonPrefix = strs => {
  if (strs.length === 0) return ''
  let ans = strs[0]
  for (let i = 1; i < strs.length; i++) {
    let j=0
    for(; j < ans.length && j < strs[i].length; j++) {
      if (ans[j] !== strs[i][j]) break
    }
    ans = ans.substr(0, j)
    if (ans === '') return ans
  }
  return ans
}
console.log(longestCommonPrefix(strs))

4.数组对象按照年龄分组

const arr = [
  {name: '小明', age: 18},
  {name: '小红', age: 19},
  {name: '小东', age: 18},
  {name: '老王', age: 26},
  {name: '小林', age: 19},
  {name: '小王', age: 18}
]

结果:{ '18': [ '小明', '小东', '小王' ], '19': [ '小红', '小林' ], '26': [ '老王' ] }
const ageGroup = (arr, key) => {
  return arr.reduce((pre,cur) => {
    pre[cur[key]] = pre[cur[key]] || []
    pre[cur[key]].push(cur['name'])
    return pre
  }, {})
}
console.log(ageGroup(arr, 'age'))

5.从一个数组中找出两个数和为一个数,找不到返回[]

const arr = [1, 5, 10, 12, 8]
total: 15

结果:[ 5, 10 ]
const findTotal = (arr, total) => {
  arr.sort((a, b) => a - b)
  for (let i = 0, j = arr.length - 1; i < j;) {
    let nums = arr[i] + arr[j]
    if (nums === total) {
      return [arr[i], arr[j]]
    } else if (nums < total) {
      i++
    } else {
      j--
    }
  }
  return []
}
console.log(findTotal(arr, 15))

6.删除排序数组中的重复项,返回数组的长度

const arr = [1, 1, 2]

结果: 2
let removeDuplicates = arr => {
  let count = 0
  let n = arr.length
  for (let i = 0; i < n; i++) {
    if (arr[i] != arr[i+1]) {
      arr[i-count] = arr[i]
    } else {
      count++
    }
  }
  return n-count
}
console.log(removeDuplicates(arr))

7.找出数组中重复的数(一个就行)

const arr = [0, 11, 30, 11, 15]

结果:11
let findRepeatNumber = arr => {
  let s = new Set() // set自动忽略重复元素
  for (let i in arr){
    let curLenth = s.size
    s.add(arr[i])
    if (s.size === curLenth) return arr[i]
  }
}

或者(双for):
let findRepeatNumber = arr => {
  for (let i = 0; i < arr.length - 1; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[i] === arr[j]) return arr[i]
    }
  }
}
console.log(findRepeatNumber(arr))

8.在两个数组中找出重复值

const arr1 = [5, 9, 6, 2, 3]
const arr2 = [6, 9, 3, 4, 1, 8]
最土:
const findSameVal1 = (arr1, arr2) => {
  let arr = []
  for (let i = 0; i < arr1.length; i++) {
    for (let j = 0; j < arr2.length; j++) {
      if (arr1[i] === arr2[j]) arr.push(arr1[i])
    }
  }
  return arr
}

有点意思(坑:万一一个数组里有很多重复的,主要学习它的思维):
const findSameVal2 = (arr1, arr2) => {
let arr = arr1.concat(arr2)
  let arr3 = []
  arr.sort((a, b) => a -b)
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] === arr[i+1]) arr3.push(arr[i])
  }
}

有内味了:
const findSameVal3 = (arr1, arr2) => {
  let arr = []
  for (let i = 0; i < arr1.length; i++) {
    arr2.some((item) => { // 类似filter,find等等
      if (item === arr1[i]) arr.push(item)
    })
  }
  return arr
}

9.数组按规则组合成对象(常用于对后端返回的值做第二次操作符合自己使用)

const list = [  {city: '北京', area: '天安门'},  {city: '北京', area: '颐和园'},  {city: '上海', area: '东方明珠'}]

结果:
let obj = {
  '北京': [{city: '北京', area: '天安门'}, {city: '北京', area: '颐和园'}],
  '上海': [{city: '上海', area: '东方明珠'}]
}
脑壳疼有点low,期待大佬的‘妙’
const goupBy = (list, city) => {
  let obj = {}
  let arr = []
  list.forEach(e => {
    if (arr.indexOf(e.city) < 0) { // 取出city
      arr.push(e.city)
    }
  })
  for (let i in arr) { // 对象格式
    obj[arr[i]] = []
  }
  for (let i = 0; i < list.length; i++) { // 对象往里添加选项
    for (let j in obj) {
      if (j === list[i].city) {
        obj[j].push(list[i])
      }
    }
  }
  return obj
}
console.log(goupBy(list, 'city'))

10.删除字符串中的所有相邻重复项

解题思路: 遍历字符串,依次入栈,入栈时判断与栈头元素是否一致,如果一致,即这两个元素相同相邻,则需要将栈头元素出栈,并且当前元素也无需入栈。

const str = 'abbaca'

结果:
'ca'
const removeDuplicates = str => {
  const arr = []
  for (const i of str) { // 字符串遍历
    if (arr.length && arr[arr.length - 1] === i) { // 入栈与栈头是否一致
      arr.pop() // 删除最后一个元素
    } else {
      arr.push(i)
    }
  }
  return arr.join('') // 字符串变数组用split()  数组变字符串用join()
}
console.log(removeDuplicates(str))

11.数组去重

棕小渐(解锁多种JavaScript数组去重姿势):juejin.cn/post/684490…

const arr = [1, 2, 0, 1, 2, 5, 5.2]

结果:
[1, 2, 0, 5, 5.2]
方法一:(map)
const unique = arr => {
  const map = new Map()
  return arr.filter(item => {
    return !map.has(item) && map.set(item, 1)
  })
}
console.log(unique(arr))

方法二:(es6的setconst unique = arr => [...new Set(arr)]

12.字符串反转

const str = 'jacklove'

结果:
evolkcaj
const reverse = str => {
  let arr = []
  let i = str.length
  while (i > -1) {
    i--
    arr.push(str[i])
  }
  return arr.join('')
}
console.log(reverse(str))

13.微宏任务执行顺序

setTimeout(() => {
  console.log(3)
  new Promise((resolve, reject) => {
    console.log(4)
    resolve()
  }).then(() => {
    setTimeout(() => {
      console.log(5)
    }, 0)
    console.log(6)
  })
}, 0)
new Promise((resolve, reject) => {
  resolve()
}).then(() => {
  console.log(8)
  return new Promise((resolve1, reject) => {
    console.log(9)
    setTimeout(() => {
      console.log(10)
    }, 0)
    resolve1()
  })
}).then(() => {
  console.log(11)
})
new Promise((resolve2, reject) => {
  resolve2()
}).then(() => {
  console.log(13)
})
结果:8 9 13 11 3 4 6 10 5

new Promise自动执行,执行完再执行.then()里面,定时器最后执行

14.赋值,指向问题

let a = {
  name: '德莱文',
  age: 12
}
const test = b => {
  b.age = 18
  let c = {
    name: '文森特',
    age: 25
  }
  b = c
  console.log(b) // { name: '文森特', age: 25 }
  b.age = 11
  console.log(c) // { name: '文森特', age: 11 }
  return b
}
let d = test(a)
console.log(d) // { name: '文森特', age: 11 }
console.log(a) // { name: '德莱文', age: 18 }
let a = {n: 1}
let b = a
a.x = a = {n: 2} // 相当于 a.x = {n: 2} a = {n: 2}
console.log(a) // {n: 2}
console.log(b) // {n: 1, x: {n: 2}}

// 当执行完b指向a时,再执行a.x(点符号优先执行),此时b={n: 1, x: undefined}。复制操作a.x = {n: 2},b指向a那么b={n: 1, x: {n: 2}},最后执行a={n: 2}

15.找出数组 arr 中重复出现过的元素

const arr = [1, 2, 4, 4, 3, 3, 1, 5, 3]

结果:
[1, 4, 3]
const duplicates = arr => {
  let list = []
  arr.forEach(e => {
    if (arr.indexOf(e) !== arr.lastIndexOf(e) && list.indexOf(e) === -1) {
      list.push(e)
    }
  })
  return list
}
console.log(duplicates(arr))

16.数组对象去重关键值

const list = [
  {name: 'a', age: 1},
  {name: 'b', age: 2},
  {name: 'c', age: 2},
  {name: 'a', age: 3},
  {name: 'c', age: 2}
]

结果:

1.关键值是name时:
const list = [
  {name: 'a', age: 1},
  {name: 'b', age: 2},
  {name: 'c', age: 2}
]

2.关键值是age时:
const list = [
  {name: 'a', age: 1},
  {name: 'b', age: 2},
  {name: 'a', age: 3}
]
// const duplicationJson = (list, key) => {
//   let map = {} // 对key进行计数,如果一个就是不重复
//   return list.filter(item => {
//     let value = item[key]
//     if (!map[value]) {
//       map[value] = 1
//     } else {
//       map[value] += 1
//     }
//     return map[value] === 1
//   })
// }
const duplicationJson = (list, key) => {
  let map = {}
  // 判断map是否有这个key不存在则写入map并赋值true,若存在不写入
  return list.filter(item => !map[item[key]] && (map[item[key]] = true))
}
console.log(dup(duplicationJson, 'name'))

17.偏平化处理tree

来自扁平数据结构转Tree

let arr = [
 {id: 1, name: '部门1', pid: 0},
 {id: 2, name: '部门2', pid: 1},
 {id: 3, name: '部门3', pid: 1},
 {id: 4, name: '部门4', pid: 3},
 {id: 5, name: '部门5', pid: 4},
]

结果:
[
 {id: 1, name: '部门1', pid: 0
  children: [
    {id: 2, name: '部门2', pid: 1, children: []},
    {id: 3, name: '部门3', pid: 1, children: [
      {id: 4, name: '部门4', pid: 3, children: [
        {id: 5, name: '部门5', pid: 4, children}
      ]}
    ]}
  ]
 }
]
function arrayToTree(arr) {
  let result = []
  let itemMap = {}
  for (let item of arr) {
    let id = item.id
    let pid = item.pid
    itemMap[id] = {
      ...item,
      children: []
    }
    let treeItem = itemMap[id]
    if (!itemMap[pid]) {
      result.push(treeItem)
    } else {
      itemMap[pid].children.push(treeItem)
    }
  }
  return result
}

18.

(我是970)[juejin.cn/post/698510…]

实现一个EatMan
说明:实现一个EatMan,EatMan可以有以下一些行为

示例:
1. EatMan('Hank')
  Hi! This is Hank!
2. EatMan('Hank').eat('dinner').eat('supper')   // 链式调用
  Hi! This is Hank!
  Eat dinner~
  Eat supper~
3. EatMan('Hank').eat('dinner').eatFirst('lunch')   // 输出顺序
  Eat lunch~
  Hi! This is Hank!
  Eat dinner~
4. EatMan('Hank').eat('dinner').eatFirst('lunch').eatFirst('breakfast')   // 插队输出
  Eat breakfast~
  Eat lunch~
  Hi! This is Hank!
  Eat dinner~
class MyEatMan {
  constructor(name) {
    this.name = name
    // 任务队列,将需要执行的函数入队
    this.tasks = []
    // 第一个任务
    const task = this.printName(this.name)
    // 放入任务队列
    this.tasks.push(task)
    // 为了保证任务都能在进队完毕之后再执行,创建一个宏任务,让执行任务的时机放到 下一个事件循环里
    let self = this
    setTimeout(function () {
      // console.log('tasks', self.tasks)
      self.run()
    }, 0)
  }
  printName(name) {
    let self = this
    return function () {
      console.log(`Hi! This is ${name}!`)
      self.run()
    }
  }
  // eat函数,每次调用都入队一个任务,而且还能实现链式调用
  eat(thing) {
    let self = this
    const task = function () {
      console.log(`Eat ${thing}~`)
      self.run()
    }
    this.tasks.push(task)
    return this
  }
  // eatFirst函数,谁最后初始化,谁先执行,而且还能实现链式调用
  eatFirst(thing) {
    let self = this
    const task = function () {
      console.log(`Eat ${thing}~`)
      self.run()
    }
    // 插入到队列的头部
    this.tasks.unshift(task)
    return this
  }
  // run执行任务
  run() {
    // 出队
    const currTask = this.tasks.shift()
    // 执行
    currTask && currTask()
  }
}
function EatMan(name) {
  return new MyEatMan(name)
}

19.金钱数字分隔

const NumberSplit = num => {
  const decimal = String(num).split('.')[1] || '' // 小数
  let tempArr = []
  const revNumArr = String(num).split('.')[0].split('').reverse() // 倒序
  for (let i in revNumArr) {
    tempArr.push(revNumArr[i])
    if ((i + 1) % 3 === 0 && i != revNumArr.length - 1) {
      tempArr.push(',')
    }
  }
  const integer = tempArr.reverse().join('') // 整数部分
  return decimal ? integer + '.' + decimal : integer
}
console.log(NumberSplit(44651223.56)) // 44,651,223.56

20.数组分类去重

const b = [
  {name: '颜色', value: '黑色'},
  {name: '尺寸', value: 'xxl'},
  {name: '颜色', value: '白色'},
  {name: '大小', value: '小'},
  {name: '尺寸', value: 'xxl'}
]

let hash = {}
let i = 0
let res = []
b.forEach(item => {
  let { name, value } = item
  if (hash[name]) {
    let list = res[hash[name] - 1].value
    if (list.indexOf(value) === -1) { // 相同值不添加
      list.push(value)
    }
  } else {
    hash[name] = ++i && res.push({name, value: [value]})
  }
})
console.log(res)
[  { name: '颜色', value: [ '黑色', '白色' ] },
  { name: '尺寸', value: [ 'xxl' ] },
  { name: '大小', value: [ '小' ] }
]