LeetCode JavaScript 30题

69 阅读5分钟

地址:leetcode.cn/studyplan/3…

1 Hello World

function createHelloWorld() {
  return function (...args): string {
    return "Hello World";
  };
}

2 计数器

function createCounter(n: number): () => number {
  let res = n;
  return function () {
    return res++;
  };
}

3 相等还是不相等

type ToBeOrNotToBe = {
  toBe: (val: any) => boolean;
  notToBe: (val: any) => boolean;
};

function expect(val: any): ToBeOrNotToBe {
  return {
    toBe: (v: any) => {
      if (v !== val) throw Error("Not Equal");
      return true;
    },
    notToBe: (v: any) => {
      if (v === val) throw Error("Equal");
      return true;
    },
  };
}

4 计数器 II

type Counter = {
  increment: () => number;
  decrement: () => number;
  reset: () => number;
};

function createCounter(init: number): Counter {
  let currentCount = init;
  return {
    increment() {
      currentCount++;
      return currentCount;
    },
    decrement() {
      currentCount--;
      return currentCount;
    },
    reset() {
      currentCount = init;
      return currentCount;
    },
  };
}

5 转换数组中的每个元素

function map(arr: number[], fn: (n: number, i: number) => number): number[] {
  let res = [];
  arr.forEach((item, index) => {
    res.push(fn(item, index));
  });
  return res;
}

6 过滤数组中的元素

type Fn = (n: number, i: number) => any;

function filter(arr: number[], fn: Fn): number[] {
  let res = [];
  for (let i = 0; i < arr.length; i++) {
    if (fn(arr[i], i)) {
      res.push(arr[i]);
    }
  }
  return res;
}

7 数组归约运算

type Fn = (accum: number, curr: number) => number;

function reduce(nums: number[], fn: Fn, init: number): number {
  let accumulator = init;
  for (const element of nums) {
    accumulator = fn(accumulator, element);
  }
  return accumulator;
}

9 复合函数

type F = (x: number) => number;

function compose(functions: F[]): F {
  return function (x) {
    if (functions.length === 0) return x;
    let input = x;
    for (let i = functions.length - 1; i >= 0; i--) {
      const currFunc = functions[i];
      input = currFunc(input);
    }
    return input;
  };
}

10 返回传递的参数的长度

type JSONValue =
  | null
  | boolean
  | number
  | string
  | JSONValue[]
  | { [key: string]: JSONValue };

function argumentsLength(...args: JSONValue[]): number {
  return args.length;
}

11 只允许一次函数调用

type JSONValue =
  | null
  | boolean
  | number
  | string
  | JSONValue[]
  | { [key: string]: JSONValue };
type OnceFn = (...args: JSONValue[]) => JSONValue | undefined;

function once(fn: Function): OnceFn {
  let isCalled = false;
  return function (...args) {
    if (isCalled) return undefined;
    isCalled = true;
    return fn(...args);
  };
}

12 记忆函数

type Fn = (...params: number[]) => number;

function memoize(fn: Fn): Fn {
  const cache = {};
  return function (...args) {
    const key = JSON.stringify(args);
    if (key in cache) {
      return cache[key];
    }
    const functionOutput = fn(...args);
    cache[key] = functionOutput;
    return functionOutput;
  };
}

13 两个 Promise 对象相加

type P = Promise<number>;

async function addTwoPromises(promise1: P, promise2: P): P {
  const [a, b] = await Promise.all([promise1, promise2]);
  return a + b;
}

14 睡眠函数

async function sleep(millis: number): Promise<void> {
  return new Promise((resolve, reject) => {
    try {
      setTimeout(() => {
        resolve();
      }, millis);
    } catch (err) {
      reject(err);
    }
  });
}

15 执行可取消的延迟函数

type JSONValue =
  | null
  | boolean
  | number
  | string
  | JSONValue[]
  | { [key: string]: JSONValue };
type Fn = (...args: JSONValue[]) => void;

function cancellable(fn: Fn, args: JSONValue[], t: number): Function {
  const timer = setTimeout(() => {
    fn(...args);
  }, t);
  return () => {
    clearTimeout(timer);
  };
}

16 间隔取消

type JSONValue =
  | null
  | boolean
  | number
  | string
  | JSONValue[]
  | { [key: string]: JSONValue };
type Fn = (...args: JSONValue[]) => void;

function cancellable(fn: Fn, args: JSONValue[], t: number): Function {
  fn(...args);
  const timer = setInterval(() => {
    fn(...args);
  }, t);

  return () => {
    clearInterval(timer);
  };
}

17 有时间限制的 Promise 对象

type Fn = (...params: any[]) => Promise<any>;

function timeLimit(fn: Fn, t: number): Fn {
  return async function (...args) {
    return Promise.race([
      fn(...args),
      new Promise((_, reject) =>
        setTimeout(() => reject("Time Limit Exceeded"), t)
      ),
    ]);
  };
}

18 有时间限制的缓存

class TimeLimitedCache {
  private cache;
  constructor() {
    this.cache = new Map();
  }

  set(key: number, value: number, duration: number): boolean {
    const existed = this.cache.get(key);
    this.cache.set(key, {
      value: value,
      duration: Date.now() + duration,
    });
    if (existed) {
      return existed.duration >= Date.now();
    } else {
      return false;
    }
  }

