feat: md-table weighted random
This commit is contained in:
parent
0261061bcb
commit
66e8564a34
|
|
@ -3,6 +3,7 @@ import { createSignal, For, Show, createEffect, createMemo, createResource } fro
|
||||||
import { marked } from '../markdown';
|
import { marked } from '../markdown';
|
||||||
import { loadCSV, CSV, processVariables, isCSV } from './utils/csv-loader';
|
import { loadCSV, CSV, processVariables, isCSV } from './utils/csv-loader';
|
||||||
import { resolvePath } from './utils/path';
|
import { resolvePath } from './utils/path';
|
||||||
|
import { areAllLabelsNumeric, weightedRandomIndex } from './utils/weighted-random';
|
||||||
|
|
||||||
export interface TableProps {
|
export interface TableProps {
|
||||||
roll?: boolean;
|
roll?: boolean;
|
||||||
|
|
@ -108,8 +109,23 @@ customElement('md-table', { roll: false, remix: false }, (props, { element }) =>
|
||||||
|
|
||||||
// 随机切换 tab
|
// 随机切换 tab
|
||||||
const handleRoll = () => {
|
const handleRoll = () => {
|
||||||
const randomIndex = Math.floor(Math.random() * filteredRows().length);
|
const filtered = filteredRows();
|
||||||
|
if (filtered.length === 0) return;
|
||||||
|
|
||||||
|
// 检查所有 label 是否都是整数/整数范围格式
|
||||||
|
const labels = filtered.map(row => row.label);
|
||||||
|
if (areAllLabelsNumeric(labels)) {
|
||||||
|
// 使用加权随机
|
||||||
|
const randomIndex = weightedRandomIndex(labels);
|
||||||
|
if (randomIndex !== -1) {
|
||||||
setActiveTab(randomIndex);
|
setActiveTab(randomIndex);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 使用简单随机
|
||||||
|
const randomIndex = Math.floor(Math.random() * filtered.length);
|
||||||
|
setActiveTab(randomIndex);
|
||||||
|
}
|
||||||
|
|
||||||
// 滚动到可视区域
|
// 滚动到可视区域
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const activeButton = tabsContainer?.querySelector('.border-blue-600');
|
const activeButton = tabsContainer?.querySelector('.border-blue-600');
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
/**
|
||||||
|
* 解析整数范围字符串
|
||||||
|
* @param label 标签字符串,可能是单个整数(如 "1")或整数范围(如 "1-3")
|
||||||
|
* @returns 如果成功解析返回 [min, max],否则返回 null
|
||||||
|
*/
|
||||||
|
export function parseIntegerRange(label: string): [number, number] | null {
|
||||||
|
const trimmed = label.trim();
|
||||||
|
|
||||||
|
// 尝试匹配整数范围格式 (如 "1-3")
|
||||||
|
const rangeMatch = trimmed.match(/^(\d+)\s*-\s*(\d+)$/);
|
||||||
|
if (rangeMatch) {
|
||||||
|
const min = parseInt(rangeMatch[1]!, 10);
|
||||||
|
const max = parseInt(rangeMatch[2]!, 10);
|
||||||
|
if (min <= max) {
|
||||||
|
return [min, max];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试匹配单个整数格式 (如 "5")
|
||||||
|
const intMatch = trimmed.match(/^(\d+)$/);
|
||||||
|
if (intMatch) {
|
||||||
|
const num = parseInt(intMatch[1]!, 10);
|
||||||
|
return [num, num];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算加权随机的总权重
|
||||||
|
* @param labels 标签数组
|
||||||
|
* @returns 总权重值
|
||||||
|
*/
|
||||||
|
export function calculateTotalWeight(labels: string[]): number {
|
||||||
|
let total = 0;
|
||||||
|
for (const label of labels) {
|
||||||
|
const range = parseIntegerRange(label);
|
||||||
|
if (range) {
|
||||||
|
const [min, max] = range;
|
||||||
|
total += max - min + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据权重随机选择一个索引
|
||||||
|
* @param labels 标签数组
|
||||||
|
* @param totalWeight 总权重(可选,会自行计算)
|
||||||
|
* @returns 选中的索引,如果没有有效权重则返回 -1
|
||||||
|
*/
|
||||||
|
export function weightedRandomIndex(labels: string[], totalWeight?: number): number {
|
||||||
|
const weight = totalWeight ?? calculateTotalWeight(labels);
|
||||||
|
|
||||||
|
if (weight === 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成一个随机权重值 (1 到 totalWeight)
|
||||||
|
const roll = Math.floor(Math.random() * weight) + 1;
|
||||||
|
|
||||||
|
let cumulative = 0;
|
||||||
|
for (let i = 0; i < labels.length; i++) {
|
||||||
|
const range = parseIntegerRange(labels[i]!);
|
||||||
|
if (range) {
|
||||||
|
const [min, max] = range;
|
||||||
|
const labelWeight = max - min + 1;
|
||||||
|
cumulative += labelWeight;
|
||||||
|
|
||||||
|
if (roll <= cumulative) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 理论上不会到这里,但为了安全返回最后一个
|
||||||
|
return labels.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查所有 label 是否都是整数或整数范围格式
|
||||||
|
* @param labels 标签数组
|
||||||
|
* @returns 如果所有 label 都是整数/整数范围格式返回 true
|
||||||
|
*/
|
||||||
|
export function areAllLabelsNumeric(labels: string[]): boolean {
|
||||||
|
if (labels.length === 0) return false;
|
||||||
|
return labels.every(label => parseIntegerRange(label) !== null);
|
||||||
|
}
|
||||||
|
|
@ -57,7 +57,6 @@ export default function markedTable(): MarkedExtension {
|
||||||
const csvData = tableToCSV(headers, rows);
|
const csvData = tableToCSV(headers, rows);
|
||||||
|
|
||||||
// 渲染为 md-table 组件,内联 CSV 数据
|
// 渲染为 md-table 组件,内联 CSV 数据
|
||||||
console.log(csvData)
|
|
||||||
return `<md-table>${csvData}</md-table>\n`;
|
return `<md-table>${csvData}</md-table>\n`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue