feat: md-table weighted random

This commit is contained in:
hypercross 2026-03-21 18:42:11 +08:00
parent 0261061bcb
commit 66e8564a34
3 changed files with 106 additions and 3 deletions

View File

@ -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();
setActiveTab(randomIndex); 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);
}
} 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');

View File

@ -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);
}

View File

@ -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`;
} }
} }