快速搞定力扣30天JavaScript挑战

317 阅读4分钟

第一天

考察知识点: 闭包

2667. 创建 Hello World 函数

var createHelloWorld = function() {
    return function(...args) {
        return "Hello World"
    }
};

2620. 计数器

var createCounter = function(n) {
    return function() {
        return n++;
    };
};

2704. 相等还是不相等

var expect = function(val) {
    return {
        toBe(val2) {
            if (val2 === val) {
                return true;
            }
            throw new Error("Not Equal");
        },
        notToBe(val2) {
            if (val2 !== val) {
                return true;
            }
            throw new Error("Equal");
        },
    }
};

2665. 计数器 II

var createCounter = function(init) {
    let _init = init;
    return {
        increment() {
            init++;
            return init;
        },
        decrement() {
            init--;
            return init;
        },
        reset() {
            init = _init;
            return init;
        },

    } 
};

第二天

考察知识点:基本数组转换

2635. 转换数组中的每个元素

var map = function(arr, fn) {
    const res = [];
    for (let i = 0; i < arr.length; i++) {
        res[i] = fn(arr[i], i);
    }
    return res;
};

2634. 过滤数组中的元素

/**
 * @param {number[]} arr
 * @param {Function} fn
 * @return {number[]}
 */
var filter = function(arr, fn) {
    const filteredArr = [];
    let j = 0;
    for (let i = 0; i < arr.length; i++) {
        if (fn(arr[i], i)) {
            filteredArr[j++] = arr[i];
        }
    }
    return filteredArr;
};

2626. 数组归约运算

/**
 * @param {number[]} nums
 * @param {Function} fn
 * @param {number} init
 * @return {number}
 */
var reduce = function(nums, fn, init) {
    for (let i = 0; i < nums.length; i++) {
        init = fn(init, nums[i]);
    }
    return init;
};

第三天

考察知识点:函数转换

2629. 复合函数

/**
 * @param {Function[]} functions
 * @return {Function}
 */
var compose = function(functions) {
	return function(x) {
        let res = x;
        for (let i = functions.length - 1; i >= 0; i--) {
            res = functions[i](res);
        }
        return res;
    }
};

2703. 返回传递的参数的长度

/**
 * @return {number}
 */
var argumentsLength = function(...args) {
    return args.length;
};

2666. 只允许一次函数调用

/**
 * @param {Function} fn
 * @return {Function}
 */
var once = function(fn) {
    let called = false;
    return function(...args){
        if (called) {
            return;
        }
        const res = fn(...args);
        called = true;
        return res;
    }
};

/**
 * let fn = (a,b,c) => (a + b + c)
 * let onceFn = once(fn)
 *
 * onceFn(1,2,3); // 6
 * onceFn(2,3,6); // returns undefined without calling fn
 */

2623. 记忆函数

/**
 * @param {Function} fn
 */
function memoize(fn) {
    const map = new Map();
    return function(...args) {
        const key = args.join('-');
        if (map.has(key)) {
            return map.get(key);
        }
        const res = fn(...args);
        map.set(key, res);
        return res;
    }
}


/** 
 * let callCount = 0;
 * const memoizedFn = memoize(function (a, b) {
 *	 callCount += 1;
 *   return a + b;
 * })
 * memoizedFn(2, 3) // 5
 * memoizedFn(2, 3) // 5
 * console.log(callCount) // 1 
 */

第四天

考察知识点:Promise和定时器

2723. 添加两个Promise对象

/**
 * @param {Promise} promise1
 * @param {Promise} promise2
 * @return {Promise}
 */
var addTwoPromises = async function(promise1, promise2) {
    return Promise.all([promise1, promise2]).then(([val1, val2]) => {
        return val1 + val2
    })
};

/**
 * addTwoPromises(Promise.resolve(2), Promise.resolve(2))
 *   .then(console.log); // 4
 */

2621. 睡眠函数

