npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@mrtujiawei/utils

v2.5.52

Published

工具:常用数据类型、算法

Downloads

2,383

Readme

@mrtujiawei/utils

npm version install size npm downloads js delivr downloads license

安装

使用 npm:

$ npm install @mrtujiawei/utils

使用 yarn:

$ yarn add @mrtujiawei/utils

使用 unpkg CDN:

<script src="https://unpkg.com/@mrtujiawei/utils"></script>

使用 jsdelivr CDN:

<script src="https://cdn.jsdelivr.net/npm/@mrtujiawei/utils"></script>

用法

直接在浏览器中使用

<script src="https://cdn.jsdelivr.net/npm/@mrtujiawei/utils"></script>
<script>
  const { Stack } = TUtils;
</script>

通过 CommonJS 引入

const { Stack } = require('@mrtujiawei/utils');

或者

const utils = require('@mrtujiawei/utils');
const Stack = utils.Stack;

通过 ESModule 引入

import { Stack } from '@mrtujiawei/utils';

或者

import * as utils from '@mrtujiawei/utils';
const Stack = utils.Stack;

使用示例

数据结构

Stack

堆栈(链表实现)

引入

import { Stack } from '@mrtujiawei/utils';

实例化

const stack = new Stack();

获取当前元素个数

stack.size;

判断栈是否为空

stack.isEmpty();

判断栈是否非空

stack.isNotEmpty();

入栈

  • 支持一次 push 多个元素
stack.push(value);

出栈

  • 栈为空时,pop会抛出StackEmptyError
stack.pop();

获取栈顶元素

  • 栈为空时,peak会抛出StackEmptyError
stack.peak();

异常

// 栈为空时获取元素
Stack.StackEmptyError

Queue

队列(双端链表实现)

引入

import { Queue } from '@mrtujiawei/utils';

实例化

const queue = new Queue();

获取当前元素个数

queue.size;

队首元素

  • 队列为空时获取front会抛出QueueEmptyError
queue.front;

队尾元素

  • 队列为空时获取tail会抛出QueueEmptyError
queue.tail;

判断队列是否为空

queue.isEmpty();

入队

  • 支持一次 enqueue 多个元素
queue.enqueue(value);

出队

  • 队列为空时dequeue会抛出QueueEmptyError
queue.dequeue();

异常

// 队列为空时仍获取元素
Queue.QueueEmptyError

Deque

双端队列(双端链表实现)

引入

import { Deque } from '@mrtujiawei/utils';

实例化

const deque = new Deque();

从队头入队

deque.pushFront(value);

从队尾入队

deque.pushBack(value);

判断队列是否为空

deque.isEmpty();

判断队列是否非空

deque.isNotEmpty();

获取队列元素个数

deque.getSize();

获取队首元素

  • 队列为空时getFront会抛出DequeEmptyError
deque.getFront();

获取队尾元素

  • 队列为空时getBack会抛出DequeEmptyError
deque.getBack();

队列转数组

deque.toArray();

从队头出队

  • 队列为空时popFront会抛出DequeEmptyError
deque.popFront();

从队尾出队

  • 队列为空时popBack会抛出DequeEmptyError
deque.popBack();

清空队列

deque.clear();

异常

// 队列未空时仍获取元素
Deque.DequeEmptyError;

LinkedList

单向链表

引入

import { LinkedList } from '@mrtujiawei/utils';

实例化

const list = new LinkedList();

获取当前元素个数

list.getSize();

链表是否为空

list.isEmpty();

链表是否非空

list.isNotEmpty();

清空链表

list.clear();

从链表头部插入

list.insertFirst(value);

指定下标处插入

  • 0 <= index <= list.getSize()
  • 下标范围不对时会抛出InvalidIndexError
  • 建议尽量少用,最坏时间复杂度 O(n)
list.insertAt(index, value);

链表尾部插入

  • 建议尽量少用,时间复杂度 O(n)
list.insertLast(value);

移除头结点

  • 链表为空时removeFirst会抛出LinkedListEmptyError
list.removeFirst();

移除尾节点

  • 链表为空时removeLast会抛出LinkedListEmptyError
list.removeLast();

