題目列表: https://leetcode.com/studyplan/30-days-of-javascript/
- Create Hello World Function
function createHelloWorld() {
return function (...args): string {
return "Hello World"
};
};
- Counter
function createCounter(n: number): () => number {
let count = n
return function() {
return count++
}
}
- 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")
}
}
};
- 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
}
}
};
- 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
};
- 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
};
- 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
};
- 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
}
};
- Return Length of Arguments Passed
var argumentsLength = function(...args) {
return args.length
};
- Allow One Function Call
var once = function(fn) {
let isCalled = false
return function(...args){
if(isCalled) return undefined
isCalled = true
return fn(...args)
}
};
- 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
}
}
- Add Two Promises
var addTwoPromises = async function(promise1, promise2) {
return await promise1 + await promise2
};
- Sleep
async function sleep(millis: number): Promise<void> {
return new Promise((resolve, reject) => setTimeout(() => resolve(), millis))
}
- Timeout Cancellation
function cancellable(fn: Fn, args: JSONValue[], t: number): Function {
const timer = setTimeout(() => fn(...args), t)
return () => clearTimeout(timer)
};
- Interval Cancellation
function cancellable(fn: Fn, args: JSONValue[], t: number): Function {
fn(...args)
const time = setInterval(() => fn(...args), t)
return () => clearInterval(time)
};
- 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])
}
};
- 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
}
}
- Debounce
var debounce = function(fn, t) {
let timer = null
return function(...args) {
clearTimeout(timer)
timer = setTimeout(() => fn(...args), t)
}
};
- 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.
- Is Object Empty
var isEmpty = function(obj) {
return ["{}", "[]"].includes(JSON.stringify(obj))
};
- 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
};
- Array Prototype Last
Array.prototype.last = function() {
const length = this.length
if(length === 0) return -1
return this[length - 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
}, {})
}
- Sort By
function sortBy(arr: JSONValue[], fn: Fn): JSONValue[] {
arr.sort((a, b) => fn(a) - fn(b))
return arr
};
- 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)
};
- 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
};
- 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;
};
- 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
}
}
- 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]"
*/
- 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
}
}