@mrtujiawei/utils
安装
使用 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();
入栈
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();
入队
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);
链表尾部插入
list.insertLast(value);
移除头结点
- 链表为空时
removeFirst
会抛出LinkedListEmptyError
list.removeFirst();
移除尾节点
- 链表为空时
removeLast
会抛出LinkedListEmptyError
list.removeLast();
移除指定下标的节点
- 下标范围不对时会抛出
InvalidIndexError
list.removeAt(index);
复制链表
list.clone(oldList);
遍历链表
list.forEach((value, index, context) => {
});
过滤出符合条件的新链表
- Boolean(returnValue) 为
true
保留, false
不保留
list.filter((value, index, context) => {
});
查找第一个满足条件的值
- Boolean(returValue) 为
true
时,返回对应的value
list.find((value, index, context) => {
});
获取第一个节点值
- 链表为空时
getFirst
会抛出LinkedListEmptyError
list.getFirst();
转化成数组
list.toArray();
从数组创建链表
let list = LinkedList.fromArray(arr);
for..of
遍历
for (const value of list) {
}
异常
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) => {
});
查找第一个满足要求的元素下标
const index = list.findIndex((value, index, context) => {
});
forEach遍历
list.forEach((value, index, context) => {
});
获取指定下标的值
- 0 <= index && index < list.size()
- 不在范围内的下标会抛出
InvalidIndexError
list.get(index);
获取链表中的第一个值
const value = list.getFirst();
获取链表中的最后一个值
const value = list.getLast();
是否包含某个值
list.includes();
第一个等于指定值的下标
const index = list.indexOf(value);
判断链表是否为空
list.isEmpty();
根据参数合并成字符串
const result = list.join(',');
最后一个满足条件的元素下标
const index = lastIndexOf(value);
映射成一个新的链表
const newList = list.map(value => {
return newValue;
});
移除最后一个元素
const value = list.pop();
向尾部添加
list.push(value);
元素汇总
list.reduce((previousValue, currentValue, index, context) => {
return nextValue;
});
元素反向汇总
list.reduceRight((previousValue, currentValue, index, context) => {
return nextValue;
});
移除指定下标的元素
- 0 <= index < list.size()
- 下标不合法时会抛出
InvalidIndexError
const value = list.remove(index);
反转链表
list.reverse();
设置指定位置的值
- 0 <= index < list.size()
- 下标不合法时会抛出
InvalidIndexError
list.set(index, value);
移除第一个元素
list.shift();
获取链表长度
list.size();
复制链表中的一段
- 浅拷贝
- [startIndex, endIndex)
list.slice(startIndex, endIndex);
查找是否有满足条件的值
list.some((value, index, context) => {
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) => {
return true || false;
});
反向遍历
list.forEachReverse((value, index, context) => {
});
反向查找
const value = list.findReverse((value, index, context) => {
return true || false;
});
for..of 遍历
for (const value of list) {
}
异常
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) => {
});
for..of 遍历
for (const value of list) {
}
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);
bTree.inorderTraversal((value) => {
console.log(value);
});
bTree.getMin();
bTree.getMax();
bTree.toArray();
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
移除节点
tree.remove(key);
判断是否存在key
tree.has(key);
获取key
对应的value
const value = tree.getValue(key);
获取节点个数
const size = tree.getSize();
清空树
tree.clear();
异常
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) => {
});
转化成数组
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(), ':');
DateTimeTool.dateFormat(new Date(), '-');
DateTimeTool.dateTimeFormat(new Date());
DateTimeTool.getNthDayBefore(2);
DateTimeTool.getNthHourBefore(2);
DateTimeTool.getNthMonthBefore(1);
DateTimeTool.toDayBegin(new Date());
DateTimeTool.toDayEnd(new Date());
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);
DateTimeTool.timestampToTime(123);
DateTimeTool.timestampToDate(123);
DateTimeTool.timestampToDateTime(123);
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;
}
});
countDown.start({
start: 60,
end: 0,
timeout: 1,
});
countDown.unsubscribe(callback);
countDown.clear();
分页
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);
events.off('start', listener);
events.emit('start');
Random
随机
import { Random } from '@mrtujiawei/utils';
Random.getRandomNumber(100, 1000);
Random.getRandomBoolean();
Random.getRandomUppercaseLetter();
Random.getRandomUppercaseString(n);
Random.getRandomLowercaseLetter();
Random.getRandomLowercaseString(n);
Random.getRandomAlphabetString(n);
Random.getRandomString(n);
Random.getRandomID();
PriorityQueue
优先队列
import { PriorityQueue } from '@mrtujiawei/utils';
const pq = new PriorityQueue((a, b) => a - b);
pq.isEmpty();
pq.enqueue(5);
pq.isEmpty();
pq.enqueue(3);
pq.enqueue(1);
pq.enqueue(2);
pq.peak();
pq.dequeue();
pq.dequeue();
工具函数
reverseRange
翻转数组中的某一段
import { reverseRange } from '@mrtujiawei/utils';
const arr = [1, 2, 3];
reverseRange(arr, 1, 3);
swap
交换数组中的两个元素
import { swap } from '@mrtujiawei/utils';
const arr = [1, 2, 3];
swap(arr, 1, 2);
sleep
延时一段时间
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 = {
timeout: 100,
leading: true,
};
const listener = throttle((data) => {
console.log(data);
}, { timeout: 100, leading: true });
addEventListener('scroll', listener);
isInteger
是否是整数, 实际值
import { isInteger } from '@mrtujiawei/utils';
isInteger(0);
isInteger('1');
isInteger(1.1);
isInteger('a');
isNaturalNumber
是否是自然数
import { isNaturalNumber } from '@mrtujiawei/utils';
isNaturalNumber('123');
isNaturalNumber(-1);
isNaturalNumber('a');
isPromise
判断是否是promise
import { isPromise } from '@mrtujiawei/utils';
const promise = new Promise(() => {});
const number = 0;
isPromise(promise);
isPromise(number);
retry
重试
import { retry } from '@mrtujiawei/utils';
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);
findFirstIndex
二分查找第一个满足条件的下标
import { findFirstIndex, } from '@mrtujiawei/utils';
findFirstIndex([1, 2, 3, 4, 5]);
objectToString
对象转字符串,不是json
import { objectToString } from '@mrtujiawei/utils';
objectToString('asf');
objectToString([1, 2]);
isSame
判断两个值是否相同,两个值都是 NaN 也是 true
type isSame = (value1: unknown, value2: unknown) => boolean;
import { isSame } from '@mrtujiawei/utils';
isSame(NaN, NaN);
isSame(null, null);
isSame('123', 123);
isSame(undefined, undefined);
TODO