移除指定下标的节点

  • 下标范围不对时会抛出InvalidIndexError
list.removeAt(index);

复制链表

list.clone(oldList);

遍历链表

list.forEach((value, index, context) => {
  // TODO ...
});

过滤出符合条件的新链表

  • Boolean(returnValue) 为 true 保留, false 不保留
list.filter((value, index, context) => {
  // TODO ...
});

查找第一个满足条件的值

  • Boolean(returValue) 为 true 时,返回对应的value
list.find((value, index, context) => {
  // TODO
});

获取第一个节点值

  • 链表为空时getFirst会抛出LinkedListEmptyError
list.getFirst();

转化成数组

list.toArray();

从数组创建链表

let list = LinkedList.fromArray(arr);

for..of 遍历

for (const value of list) {
  // TODO
}

异常

// 为空时获取元素
LinkedList.LinkedListEmptyError;

// 传入不合理的下标
LinkedList.InvalidIndexError;

DoublyLinkedList

双向链表

引入

import { DoublyLinkedList } from '@mrtujiawei/utils';

实例化

const list = new DoublyLinkedList();

清空链表

list.clear();

连接两个链表

  • 只是单纯的尾节点连接头结点
  • 继续操作原来的链表会影响连接后的链表
const newList = list.concat(otherList);

是否包含

list.contains(value);

过滤出符合条件的新链表

  • Boolean(returnValue) 为 true 保留, false 不保留
const newList = list.filter((value, index, context) => {
  // TODO
});

查找第一个满足要求的元素

const value = list.find((value, index, context) => {
  // TODO
});

查找第一个满足要求的元素下标

const index = list.findIndex((value, index, context) => {
  // TODO
});

forEach遍历

list.forEach((value, index, context) => {
  // TODO
});

获取指定下标的值

  • 0 <= index && index < list.size()
  • 不在范围内的下标会抛出 InvalidIndexError
list.get(index);

获取链表中的第一个值

  • 链表为空时,会抛出EmptyError
const value = list.getFirst();

获取链表中的最后一个值

  • 链表为空时,会抛出EmptyError
const value = list.getLast();

是否包含某个值

list.includes();

第一个等于指定值的下标

  • 不存在时 index = -1
const index = list.indexOf(value);

判断链表是否为空

list.isEmpty();

根据参数合并成字符串

  • 可以传入第二个参数,用来将元素转化成字符串
const result = list.join(',');

最后一个满足条件的元素下标

const index = lastIndexOf(value);

映射成一个新的链表

const newList = list.map(value => {
  // TODO
  return newValue;
});

移除最后一个元素

  • 链表为空时会抛出EmptyError
const value = list.pop();

向尾部添加

list.push(value);

元素汇总

list.reduce((previousValue, currentValue, index, context) => {
  // TODO
  return nextValue;
});

元素反向汇总

list.reduceRight((previousValue, currentValue, index, context) => {
  // TODO
  return nextValue;
});

移除指定下标的元素

  • 0 <= index < list.size()
  • 下标不合法时会抛出 InvalidIndexError
const value = list.remove(index);

反转链表

list.reverse();

设置指定位置的值

  • 0 <= index < list.size()
  • 下标不合法时会抛出 InvalidIndexError
list.set(index, value);

移除第一个元素

  • 链表为空时会抛出 EmptyError
list.shift();

获取链表长度

list.size();

复制链表中的一段

  • 浅拷贝
  • [startIndex, endIndex)
list.slice(startIndex, endIndex);

查找是否有满足条件的值

list.some((value, index, context) => {
  // TODO
  return false || true;
})

转化成数组

const arr = list.toArray();

头部添加

list.unshift(value);

链表排序

  • 同数组排序
sort((a, b) => number);

转化成字符串

const str = list.toString();

移除所有满足条件的元素

const removeSize = list.remove((value, index, context) => {
  // TODO
  return true || false;
});

反向遍历

list.forEachReverse((value, index, context) => {
  // TODO
});

反向查找

const value = list.findReverse((value, index, context) => {
  // TODO
  return true || false;
});

for..of 遍历

for (const value of list) {
  // TODO
}

异常

// 下标异常
DoublyLinkedList.InvalidIndexError

// 链表为空
DoublyLinkedList.EmptyError

Skiplist

引入

import { Skiplist } from '@mrtujiawei/utils';

初始化

  • 如果 a < b 预期返回小于0的数
  • 如果 a == b 预期返回0
  • 如果 a > b 预期返回大于0的数
  • 递减取相反数
const list = new Skiplist((a, b) => a - b);

获取长度

list.length;

查找是否存在指定值

list.search(value);

获取第一个元素

const firstValue = list.getFirst();

插入元素

list.insert(value);

移除元素

list.remove(value);

转化成数组

list.toArray();

遍历

list.forEach((value, index, context) => {
  // TODO
});

for..of 遍历

for (const value of list) {
  // TODO
}

Heap

引入

import { Heap } from '@mrtujiawei/utils';

实例化(小顶堆)

  • 如果 a < b 预期返回小于0的数
  • 如果 a == b 预期返回0
  • 如果 a > b 预期返回大于0的数
  • 大顶堆取相反数
const heap = new Heap((a, b) => a - b);

获取堆中元素数量

heap.size;

判断堆是否为空

heap.isEmpty();

判断堆是否非空

Heap.isNotEmpty();

获取堆顶元素

  • 堆为空时peak会抛出Heap.HeapEmptyError
heap.peak();

插入元素

heap.insert(value);

移除堆顶元素

  • 堆为空时peak会抛出Heap.HeapEmptyError
heap.remove();

替换堆顶元素

  • 如堆为空则插入
  • 非空则替换栈顶元素,并重新调整堆
  • 两次O(logn)时间复杂度的操作减少为一次
heap.replaceTop(0);

异常

// 堆为空时获取元素
Heap.HeapEmptyError

// 比较器错误
Heap.CompareInvalidError

UnionFind

并查集

引入

import { UnionFind } from '@mrtujiawei/utils';

实例化

  • capacity不能小于1 IllegaleArgumentException
const uf = new UnionFind(capacity);

合并集合

  • 0 <= u1 < capacity
  • 0 <= u2 < capacity
uf.union(u1, u2);

查找集合的根节点

uf.find(u);

是否属于同一个集合

uf.isBelongSameUnion(u1, u2);

BinarySearchTree

二叉搜索树

import { BinarySearchTree } from '@mrtujiawei/utils';

const bTree = new BinarySearchTree((a, b) => a - b, []);
bTree.append(1);
bTree.append(2);
bTree.append(3);
bTree.append(4);

// 执行4次
// 1, 2, 3, 4
bTree.inorderTraversal((value) => {
  console.log(value);
});
bTree.getMin(); // 1
bTree.getMax(); // 4
bTree.toArray(); // [1, 2, 3, 4]
bTree.clear(); // []

AVLTree

平衡二叉搜索树

引入

import { AVLTree } from '@mrtujiawei/utils';

实例化

  • 如果 a < b 预期返回小于0的数
  • 如果 a == b 预期返回0
  • 如果 a > b 预期返回大于0的数
  • 取相反数时,左节点 > 父节点 > 右节点
const tree = new AVLTree((a, b) => a - b);

添加节点

  • 添加相同的key时会抛出DuplicateValueError
tree.append(key, value);

根据 key 移除节点

  • 返回true时删除成功,否则失败
tree.remove(key);

判断是否存在key

tree.has(key);

获取key对应的value

const value = tree.getValue(key);

获取节点个数

const size = tree.getSize();

清空树

tree.clear();

异常

// 插入已经存在的key
AVLTree.DuplicateValueError

Trie

前缀树(支持插入相同单词)

引入

import { Trie } from '@mrtujiawei/utils';

实例化

const trie = new Trie();

从数组实例化

const trie = Trie.fromArray(words);

获取字典树中有多少个单词

const count = trie.getWordCount();

插入单词

trie.insert(word);

判断是否存在该单词

trie.search(word);

判断指定前缀的单词是否存在

trie.startsWith(prefix);

移除指定单词

  • 返回false移除失败:单词不存在
  • 返回true移除成功
  • 如有多个相同个单词,只会移除一个
trie.remove(word);