/**
 * @param {number} millis
 */
async function sleep(millis) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve();
        }, millis);
    })
}

/** 
 * let t = Date.now()
 * sleep(100).then(() => console.log(Date.now() - t)) // 100
 */

2715. 执行可取消的延迟函数

/**
 * @param {Function} fn
 * @param {Array} args
 * @param {number} t
 * @return {Function}
 */
var cancellable = function(fn, args, t) {
    const timerId = setTimeout(() => fn(...args), t)
    return () => {
        clearTimeout(timerId);
    }
};

/**
 *  const result = []
 *
 *  const fn = (x) => x * 5
 *  const args = [2], t = 20, cancelT = 50
 *
 *  const start = performance.now() 
 *
 *  const log = (...argsArr) => {
 *      const diff = Math.floor(performance.now() - start);
 *      result.push({"time": diff, "returned": fn(...argsArr))
 *  }
 *       
 *  const cancel = cancellable(log, args, t);
 *
 *  const maxT = Math.max(t, cancelT)
 *           
 *  setTimeout(() => {
 *     cancel()
 *  }, cancelT)
 *
 *  setTimeout(() => {
 *     console.log(result) // [{"time":20,"returned":10}]
 *  }, maxT + 15)
 */

2725. 间隔取消

/**
 * @param {Function} fn
 * @param {Array} args
 * @param {number} t
 * @return {Function}
 */
var cancellable = function(fn, args, t) {
    const timerId = setInterval(() => fn(...args), t);
    fn(...args);
    return () => {
        clearInterval(timerId);
    }
};

/**
 *  const result = []
 *
 *  const fn = (x) => x * 2
 *  const args = [4], t = 20, cancelT = 110
 *
 *  const start = performance.now()
 *
 *  const log = (...argsArr) => {
 *      const diff = Math.floor(performance.now() - start)
 *      result.push({"time": diff, "returned": fn(...argsArr)})
 *  }
 *       
 *  const cancel = cancellable(log, args, t);
 *
 *  setTimeout(() => {
 *     cancel()
 *  }, cancelT)
 *   
 *  setTimeout(() => {
 *    console.log(result)  // [
 *                         //      {"time":0,"returned":8},
 *                         //      {"time":20,"returned":8},
 *                         //      {"time":40,"returned":8},           
 *                         //      {"time":60,"returned":8},
 *                         //      {"time":80,"returned":8},
 *                         //      {"time":100,"returned":8}
 *                         //  ]
 *  }, cancelT + t + 15)    
 */

2637. 有时间限制的Promise对象

/**
 * @param {Function} fn
 * @param {number} t
 * @return {Function}
 */
var timeLimit = function(fn, t) {
	return async function(...args) {
        const timerLimitP = new Promise((_, reject) => setTimeout(() => reject("Time Limit Exceeded"), t));  
        return Promise.race([fn(...args), timerLimitP]);
    }
};

/**
 * const limited = timeLimit((t) => new Promise(res => setTimeout(res, t)), 100);
 * limited(150).catch(console.log) // "Time Limit Exceeded" at t=100ms
 */

2622. 有时间限制的缓存


var TimeLimitedCache = function() {
    this.map = new Map();
    this.timerIds = {};
};

/** 
 * @param {number} key
 * @param {number} value
 * @param {number} time until expiration in ms
 * @return {boolean} if un-expired key already existed
 */
TimeLimitedCache.prototype.set = function(key, value, duration) {
    const res = this.map.has(key);
    this.map.set(key, value);
    clearTimeout(this.timerIds[key]);
    this.timerIds[key] = setTimeout(() => {
        this.map.delete(key);
    }, duration);
    return res;
};

/** 
 * @param {number} key
 * @return {number} value associated with key
 */
TimeLimitedCache.prototype.get = function(key) {
    return this.map.get(key) ?? -1;
};

