@mxssfd/random-picker
v5.0.1-beta.1
Published
从数组中随机挑选出n个选项的js工具类库
Downloads
10
Maintainers
Readme
@mxssfd/random-picker
从数组中随机挑选出 n 个选项的 js 工具类库。
安装方法
npm install @mxssfd/random-picker
使用方法
pick
/ take
随机选取单个/多个选项
pick
从选项池中随机选中一个
import { RandomPicker } from '@mxssfd/random-picker';
const picker = new RandomPicker([1, 2]);
// pick 1个
const v1 = picker.pick(); // 随机从1,2中选中一个,每个选项选中的几率是50%
// pick多个
const v2 = picker.pick(2); // 随机选中2个,可能会出现重复的
const v3 = picker.pick(10); // 随机选中10个,会一直在1,2之间选取
take
从选项池中随机拿走一个
import { RandomPicker } from '@mxssfd/random-picker';
const picker = new RandomPicker([1, 2]);
// take 1个
const v1 = picker.take(); // 随机从1,2中选中一个,每个选项选中的几率是50%
// take多个
const v2 = picker.take(2); // 随机选中2个,不会出现重复的
const v3 = picker.take(10); // 随机选中10个,但是最多只会返回两个,多了的会去掉
const v4 = picker.take(); // null。因为在上面已经取走了所有选项
const v5 = picker.pick(); // null。pick和take共享一个选项池,take拿走了以后,pick也会被影响。
// 如果需要再次pick或者take的话需要reset
picker.reset();
// 此时又可以pick/take了
const v6 = picker.pick(); // 1 | 2
const v7 = picker.take(); //
两者区别
| | take
| pick
|
| ------------------------------ | ------ | ------ |
| 可以选取一个 | 是 | 是 |
| 可以选取多个 | 是 | 是 |
| 选取多个时是否可能会重复选取 | 否 | 是 |
| 选取多个时是否可以超出可选数量 | 否 | 是 |
| 影响 | pick
| 无 |
| 是否会从选项池中移除 | 是 | 否 |
权重(weights
)
import { RandomPicker } from '@mxssfd/random-picker';
// 默认权重为1
const picker = new RandomPicker([1, 2]);
// 约等于
const picker2 = new RandomPicker([
[1, 1], // [选项, 权重]
[2, 1], // [选项, 权重]
]);
选项有权重的概念
注:权重(weights
)不完全等于选中几率。
规则:
- 如果总共有 3 个选项,且权重都为 1 的选项,那么每个选中几率是 33%。
- 如果总共只有 1 个选项,且唯一选项权重为 1,那么选中几率是 100%。
- 如果总共有 2 个选项,第一个选项权重为 20,第二个权重为 80,那么选中几率依次为 20%、80%。
- 添加权重为 0 或者为负数的选项会抛出异常
- 如果有三个权重为 1 的选项,take 一个选项会刷新选项池,剩余两个的选中几率各为 50%,再次 take 一个,则唯一的一个选中几率是 100%
- 每额外添加、删除、take 一个选项,选项池内选项的选中几率都会重新计算
option
/options
添加选项
import { RandomPicker } from '@mxssfd/random-picker';
const picker = new RandomPicker([1, 2]);
// 添加单个选项
picker.option(3); // 添加权重为1的选项3
picker.option(4, 2); // 添加选项3,并且设置权重为2
// 添加动态权重的选项33,该权重回调函数会在计算所有静态权重后调用,此时如果只有一个动态权重的话约等于50%
picker.option(33, (weights) => weights);
// 添加多个选项
picker.options([5, 6]); // 添加权重为1的选项5和6
picker.options([
[7, 2],
[8, 2],
]); // 添加权重为2的选项7和8
picker.options([[9], [10, (weights) => weight]]); // 添加权重为1的选项9和动态权限的选项10,此时的选项10和选项33选中几率为33.3333%,其他按权限计算各个选项选中几率
注意:如果添加选项前有用过 take
的话,调用 option/options
会自动刷新选项池,已经 take
过的选项会重新进入选项池
rateOf
获取选项的选中几率
import { RandomPicker } from '@mxssfd/random-picker';
const picker = new RandomPicker([1]);
console.log(picker.rateOf(1)); // 选中几率 100
picker.option(2);
console.log(picker.rateOf(1)); // 选中几率 50
console.log(picker.rateOf(2)); // 选中几率 50
picker.option(3, 2);
console.log(picker.rateOf(1)); // 选中几率 25
console.log(picker.rateOf(2)); // 选中几率 25
console.log(picker.rateOf(3)); // 选中几率 50
picker.option(4, (weightTotal) => weightTotal);
console.log(picker.rateOf(1)); // 选中几率 12.5
console.log(picker.rateOf(2)); // 选中几率 12.5
console.log(picker.rateOf(3)); // 选中几率 25
console.log(picker.rateOf(4)); // 选中几率 50
picker.option(5, (weightTotal) => weightTotal);
console.log(picker.rateOf(1)); // 选中几率 8.3333
console.log(picker.rateOf(2)); // 选中几率 8.3333
console.log(picker.rateOf(3)); // 选中几率 16.6667
console.log(picker.rateOf(4)); // 选中几率 33.3333
console.log(picker.rateOf(5)); // 选中几率 33.3334
remove
移除选项
import { RandomPicker } from '@mxssfd/random-picker';
const picker = new RandomPicker([1, 2, 3]);
picker.remove(1); // 此时会把选项1移除
注意:如果移除的选项不存在于 seed
或者未备份的话,将不能恢复
reset
/resetWithSeed
重置选项池
使用 reset
重置选项池
import { RandomPicker } from '@mxssfd/random-picker';
const seed = [1, 2, 3];
const picker = new RandomPicker(seed);
// 当take 3次后,选项池就为空了,此时再take或pick都会返回null
picker.take(seed.length); // [number,number,number]
picker.take(); // invalid null
// 重置选项池
picker.reset();
picker.take(); // 1 | 2 | 3
// 注意:不会重置remove过的选项
picker.reset();
picker.remove(1); // 移除选项1
picker.take(seed.length); // [number,number]
picker.reset();
picker.take(seed.length); // 重置回去也是[number,number]
// option
picker.reset();
picker.option(5);
picker.take(seed.length); // [number,number,number]
// 使用reset可以重置option/options添加的选项
picker.reset();
picker.take(seed.length); // [number,number,number]
//
使用 resetWithSeed
重置选项池
import { RandomPicker } from '@mxssfd/random-picker';
const seed = [1, 2, 3];
const picker = new RandomPicker(seed);
// 当take 3次后,选项池就为空了,此时再take或pick都会返回null
picker.take(seed.length); // [number,number,number]
picker.take(); // invalid null
// 重置选项池
picker.resetWithSeed();
picker.take(); // 1 | 2 | 3
// 注意:会重置remove过的选项
picker.resetWithSeed();
picker.remove(1); // 移除选项1
picker.take(seed.length); // [number,number]
picker.resetWithSeed();
picker.take(seed.length); // [number,number,number]
// option
picker.resetWithSeed();
picker.option(5);
picker.take(seed.length); // [1 | 2 | 3 | 5, ...]
// 使用resetWithSeed会丢弃option/options添加的选项
picker.resetWithSeed();
picker.take(seed.length); // [1 | 2 | 3 , ...]
两者区别
| | reset
| resetWithSeed
|
| -------------------------------- | ------- | ------------------------------------------- |
| 恢复被take
的选项 | 是 | 是 |
| 重置option
/options
添加的选项 | 是 | 否,会丢弃选项 |
| 恢复被remove
的选项 | 否 | 如果是存在于seed
的选项被 remove
会被恢复 |
export
/ exportPool
导出选项
export
导出所有选项
import { RandomPicker, Seed } from '@mxssfd/random-picker';
const seed: Seed<number> = [1, 2, [3, (weights: number) => weights]];
const picker = new RandomPicker(seed);
// export 导出所有选项
console.log(picker.export()); // [[1, 1], [2, 1], [3, (weights: number) => weights]]
// take过的的选项也会导出
picker.take(3);
console.log(picker.export()); // [[1, 1], [2, 1], [3, (weights: number) => weights]]
// 不会导出remove过的选项
picker.remove(3);
console.log(picker.export()); // [[1, 1], [2, 1]]
export
导出剩余选项
import { RandomPicker, Seed } from '@mxssfd/random-picker';
const seed: Seed<number> = [1, 2, [3, (weights: number) => weights]];
const picker = new RandomPicker(seed);
// exportPool 导出剩余选项
console.log(picker.exportPool()); // [[1, 1], [2, 1], [3, (weights: number) => weights]]
// take过的选项不会导出
picker.take(3);
console.log(picker.exportPool()); // []
picker.reset();
// 不会导出remove过的选项
picker.remove(3);
console.log(picker.exportPool()); // [[1, 1], [2, 1]]
两者区别
| | export
(导出未 remove
的所有选项) | exportPool
(剩余可供选择的所有选项) |
| ----------------------- | ------------------------------------ | ------------------------------------ |
| 能导出take
过的选项 | 是 | 否 |
| 能导出remove
过的选项 | 是 | 否 |
poolOptions
查看剩余选项
import { RandomPicker } from '@mxssfd/random-picker';
const picker = new RandomPicker([1, 2, [3, 98]]);
picker.take(); // 3 98%几率会出现3
console.log(picker.poolOptions); // [1|2, 1|2]
picker.take();
console.log(picker.poolOptions); // [1|2]
picker.take();
console.log(picker.poolOptions); // []
len
/ poolLen
查看选项数量
import { RandomPicker } from '@mxssfd/random-picker';
const picker = new RandomPicker([1, 2, 3]);
// 初始都有3个
console.log(picker.len, picker.poolLen); // 3, 3
picker.take(2); // 拿走两个
console.log(picker.len, picker.poolLen); // 3, 1
picker.remove(3); // 移除3
console.log(picker.len, picker.poolLen); // 2, 2
picker.take(2); // 拿走两个
console.log(picker.len, picker.poolLen); // 2, 0
添加命令行脚本工具
如果你有选择困难症,那么全局安装此工具npm
包可以帮助你选择,类似掷骰子
添加命令行脚本步骤
1.在项目根目录下新建bin
目录,然后在bin
目录下新建index.js
文件
index.js
#!/usr/bin/env node
const { RandomPicker } = require('../dist/random-picker.cjs');
/**
* @returns {
* {
* help?:boolean;h?:boolean;
* pick?:boolean|number;p?:boolean|number;
* take?:boolean|number;t?:boolean;
* options:(number | string| [string|number,number])[]
* }
* }
*/
function getOptions() {
const args = process.argv.slice(2);
const keyReg = /^-+/;
return args.reduce((result, cur, index) => {
const next = args[index + 1];
if (keyReg.test(cur)) {
const key = cur.replace(keyReg, '');
// --take 2 获取take后面的2
result[key] = /^\d+$/.test(next) ? Number(next) ?? true : true;
} else if (/^\[.+]$/.test(cur)) {
// 随机选项
try {
result.options = JSON.parse(cur);
} catch (e) {
throw new TypeError('请检查选项数组是否符合JSON格式');
}
}
return result;
}, {});
}
function showHelp() {
const helpContent = `
--help/-h 帮助
--take/-t 使用take获取随机选项 获取2个随机选项--take 2 '[1,2]'
--pick/-p 使用pick获取随机选项 同take
`.trim();
console.log(helpContent);
}
/**
*
* @param {(string|number)[]} options
* @param {boolean|number} take
* @param {boolean|number} pick
*/
function runRP(options, take, pick) {
const rm = new RandomPicker(options);
const result = take
? rm.take(typeof take === 'number' ? take : 1)
: rm.pick(typeof pick === 'number' ? pick : 1);
console.log(result);
}
function setup() {
const options = getOptions();
if (options.h || options.help) {
showHelp();
return;
}
if (!Array.isArray(options.options)) {
throw new Error('缺少选项');
}
runRP(options.options, options.take ?? options.t, options.pick ?? options.p);
}
setup();
需要注意的是文件头部的#!/usr/bin/env node
不能缺少,否则命令行工具是运行不了的。
2.然后修改package.json
加上bin
属性
package.json
+ "bin": {
+ "random-picker": "./bin/index.js"
+ },
3.调试
在控制台打开项目目录,然后输入命令npm link
,这样random-picker
包就相当于全局安装好了。
比如说我明天不知道学习好还是躺平好,那么我就可以使用它来帮助我选择,命令行输入如下:
random-picker '[["睡觉", 99], "学习"]'
那么 99%的几率是睡觉 😂
后记
以上是实现步骤,你也可以直接安装npm i @mxssfd/random-picker -g
使用
代码测试覆盖率
--------------------------|---------|----------|---------|---------|----------------------------------------------------------------------------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------------------|---------|----------|---------|---------|----------------------------------------------------------------------------------------
random-picker/src | 100 | 100 | 100 | 100 |
OptionsPool.ts | 100 | 100 | 100 | 100 |
OptionsStore.ts | 100 | 100 | 100 | 100 |
RandomPicker.ts | 100 | 100 | 100 | 100 |
--------------------------|---------|----------|---------|---------|----------------------------------------------------------------------------------------
测试覆盖率达到 100%请放心食用