string-overlap-matching-degree

计算字符串重叠/匹配度计算

目前為 2024-08-18 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.cn-greasyfork.org/scripts/501646/1429884/string-overlap-matching-degree.js

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

/**
 * 重叠匹配度
 * @author: zhuangjie
 * @date: 2024-07-23
 */
function overlapMatchingDegreeForObjectArray(keyword = "", objArr = [], fun = (obj) => [], {sort = "desc", onlyHasScope = false, scopeForObjArrContainer} = {}) {
    const scopeForData = objArr.map(item => overlapMatchingDegree(keyword, fun(item), sort));
    // scope与 objArr 同步排序
    sortAndSync(scopeForData, objArr, sort);
    
    if (Array.isArray(scopeForObjArrContainer)) {
        scopeForObjArrContainer.push(...scopeForData);
    }

    return onlyHasScope ? objArr.filter((_, index) => scopeForData[index] !== 0) : objArr;
}
/**
 * 计算匹配度外层封装工具
 * @param {string} keyword - 匹配字符串1
 * @param {Object | Arrayy} topicWeighs - 匹配字符串2与它的权重
 * @returns {number} 匹配度分数
 */
function overlapMatchingDegree(keyword, topicWeighs = {}, sort = "desc") {
    if (Array.isArray(topicWeighs)) {
        const weightMultiplier = sort === "asc" ? 1 : -1;
        topicWeighs = Object.fromEntries(topicWeighs.map((topic, index) => [topic, (index + 1) * weightMultiplier]));
    }
    
    return Object.keys(topicWeighs).reduce((totalScore, topic) => {
        const currentScore = topicWeighs[topic];
        const overlapLengthBlocksMap = findOverlapBlocks(keyword, topic);
        return totalScore + Object.entries(overlapLengthBlocksMap).reduce((sum, [length, blocks]) => {
            return sum + blocks.length * Math.pow(currentScore, Number(length));
        }, 0);
    }, 0);
}
/**
 * 查找重叠匹配块(入口函数)
 * @param {*} str1 
 * @param {*} str2 
 * @returns 返回重叠块 如:{"2": ["好用","推荐"],"3": ["好用推荐"]}
 * 算法核心思想:
 * -----------------------------------------------------
 * sumatrapdf*          | sumatrapdf*      | sumatrapdf*
 *           pdf-       |          pdf-    |         pdf-
 * ------------------------------------------------------
 */
function findOverlapBlocks(str1 = "", str2 = "") {
    const alignmentHub = {};
    const str1Len = str1.length;
    const str2Len = str2.length;
    const minLen = Math.min(str1Len, str2Len);

    for (let offset = 1 - str2Len; offset < str1Len; offset++) {
        const start = Math.max(0, offset);
        const end = Math.min(str1Len, str2Len + offset);
        const overlapStr1 = str1.slice(start, end);
        const overlapStr2 = str2.slice(start - offset, end - offset);

        const alignmentContent = alignment(overlapStr1, overlapStr2);
        for (const [len, blocks] of Object.entries(alignmentContent)) {
            alignmentHub[len] = alignmentHub[len] ? [...new Set([...alignmentHub[len], ...blocks])] : blocks;
        }
    }
    return alignmentHub;
}
// 对齐
function alignment(str1 = "", str2 = "") {
    const overlappingBlocks = {};
    let currentBlock = "";

    for (let i = str1.length - 1; i >= 0; i--) {
        if (str1[i] === str2[i]) {
            currentBlock = str1[i] + currentBlock;
        } else if (currentBlock.length > 0) {
            const len = currentBlock.length;
            overlappingBlocks[len] = overlappingBlocks[len] || [];
            if (!overlappingBlocks[len].includes(currentBlock)) {
                overlappingBlocks[len].push(currentBlock);
            }
            currentBlock = "";
        }
    }
    if (currentBlock.length > 0) {
        const len = currentBlock.length;
        overlappingBlocks[len] = overlappingBlocks[len] || [];
        if (!overlappingBlocks[len].includes(currentBlock)) {
            overlappingBlocks[len].push(currentBlock);
        }
    }

    return overlappingBlocks;
}
// 【同步排序算法】
function sortAndSync(arr1, arr2, order = 'desc') {
    const compare = order === 'asc' ? (a, b) => a - b : (a, b) => b - a;
    arr1.map((v, i) => [v, arr2[i]])
        .sort((a, b) => compare(a[0], b[0]))
        .forEach(([v, o], i) => {
            arr1[i] = v;
            arr2[i] = o;
        });
}


// 【算法测试1】
//  console.log("-- 算法测试开始 --")
//  console.log(findOverlapBlocks("[推荐]sumatrapdf非常好用","pdf 推荐"))
//  console.log("-- 算法测试结束 --")

// 【算法测试2】
// console.log("匹配度:", overlapMatchingDegree("好用的pdf工具", { "sumatrapdf": 10, "小而好用的pdf阅读器": 8, "https://www.sumatrapdfreader.org/downloadafter": 3 }));