/** 
 * @return {number} count of non-expired keys
 */
TimeLimitedCache.prototype.count = function() {
    return this.map.size;
};

/**
 * Your TimeLimitedCache object will be instantiated and called as such:
 * var obj = new TimeLimitedCache()
 * obj.set(1, 42, 1000); // false
 * obj.get(1) // 42
 * obj.count() // 1
 */

2627. 函数防抖

/**
 * @param {Function} fn
 * @param {number} t milliseconds
 * @return {Function}
 */
var debounce = function(fn, t) {
    let timerId = null;
    return function(...args) {
        if (timerId) {
            clearTimeout(timerId);
            timerId = null;
        }
        timerId = setTimeout(() => {
            fn(...args);
        }, t)
    }
};

/**
 * const log = debounce(console.log, 100);
 * log('Hello'); // cancelled
 * log('Hello'); // cancelled
 * log('Hello'); // Logged at t=100ms
 */

2721. 并行执行异步函数

/**
 * @param {Array<Function>} functions
 * @return {Promise<any>}
 */
var promiseAll = async function(functions) {
    return new Promise((resolve, reject) => {
        const ret = [];
        let count = 0;
        for (let i = 0; i < functions.length; i++) {
            functions[i]().then((val) => {
                
                ret[i] = val;
                count++;
                if (count === functions.length) {
                    resolve(ret);
                }
            }).catch(reject);
        }
    })
};

/**
 * const promise = promiseAll([() => new Promise(res => res(42))])
 * promise.then(console.log); // [42]
 */

第五天

考察知识点:对象

2727. 判断对象是否为空

/**
 * @param {Object | Array} obj
 * @return {boolean}
 */
var isEmpty = function(obj) {
    return Object.keys(obj).length === 0;
};

2677. 分块数组

/**
 * @param {Array} arr
 * @param {number} size
 * @return {Array[]}
 */
var chunk = function(arr, size) {
    const res = [];
    let j = 0;
    for (let i = 0; i < arr.length; i+=size) {
        res[j++] = arr.slice(i, i+size);
    }
    return res;
};

2619. 数组原型对象的最后一个元素

Array.prototype.last = function() {
    return this.length ? this[this.length-1] : -1;
};

/**
 * const arr = [1, 2, 3];
 * arr.last(); // 3
 */

2631. 分组

/**
 * @param {Function} fn
 * @return {Array}
 */
Array.prototype.groupBy = function(fn) {
    const res = {};
    for (let i = 0; i < this.length; i++) {
        const cur = this[i];
        const key = fn(this[i]);
        if (!res[key]) {
           res[key] = [];
        } 
        res[key].push(cur);
    }
    return res;
};

/**
 * [1,2,3].groupBy(String) // {"1":[1],"2":[2],"3":[3]}
 */

2724. 排序方式

/**
 * @param {Array} arr
 * @param {Function} fn
 * @return {Array}
 */
var sortBy = function(arr, fn) {
    return arr.sort((a, b) => fn(a) - fn(b));
};

2722. 根据ID合并两个数组

/**
 * @param {Array} arr1
 * @param {Array} arr2
 * @return {Array}
 */
var join = function(arr1, arr2) {
    let i = 0;
    let j = 0;
    const res = [];
    while(i < arr1.length && j < arr2.length) {
        if (arr1[i].id === arr2[j].id) {
            res.push({...arr1[i], ...arr2[j]})
            i++;
            j++;
        } else if (arr1[i].id < arr2[j].id) {
            res.push(arr1[i]);
            i++;
        } else {
            res.push(arr2[j]);
            j++;
        }
    }

    while(i < arr1.length) {
        res.push(arr1[i]);
        i++;
    }

    while(j <  arr2.length) {
        res.push(arr2[j]);
        j++;
    }

    return res;
};

2625. 扁平化嵌套数组


/**
 * 递归版本
 * @param {any[]} arr
 * @param {number} depth
 * @return {any[]}
 */