  get(key: number): number {
    const existed = this.cache.get(key);
    if (existed) {
      if (existed.duration >= Date.now()) return existed.value;
      this.cache.delete(key);
    }
    return -1;
  }

  count(): number {
    return [...this.cache.values()].filter((el) => el.duration >= Date.now())
      .length;
  }
}

19 函数防抖

function debounce(fn: F, t: number) {
  let timerId;
  return function (...args) {
    clearTimeout(timerId);
    timerId = setTimeout(() => {
      fn.apply(this, ...args);
    }, t);
  };
}

20 并行执行异步函数

type Fn<T> = () => Promise<T>;

function promiseAll<T>(functions: Fn<T>[]): Promise<T[]> {
  return new Promise((resovle, reject) => {
    if (functions.length === 0) {
      resovle([]);
      return;
    }

    const res = new Array(functions.length).fill(null);
    let reslovedCount = 0;
    functions.forEach(async (el, idx) => {
      try {
        const subResult = await el();
        res[idx] = subResult;
        reslovedCount++;
        if (reslovedCount === functions.length) {
          resovle(res);
        }
      } catch (err) {
        reject(err);
      }
    });
  });
}

21 判断对象是否为空

type JSONValue =
  | null
  | boolean
  | number
  | string
  | JSONValue[]
  | { [key: string]: JSONValue };
type Obj = Record<string, JSONValue> | JSONValue[];

function isEmpty(obj: Obj): boolean {
  if (Array.isArray(obj)) {
    return obj.length === 0;
  }
  return Object.keys(obj).length === 0;
}

22 分块数组

type JSONValue =
  | null
  | boolean
  | number
  | string
  | JSONValue[]
  | { [key: string]: JSONValue };
type Obj = Record<string, JSONValue> | Array<JSONValue>;

function chunk(arr: Obj[], size: number): Obj[][] {
  const result = [];
  for (let i = 0; i < arr.length; i = i + size) {
    result.push(arr.slice(i, i + size));
  }
  return result;
}

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

declare global {
  interface Array<T> {
    last(): T | -1;
  }
}

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

24 分组

/**
 * @param {Function} fn
 * @return {Object}
 */
Array.prototype.groupBy = function (fn) {
  const map = new Map();
  this.forEach((item) => {
    const key = fn(item);
    const arr = map.get(key) || [];
    arr.push(item);
    map.set(key, arr);
  });
  const ans = {};
  for (let [key, val] of map) {
    ans[key] = val;
  }
  return ans;
};

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

25 排序方式

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

26 根据 ID 合并两个数组

/**
 * @param {Array} arr1
 * @param {Array} arr2
 * @return {Array}
 */
var join = function (arr1, arr2) {
  const datas = new Map();
  for (let item of arr1) {
    update(item, datas);
  }
  for (let item of arr2) {
    update(item, datas);
  }

  return [...datas.values()].sort((a, b) => a.id - b.id);
};

function update(change, datas) {
  let target = {};
  if (datas.has(change.id)) target = datas.get(change.id);
  else datas.set(change.id, target);

  for (let key of Object.keys(change)) {
    target[key] = change[key];
  }
}

27 扁平化嵌套数组

/**
 * @param {Array} arr
 * @param {number} depth
 * @return {Array}
 */
var flat = function (arr, n) {
  if (n <= 0) return arr;
  const res = [];
  for (const item of arr) {
    if (Array.isArray(item)) {
      res.push(...flat(item, n - 1));
    } else {
      res.push(item);
    }
  }
  return res;
};

28 精简对象

/**
 * @param {Object|Array} obj
 * @return {Object|Array}
 */
var compactObject = function (obj) {
  if (obj == null || typeof obj !== "object") {
    return obj;
  }

  if (Array.isArray(obj)) {
    const res = [];
    for (let item of obj) {
      const val = compactObject(item);
      if (val) res.push(val);
    }
    return res;
  }

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

29 事件发射器

class EventEmitter {
  constructor() {
    this.events = {};
  }

  /**
   * @param {string} eventName
   * @param {Function} callback
   * @return {Object}
   */
  subscribe(eventName, callback) {
    this.events[eventName] = this.events[eventName] ?? [];
    this.events[eventName].push(callback);
    return {
      unsubscribe: () => {
        this.events[eventName] = this.events[eventName].filter(
          (f) => f !== callback
        );
        if (this.events[eventName].length === 0) {
          delete this.events[eventName];
        }
      },
    };
  }

  /**
   * @param {string} eventName
   * @param {Array} args
   * @return {Array}
   */
  emit(eventName, args = []) {
    if (!(eventName in this.events)) return [];
    return this.events[eventName].map((f) => f(...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'); // []
 */

30 包装数组

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

/**
 * @return {number}
 */
ArrayWrapper.prototype.valueOf = function () {
  return this.values.reduce((pre, cur) => pre + cur, 0);
};

/**
 * @return {string}
 */
ArrayWrapper.prototype.toString = function () {
  return "[" + this.values + "]";
};

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

31 使用方法链的计算器

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

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

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

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

  /**
   * @param {number} value
   * @return {Calculator}
   */
  divide(value) {
    if (value === 0) throw "Division by zero is not allowed";
    this.value /= value;
    return this;
  }

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

  /**
   * @return {number}
   */
  getResult() {
    return this.value;
  }
}