遍历

  • 不保证遍历的顺序
trie.forEach((word) => {
  // TODO
});

转化成数组

  • 包含重复单词
const words = trie.toArray();

清空前缀树

trie.clear();

Lock

异步流程加锁

import { Lock, sleep }  from '@mrtujiawei/utils';

const lock = new Lock(1);

/**
 * 异步任务只有等上次任务结束后才会开始执行下一个异步任务
 */
const run = async (value, timeout) => {
  try {
    await lock.lock();
    // 异步任务
    await sleep(timeout);
    console.log(value);
  } finally {
    lock.unlock();
  }
};

run(0, 1000);
run(1, 100);
run(2, 0);

output: 0 1 2

TaskQueue

任务队列,主要是用来执行单任务

ResponsibilityChain

职责链

DateTimeTool

日期时间处理类
解析时间太复杂,没做

import { DateTimeTool } from '@/mrtujiawei/utils';

DateTimeTool.timeFormat(new Date(), ':'); // hh:mm:ss
DateTimeTool.dateFormat(new Date(), '-'); // yyyy-mm-dd
DateTimeTool.dateTimeFormat(new Date()); // yyyy-mm-dd hh:mm:ss
DateTimeTool.getNthDayBefore(2); // 获取n天以前时间和当前日期时间
DateTimeTool.getNthHourBefore(2); // 获取n小时之前到当前时间
DateTimeTool.getNthMonthBefore(1); // 获取n月以前时间到当前月时间
DateTimeTool.toDayBegin(new Date()); // 设置到当前天的开始 00:00:00.000
DateTimeTool.toDayEnd(new Date()); // 设置到当前天的结束 23:59:59.999
DateTimeTool.isLeapYear(new Date()); // 是否是闰年
DateTimeTool.diffTimestamp(new Date(), new Date()); // 时间戳差值
DateTimeTool.diffSeconds(new Date(), new Date()); // 秒差值
DateTimeTool.diffMinutes(new Date(), new Date()); // 分钟差值
DateTimeTool.diffHours(new Date(), new Date()); // 小时差值
DateTimeTool.diffDays(new Date(), new Date()); // 天差值
DateTimeTool.addDays(new Date(), 10); // 日期往后加10天
DateTimeTool.timestampToTime(123); // hh:mm:ss
DateTimeTool.timestampToDate(123); // yyyy-mm-dd
DateTimeTool.timestampToDateTime(123); // yyyy-mm-dd hh:mm:ss
DateTimeTool.getCurrentWeek(new Date()); // 获取当周的日期范围

CountDown

倒计时

import { CountDown } from '@mrtujiawei/utils';

const countDown = new CountDown('默认信息');
const callback = countDown.subscribe((data) => {
  // 结束倒计时
  if (data.done) {
    data.message; // 默认信息
  } else {
    data.message; // 倒计时数字 60, 59...
  }
});

countDown.start({
  start: 60,
  end: 0,
  timeout: 1,
});

// 取消其中的一个订阅
countDown.unsubscribe(callback);

// 清空所有订阅函数
countDown.clear();

Pagination

分页

import { Pagination } from '@mrtujiawei/utils';

const tableData = {
  pageNum: 1,
  pageSize: 10,
  tableData: [],
  total: 0,
};

const pagination = new Pagination(tableData);
pagination.subscribe((tableData) => {
  console.log(tableData);
});
pagination.setPageSize(20);
const key = 'key';
pagination.setOrder(key); // 设置排序
pagination.sort(); // 重新排序
pagination.to(2); // 跳到第二页

Logger

日志记录

import { Logger } from '@mrtujiawei/utils';

const logger = Logger.getLogger();
const callback = logger.subscribe((message) => {
  console.log(message);
});
logger.setLevel(Logger.LOG_LEVEL.ALL);
logger.trace('info');
logger.info('info');
logger.debug('debug');
logger.warn('warn');
logger.error('error');
logger.fatal('fatal');
logger.unsubscribe(callback);

Events

事件发射

const events = new Events();

const listener = events.on('start', (...data) => {
  console.log(data);
});

events.once('start', (...data) => {
  console.log(data);
});