var flat = function (arr, n) {
    if (n <= 0) return arr;
    const result = [];
    arr.forEach(item => result.push(...(Array.isArray(item) ? flat(item, n -1) : [item])));
    return result;
};
/**迭代版本
 * @param {any[]} arr
 * @param {number} depth
 * @return {any[]}
 */
var flat = function (arr, n) {
    while(n > 0 && arr.some(Array.isArray)) {
        arr = [].concat(...arr);
        n--;

    }
    return arr;
};

2705. 精简对象

/**
 * @param {Object} obj
 * @return {Object}
 */
var compactObject = function(obj) {
    if (obj == null || typeof obj !== 'object') {
        return obj
    }
    const ret = Array.isArray(obj) ? [] : {};
    if (Array.isArray(obj)) {
        for (let item of obj) {
            let val = compactObject(item);
            if (val) {
                ret.push(val);
            }
        }
        return ret;
    } 

    let keys = Object.keys(obj);
    for (let key of keys) {
        let val = compactObject(obj[key]);
        if (val){
            ret[key] = val;
        }
    }
    return ret;
};

第六天

考察知识点: 类

2694. 事件发射器

class EventEmitter {
  constructor() {
      this.subscribers = {};
  }  
  subscribe(event, cb) {
    if (!this.subscribers[event]) {
        this.subscribers[event] = [];
    }
    this.subscribers[event].push(cb);
    return {
        unsubscribe: () => {
            const cbIndex = this.subscribers[event].findIndex(fn => fn === cb);
            if (cbIndex > -1) {
                this.subscribers[event].splice(cbIndex, 1);
            }
        }
    };
  }

  emit(event, args = []) {
      if (!this.subscribers[event]) {
          return [];
      }

      return this.subscribers[event].map(cb => cb(...args));
  }
}

/**
 * const emitter = new EventEmitter();
 *
 * // Subscribe to the onClick event with onClickCallback
 * function onClickCallback() { return 99 }
 * const sub = emitter.subscribe('onClick', onClickCallback);
 *
 * emitter.emit('onClick'); // [99]
 * sub.unsubscribe(); // undefined
 * emitter.emit('onClick'); // []
 */

2695. 包装数组

/**
 * @param {number[]} nums
 */
var ArrayWrapper = function(nums) {
    this.nums = nums;
};

ArrayWrapper.prototype.valueOf = function() {
    return this.nums.reduce((acc, prev) => acc + prev, 0);
}

ArrayWrapper.prototype.toString = function() {
    return '[' + this.nums.join(',') + ']'
}

/**
 * const obj1 = new ArrayWrapper([1,2]);
 * const obj2 = new ArrayWrapper([3,4]);
 * obj1 + obj2; // 10
 * String(obj1); // "[1,2]"
 * String(obj2); // "[3,4]"
 */

2726. 使用方法链的计算器

class Calculator {
  
  /** 
   * @param {number} value
   */
  constructor(value) {
      this.val = value;
  }

  /** 
   * @param {number} value
   * @return {Calculator}
   */
  add(value){
      this.val += value;
      return this;
  }

  /** 
   * @param {number} value
   * @return {Calculator}
   */
  subtract(value){
      this.val -= value;
      return this;
  }

  /** 
   * @param {number} value
   * @return {Calculator}
   */  
  multiply(value) {
      this.val *= value;
      return this;
  }

  /** 
   * @param {number} value
   * @return {Calculator}
   */
  divide(value) {
      if (value === 0) {
          throw new Error('Division by zero is not allowed');
      }
      this.val /= value;
      return this;    
  }
  
  /** 
   * @param {number} value
   * @return {Calculator}
   */
  power(value) {
      this.val **= value;
      return this;
  }
    
  /** 
   * @return {number}
   */
  getResult() {
      return this.val;
  }
}

参考

力扣30天Javascript挑战