一些实用的小方法

查找是否含有子序列

function isFuzzMatch(lq,sq){
	let j=0;
	for(let i=0;i<lq.length;i++){
		if(lq[i]===sq[j]){
			j++
		}else{
			j=0;
		}
	}
	return j===sq.length;
}

isFuzzMatch("h", "e", "l", "l", "o"],["e", "l", "o"])

将数字转为会计计数

不带货币单位

function formatCurrency(num){
   return num.toLocalString('zh-CN')
}

formatCurrency(1000)

带货币单位

function formatCurrency(num) {
  return num.toLocaleString("zh-CN", {
    style: "currency",
    currency: "CNY",
  });
}
formatCurrency(1000);

正则表达

function formatCurrency(num) {
  let [intPart, decPart] = Math.abs(num).toFixed(2).split("."); // 保留2位小数
  intPart = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ","); // 千分位
  return (num < 0 ? "-" : "") + intPart + "." + decPart;
}
formatCurrency(1000);

查找重复元素

function findDuplicates(arr){
	const hash=new Map();
	const duplicates=new Set();
	for(let i of arr){
		if(hash.has(i)){
			duplicates.add(i)
		}
		hash.set(i,true)
	}
	return Array.from(duplicates)
}
findDuplicates([1, 2, 3, 4, 5, 2, 3, 6, 7, 3])

数组去重

new Set

function uniq(arr) {
  return Array.from(new Set(arr));
}
uniq([1, 1, 1, 2, 2]);

filter

function uniq(arr) {
  return arr.filter((item, index) => arr.indexOf(item) === index);
}
uniq([1, 1, 1, 2, 2]);

对象数组使用Map去重

function uniq(arr, key) {
  return Array.from(new Map(arr.map((item) => [item[key], item])).values());
}
uniq(
  [
    { id: 1, name: "A" },
    { id: 2, name: "B" },
    { id: 1, name: "A" },
  ],
  "id"
);

class实现react setState

class Component {
  constructor(initialState = {}) {
    this.state = initialState;
    this.pendingStates = []; // 存储所有待处理的更新
    this.callbacks = []; // 存储所有待执行的回调
    this.isBatchingUpdate = false; // 标记是否已安排批量更新
  }

  // 模拟 setState(支持对象或函数形式的更新)
  setState(update, callback) {
    this.pendingStates.push(update);
    if (callback) {
      this.callbacks.push(callback);
    }

    // 如果还未安排批量更新,则安排一个批量任务
    if (!this.isBatchingUpdate) {
      this.isBatchingUpdate = true;
      // 使用 setTimeout 模拟批量调度:在当前事件循环结束后执行
      setTimeout(() => this.flushUpdates(), 0);
    }
  }

  flushUpdates() {
    // 依次合并所有 pendingStates 得到最终 state
    let nextState = this.state;
    this.pendingStates.forEach((update) => {
      if (typeof update === "function") {
        nextState = { ...nextState, ...update(nextState) };
      } else {
        nextState = { ...nextState, ...update };
      }
    });
    this.state = nextState;
    // 清空 pendingStates 和回调列表
    this.pendingStates = [];

    // 批量更新完成后,统一执行所有回调
    this.callbacks.forEach((cb) => cb(this.state));
    this.callbacks = [];

    // 重置批量标记
    this.isBatchingUpdate = false;
  }
}

最长的不包含重复字符的子字符串的长度

/**
 * 计算输入字符串中最长的不包含重复字符的子字符串的长度。
 * 使用滑动窗口和哈希表来记录字符的索引,从而提高效率。
 *
 * @param {string} ls - 输入字符串
 * @returns {number} - 最长的不包含重复字符的子字符串的长度
 */