events.emit('start', 1, 2, 3);
// 1 2 3
// 1 2 3

events.off('start', listener);
events.emit('start');
// 没有输出

Random

随机

import { Random } from '@mrtujiawei/utils';

Random.getRandomNumber(100, 1000); // [100, 1000)
Random.getRandomBoolean(); // true | false
Random.getRandomUppercaseLetter(); // [A-Z]
Random.getRandomUppercaseString(n); // [A-Z]{n}
Random.getRandomLowercaseLetter(); // [a-z]
Random.getRandomLowercaseString(n); // [a-z]{n}
Random.getRandomAlphabetString(n); // [a-zA-Z]{n}
Random.getRandomString(n); // [a-zA-Z0-9]{n}
Random.getRandomID(); // ********-**************-************

PriorityQueue

优先队列

import { PriorityQueue } from '@mrtujiawei/utils';

// 数字越小优先级越高
const pq = new PriorityQueue((a, b) => a - b);
pq.isEmpty(); // true
pq.enqueue(5);
pq.isEmpty(); // false
pq.enqueue(3);
pq.enqueue(1);
pq.enqueue(2);
pq.peak(); // 1
pq.dequeue(); // 1
pq.dequeue(); // 2

工具函数

reverseRange

翻转数组中的某一段

import { reverseRange } from '@mrtujiawei/utils';

const arr = [1, 2, 3];
reverseRange(arr, 1, 3); // [1, 3, 2]

swap

交换数组中的两个元素

import { swap } from '@mrtujiawei/utils';

const arr = [1, 2, 3];
swap(arr, 1, 2);  // [1, 3, 2];

sleep

延时一段时间

// 延时 1s
await sleep(1);

debounce

防抖

import { debounce } from '@mrtujiawei/utils';

const listener = debounce((event) => {
  console.log(event);
}, 500);

addEventListener('scroll', listener, {
  passive: true,
});

throttle

节流

import { throttle } from '@mrtujiawei/utils';

const options = {
  // 100ms以内只触发一次
  timeout: 100,

  // 第一次是否直接触发
  // false 100ms以后才会触发
  leading: true,
};
const listener = throttle((data) => {
  console.log(data);
}, { timeout: 100, leading: true });

addEventListener('scroll', listener);

isInteger

是否是整数, 实际值

import { isInteger } from '@mrtujiawei/utils';

isInteger(0);     // true
isInteger('1');   // true
isInteger(1.1);   // false
isInteger('a');   // false

isNaturalNumber

是否是自然数

import { isNaturalNumber } from '@mrtujiawei/utils';

isNaturalNumber('123');   // true
isNaturalNumber(-1);      // false
isNaturalNumber('a');     // false

isPromise

判断是否是promise

import { isPromise } from '@mrtujiawei/utils';
const promise = new Promise(() => {});
const number = 0;

isPromise(promise);   // true
isPromise(number);    // false

retry

重试

import { retry } from '@mrtujiawei/utils';

// 如果回调执行失败,会重复执行直到成功或者执行次数超过10次
const listener = retry((data) => {
  console.log(data);
}, 9);
listener(1);

reentrant

重入

import { reentrant, sleep, } from '@mrtujiawei/utils';

const func = reentrant(async (value) => {
  await sleep(1);
  return `#{value}#`;
});

const run = (data) => {
  const result = await func(data);
  console.log(result);
};

// 无输出
run(100);
run(200);   // #200#

findFirstIndex

二分查找第一个满足条件的下标

import { findFirstIndex, } from '@mrtujiawei/utils';

findFirstIndex([1, 2, 3, 4, 5]);

objectToString

对象转字符串,不是json

import { objectToString } from '@mrtujiawei/utils';

objectToString('asf');    // "asf"
objectToString([1, 2]);   // [1, 2, length: 2]

isSame

判断两个值是否相同,两个值都是 NaN 也是 true

type isSame = (value1: unknown, value2: unknown) => boolean;
import { isSame } from '@mrtujiawei/utils';

isSame(NaN, NaN); // true
isSame(null, null); // true
isSame('123', 123); // false
isSame(undefined, undefined); // true

TODO

  • [ ] 算法
  • [ ] 树形数据结构