Taiyi dev

Notes

30 Days of JavaScript

題目列表: https://leetcode.com/studyplan/30-days-of-javascript/

  1. Create Hello World Function
function createHelloWorld() {
  return function (...args): string {
    return "Hello World"
  };
};
  1. Counter
function createCounter(n: number): () => number {
  let count = n
  return function() {
    return count++        
  }
}
  1. To Be Or Not To Be
type ToBeOrNotToBe = {
  toBe: (val: any) => boolean;
  notToBe: (val: any) => boolean;
};

function expect(val: any): ToBeOrNotToBe {
  return {
    toBe: (n) => {
      if(val === n){
        return true
      }
      throw new Error("Not Equal")           
    },
    notToBe: (n) => { 
      if(val !== n){
        return true
      }
      throw new Error("Equal")
    }
  }
};
  1. Counter II
type Counter = {
  increment: () => number,
  decrement: () => number,
  reset: () => number,
}

function createCounter(init: number): Counter {
  let count = init

  return {
    increment: () => ++count,
    decrement: () => --count,
    reset: () => {
      count = init
      return count
    }
  }
};
  1. Apply Transform Over Each Element in Array
function map(arr: number[], fn: (n: number, i: number) => number): number[] {
  const res = []
  
  for(let i = 0; i < arr.length; i++){
    res.push(fn(arr[i], i))
  }

  return res
};
  1. Filter Elements from Array
type Fn = (n: number, i: number) => any

function filter(arr: number[], fn: Fn): number[] {
  const res = []

  for (let i = 0; i < arr.length; i++) {
    if (fn(arr[i], i)) {
      res.push(arr[i])
    }
  }
  return res
};
  1. Array Reduce Transfromation
type Fn = (accum: number, curr: number) => number

function reduce(nums: number[], fn: Fn, init: number): number {
  let res = init

  for (let i = 0; i < nums.length; i++){
    res = fn(res, nums[i])
  }

  return res
};
  1. Function Composition
var compose = function(functions) {
  return function(x) {
    res = x
    for(let i = functions.length - 1; i >= 0; i--){
      res = functions[i](res)
    }
    return res
}
};
  1. Return Length of Arguments Passed
var argumentsLength = function(...args) {
  return args.length
};
  1. Allow One Function Call
var once = function(fn) {
  let isCalled = false

  return function(...args){
    if(isCalled) return undefined
    isCalled = true
    return fn(...args)
  }
};
  1. Memoize
function memoize(fn) {
  cache = {}
  return function(...args) {
    key = args.toString()
    if(key in cache) {
      return cache[key]
    }

    result = fn(...args)
    cache[key] = result
    return result
  }
}
  1. Add Two Promises
var addTwoPromises = async function(promise1, promise2) {
  return await promise1 + await promise2
};
  1. Sleep
async function sleep(millis: number): Promise<void> {
  return new Promise((resolve, reject) => setTimeout(() => resolve(), millis))
}

  1. Timeout Cancellation
function cancellable(fn: Fn, args: JSONValue[], t: number): Function {
    const timer = setTimeout(() => fn(...args), t)
    return () => clearTimeout(timer)
};
  1. Interval Cancellation
function cancellable(fn: Fn, args: JSONValue[], t: number): Function {
  fn(...args)
  const time = setInterval(() => fn(...args), t)
  return () => clearInterval(time)
};
  1. Promise Time Limit
var timeLimit = function (fn, t) {
  return async function (...args) {
    const timeout = new Promise((_, reject) => {
      setTimeout(() => {
        reject("Time Limit Exceeded")
      }, t);
    })

    return Promise.race([fn(...args), timeout])
  }
};

  1. Cache With Time Limit
class TimeLimitedCache {
  cache: Record<number, number>
  timer: Record<number, ReturnType<typeof setTimeout>>
  constructor() {
    this.cache = {}
    this.timer = {}
  }
  
  set(key: number, value: number, duration: number): boolean {
    let isCache = false
    if(key in this.cache) {
      isCache = true
      clearTimeout(this.timer[key])
    }
    this.cache[key] = value
    this.timer[key] = setTimeout(() => {
      delete this.cache[key]
    }, duration)
    return isCache
  }
  
  get(key: number): number {
    return this.cache[key] || -1
  }
  
  count(): number {
    return Object.keys(this.cache).length
  }
}
  1. Debounce
var debounce = function(fn, t) {
  let timer = null
  return function(...args) {
    clearTimeout(timer)
    timer = setTimeout(() => fn(...args), t)
  }
};
  1. Execute Asynchronous Functions in Parallel (implement Promise.all)
var promiseAll = function(functions) {
  return new Promise((resolve, reject) => {
    const results = Array({from: functions.length})
    let count = 0

    functions.forEach((fn, index) => {
      fn().then(v => {
        results[index] = v
        count += 1
        if (count === functions.length) {
          resolve(results)
        }
      })
      .catch(reject)
    })
  })
};

You can't use results.push here because you need to keep the order of functions call.

  1. Is Object Empty
