@mrtujiawei/utils
v2.5.52
Published
工具:常用数据类型、算法
Downloads
2,383
Readme
@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();
入栈
- 支持一次
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
- [ ] 算法
- [ ] 树形数据结构