function lengthOfLongestSubstring(ls: string): number {
  // 初始化最大长度为0
  let maxLength = 0;
  // 初始化滑动窗口的起始位置为0
  let start = 0;
  // 创建一个哈希表来存储字符及其对应的索引
  const charIndexMap: { [key: string]: number } = {};

  // 遍历输入字符串
  for (let end = 0; end < ls.length; end++) {
    // 获取当前字符
    const char = ls[end];
    // 如果当前字符已经在哈希表中,并且其索引在当前滑动窗口内
    if (charIndexMap[char] !== undefined && charIndexMap[char] >= start) {
      // 将滑动窗口的起始位置移动到重复字符的下一个位置
      start = charIndexMap[char] + 1;
    }
    // 更新或添加当前字符的索引到哈希表
    charIndexMap[char] = end;
    // 更新最大长度
    maxLength = Math.max(maxLength, end - start + 1);
  }

  // 返回最大长度
  return maxLength;
}

indexOf查找

/**
 * 计算输入字符串中最长的不包含重复字符的子字符串的长度。
 * 使用滑动窗口和字符串操作来记录字符的索引,从而提高效率。
 *
 * @param {string} ls - 输入字符串
 * @returns {number} - 最长的不包含重复字符的子字符串的长度
 */
function lengthOfLongestSubstring(ls: string): number {
  // 初始化最大长度为0
  let maxLength = 0;
  // 初始化当前子字符串为空
  let currentSubstring = "";

  // 遍历输入字符串
  for (let i = 0; i < ls.length; i++) {
    const char = ls[i];
    const index = currentSubstring.indexOf(char);

    // 如果当前字符在当前子字符串中存在
    if (index !== -1) {
      // 更新最大长度
      maxLength = Math.max(maxLength, currentSubstring.length);
      // 将当前子字符串截取到重复字符的下一个位置
      currentSubstring = currentSubstring.substring(index + 1);
    }

    // 将当前字符添加到当前子字符串
    currentSubstring += char;
  }

  // 返回最大长度,确保包括最后一个子字符串的长度
  return Math.max(maxLength, currentSubstring.length);
}
function fn(ls) {
  let aw = "";
  let i = 0;
  for (let j = 0; j < ls.length; j++) {
    if (aw.indexOf(ls[j]) > -1) {
      i = Math.max(aw.length, i);
      aw = "";
    } else {
      aw += ls[j];
    }
  }
  return i;
}

斐波那契数

/**
 * 计算第 n 个斐波那契数。
 * 
 * 斐波那契序列是一个由数字组成的系列,其中每个数字是前两个数字的和,通常从 0 和 1 开始。
 * 在此函数中,使用迭代方法计算第 n 个斐波那契数,避免了递归调用带来的性能问题。
 * 
 * @param n 斐波那契序列中的位置,从 0 开始。
 * @returns 第 n 个斐波那契数。
 * @throws {Error} 如果输入为负整数,则抛出错误。
 */
function fibonacci(n: number): number {
  // 验证输入以确保其为非负数
  if (n < 0) throw new Error("输入必须是非负整数");

  // 如果 n 小于等于 1,则直接返回 n,因为结果就是输入本身
  if (n <= 1) return n;

  // 初始化前两个斐波那契数
  let a = 0, b = 1;

  // 通过循环计算第 n 个斐波那契数
  for (let i = 2; i <= n; i++) {
    // 使用解构赋值更新 a 和 b 的值,实现数值的交换与更新
    [a, b] = [b, a + b];
  }

  // 返回计算得到的第 n 个斐波那契数
  return b;
}

深拷贝函数

/**
 * 深拷贝一个给定的对象。
 * 深拷贝意味着所有嵌套的对象或数组都会被拷贝,而不仅仅是它们的引用。
 * @param obj 需要深拷贝的对象。
 * @returns 返回深拷贝后的对象。
 */
function deepClone(obj: any) {
  // 检查obj是否是对象或数组,如果不是则直接返回
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }
  
  // 初始化拷贝的对象,如果原对象是数组则初始化为空数组,否则初始化为空对象
  let cObj: any = Array.isArray(obj) ? [] : {};
  
  // 遍历原对象的所有属性
  for (let key in obj) {
    // 确保只拷贝对象自身的属性,而不是原型链上的属性
    if (obj.hasOwnProperty(key)) {
      // 递归调用deepClone,深拷贝每个属性值
      cObj[key] = deepClone(obj[key]);
    }
  }
  
  // 返回深拷贝后的对象
  return cObj;
}