var isEmpty = function(obj) {
  return ["{}", "[]"].includes(JSON.stringify(obj))
};
  1. Chunk Array
var chunk = function(arr, size) {
  const res = []
  let i = 0
  while(i < arr.length){
    const sub = []
    for (let j = 0; j < size; j++){
      if(i < arr.length) sub.push(arr[i++])
    }
    res.push(sub)
  }
  return res
};
var chunk = function(arr, size) {
  const res = []
  let i = 0
  while(i < arr.length){
    res.push(arr.slice(i, i + size))
    i += size
  }
  return res
};
  1. Array Prototype Last
Array.prototype.last = function() {
  const length = this.length
  if(length === 0) return -1
  
  return this[length - 1]
};
  1. Group By
Array.prototype.groupBy = function(fn) {
  return this.reduce((groups, item) => {
    const key = fn(item)
    if(groups[key]){
      groups[key].push(item)
    }else {
      groups[key] = [item]
    }
    return groups
  }, {})
}
  1. Sort By
function sortBy(arr: JSONValue[], fn: Fn): JSONValue[] {
  arr.sort((a, b) => fn(a) - fn(b))
  return arr
};
  1. Join Two Arrays by ID
var join = function(arr1, arr2) {
  res = {}
  for(let item of arr1){
    res[item.id] = item
  }
  for(let item of arr2){
    if(item.id in res) {
      Object.keys(item).forEach(key => res[item.id][key] = item[key])
    }else{
      res[item.id] = item
    }
  }
  return Object.values(res)
};
  1. Flatten Deeply Nested Array
var flat = function (arr, n) {
  const res = []

  const dfs = (arr, times) => {
    for(let item of arr) {
      if(Array.isArray(item) && times > 0){
        dfs(item, times - 1)
      }else {
        res.push(item)  
      }
    }
  }
  dfs(arr, n)
  return res
};
  1. Compact Object
type JSONValue = null | boolean | number | string | JSONValue[] | { [key: string]: JSONValue };
type Obj = Record<string, JSONValue> | Array<JSONValue>;

function compactObject(obj: Obj): Obj {
  if(Array.isArray(obj)) {
    obj = obj.filter((v, index) => {
      if(Array.isArray(v)){
        obj[index] = compactObject(v)
        return true
      }else{
        return Boolean(v)
      }
    })
  }
  if(typeof obj === 'object') {
    Object.keys(obj).forEach(key => {
      if(!Boolean(obj[key])){
        delete obj[key]
      }
      if(Array.isArray(obj[key]) || typeof obj[key] === 'object'){
        obj[key] = compactObject(obj[key] as Array<JSONValue>)
      }
    })
  }
  return obj
};

別人的答案

var compactObject = function(obj) {
  if (obj === null) return null;
  if (Array.isArray(obj)) return obj.filter(Boolean).map(compactObject);
  if (typeof obj !== "object") return obj;

  const compacted = {};
  for (const key in obj) {
    let value = compactObject(obj[key]);
    if (value) compacted[key] = value;
  }

  return compacted;
};
  1. Event Emitter
type Callback = (...args: any[]) => any;
type Subscription = {
  unsubscribe: () => void
}

class EventEmitter {
  list: {eventName: string, callback: Callback}[]

  constructor() {
    this.list = []
  }
  subscribe(eventName: string, callback: Callback): Subscription {
    this.list.push({eventName, callback})

    return {
      unsubscribe: () => {
        const pos = this.list.findIndex(v => v.callback === callback)
        this.list.splice(pos, 1)
        return undefined
      }
    };
  }
  
  emit(eventName: string, args: any[] = []): any[] {
    const res = []
    this.list?.forEach(subscriber => {
      if(subscriber.eventName === eventName){
        res.push(subscriber.callback(...args))
      }
    })
    return res
  }
}
  1. Array Wrapper
class ArrayWrapper {
  nums: number[]
  constructor(nums: number[]) {
    this.nums = nums
  }
  
  valueOf(): number {
    return this.nums.reduce((a, b) => a + b, 0)
  }
  
  toString(): string {
    return JSON.stringify(this.nums)
  }
};

/**
 * const obj1 = new ArrayWrapper([1,2]);
 * const obj2 = new ArrayWrapper([3,4]);
 * obj1 + obj2; // 10
 * String(obj1); // "[1,2]"
 * String(obj2); // "[3,4]"
 */
  1. Calculator with Method Chaining
class Calculator {
  num: number
  constructor(value: number) {
    this.num = value
  }
  
  add(value: number): Calculator {
    this.num += value
    return this
  }
  
  subtract(value: number): Calculator {
    this.num -= value
    return this
  }
  
  multiply(value: number): Calculator {
    this.num *= value
    return this
  }
  
  divide(value: number): Calculator {
    if(value === 0) throw new Error("Division by zero is not allowed")
    this.num /= value
    return this
  }
  
  power(value: number): Calculator {
    this.num = this.num ** value
    return this
  }
  
  getResult(): number {
    return this.num
  }
}