快速排序

/**
 * 快速排序算法的默认实现
 * 
 * 此函数使用分治法对数组进行排序,通过递归方式将数组分为较小的部分并进行排序
 * 它选取一个基准值,然后将数组分为两部分:一部分包含小于基准值的元素,另一部分包含大于基准值的元素
 * 这个过程一直重复,直到整个数组变得有序
 * 
 * @param arr 要排序的数组
 * @param left 排序开始的索引,默认为数组的起始位置
 * @param right 排序结束的索引,默认为数组的结束位置
 */
function quickSort(arr: number[], left: number = 0, right: number) {
  // 检查递归的终止条件,如果左指针大于右指针,则终止递归
  if (left > right) return;

  // 选取基准值,这里选择数组最左边的元素作为基准
  let base = arr[left],
    i = left,
    j = right;

  // 开始寻找基准值的正确位置,直到i和j相遇
  while (i != j) {
    // 从右向左找到第一个小于基准值的元素
    while (arr[j] >= base && i < j) {
      j--;
    }
    // 从左向右找到第一个大于基准值的元素
    while (arr[i] <= base && i < j) {
      i++;
    }
    // 如果找到的两个元素不满足条件,则交换它们的位置
    [arr[i], arr[j]] = [arr[j], arr[i]];
  }

  // 将基准值放到正确的位置上
  arr[left] = arr[i];
  arr[i] = base;

  // 递归对基准值左边的数组进行排序
  quickSort(arr, left, i - 1);
  // 递归对基准值右边的数组进行排序
  quickSort(arr, i + 1, right);
}

字符串中频率最高的字符

/**
 * 找出字符串中出现频率最高的字符
 * @param ls 输入的字符串
 * @returns 出现频率最高的字符
 */
function findMostFrequentChar(ls: string) {
  // 创建一个哈希表来记录每个字符的出现次数
  let hash: { [x: string]: number } = {}
  // 初始化最大出现次数为0
  let maxCount = 0;
  // 初始化出现频率最高的字符为空字符串
  let mostFrequentChar = ''
  // 遍历字符串中的每个字符
  for (const i of ls) {
    // 在哈希表中更新字符的出现次数,如果字符不存在则初始化为0后加1
    hash[i] = (hash[i] || 0) + 1;
    // 如果当前字符的出现次数超过了最大出现次数
    if (hash[i] > maxCount) {
      // 更新出现频率最高的字符为当前字符
      mostFrequentChar = i
      // 更新最大出现次数为当前字符的出现次数
      maxCount = hash[i]
    }
  }
  // 返回出现频率最高的字符
  return mostFrequentChar;
}

逆波兰表达式

function evalRPN(tokens) {
    // 定义一个栈用于存储操作数
    const stack = [];
    
    // 定义运算符及其对应的计算方法
    const operators = {
        '+': (a, b) => a + b,
        '-': (a, b) => a - b,
        '*': (a, b) => a * b,
        '/': (a, b) => Math.trunc(a / b)
    };

    // 遍历输入的 token 数组
    for (const token of tokens) {
        // 如果 token 是运算符,则弹出两个操作数进行计算
        if (operators[token]) {
            const b = stack.pop(); // 取出栈顶元素作为右操作数
            const a = stack.pop(); // 取出次栈顶元素作为左操作数
            stack.push(operators[token](a, b)); // 计算结果并压入栈中
        } else {
            // 如果 token 是数字,则转换为数字类型并压入栈中
            stack.push(Number(token));
        }
    }

    // 返回栈顶元素,即最终计算结果
    return stack[0];
}

// 示例用法
const tokens = ["4", "13", "5", "/", "+"];
console.log(evalRPN(tokens)); // 输出 6

一些实用的小方法
https://blog.fullsize.cn/2025/02/08/notion/yi-xie-shi-yong-de-xiao-fang-fa/
作者
fullsize
发布于
2025年2月8日
许可协议