JSON表格近似字符串搜索功能实现 匹配相近键返回对应条目数据
模糊匹配JSON条目搜索功能实现
需求说明
- 入参:用户输入的搜索字符串
- 数据源:固定格式的JSON条目表,键为条目名称,值为条目属性
- 出参:返回和输入字符串相似度最高的键对应的完整条目,相似度过低时返回空
数据源
{ "Death": { "Value": 190000, "Demand": 4, "Trend": 1, "Tier": "S" }, "Legendary Borul (Alternative)": { "Value": 125000, "Demand": 2, "Trend": 1, "Tier": "S" }, "Club Beast": { "Value": 50000, "Demand": 6, "Trend": 1, "Tier": "S" }, "Dark Wing": { "Value": 19800, "Demand": 6, "Trend": 1, "Tier": "S" }, "Zaruto (GRR III)": { "Value": 18800, "Demand": 6, "Trend": 1, "Tier": "S" }, "First Wood Bender": { "Value": 17000, "Demand": 8, "Trend": 1, "Tier": "S" }, "Old Will": { "Value": 14500, "Demand": 5, "Trend": 1, "Tier": "S" }, "Martial Artist": { "Value": 12000, "Demand": 3, "Trend": 6, "Tier": "S" }, "Expert Sorcerer": { "Value": 11250, "Demand": 5, "Trend": 1, "Tier": "S" }, "Zaruto (GRR II)": { "Value": 9600, "Demand": 8, "Trend": 7, "Tier": "S" }, "Sandwhich Leader / Mecha Frieza": { "Value": 1050000, "Demand": 0, "Trend": 4, "Tier": "S" }, "First Wood Bender (Sage)": { "Value": "N/A", "Demand": 0, "Trend": 4, "Tier": "S" } }
实现方案
采用编辑距离+规则加权的轻量实现,不需要引入第三方依赖,对短文本搜索场景准确率足够,匹配时统一转小写实现大小写不敏感。
完整代码
// 先把数据源存为常量 const itemData = { "Death": { "Value": 190000, "Demand": 4, "Trend": 1, "Tier": "S" }, "Legendary Borul (Alternative)": { "Value": 125000, "Demand": 2, "Trend": 1, "Tier": "S" }, "Club Beast": { "Value": 50000, "Demand": 6, "Trend": 1, "Tier": "S" }, "Dark Wing": { "Value": 19800, "Demand": 6, "Trend": 1, "Tier": "S" }, "Zaruto (GRR III)": { "Value": 18800, "Demand": 6, "Trend": 1, "Tier": "S" }, "First Wood Bender": { "Value": 17000, "Demand": 8, "Trend": 1, "Tier": "S" }, "Old Will": { "Value": 14500, "Demand": 5, "Trend": 1, "Tier": "S" }, "Martial Artist": { "Value": 12000, "Demand": 3, "Trend": 6, "Tier": "S" }, "Expert Sorcerer": { "Value": 11250, "Demand": 5, "Trend": 1, "Tier": "S" }, "Zaruto (GRR II)": { "Value": 9600, "Demand": 8, "Trend": 7, "Tier": "S" }, "Sandwhich Leader / Mecha Frieza": { "Value": 1050000, "Demand": 0, "Trend": 4, "Tier": "S" }, "First Wood Bender (Sage)": { "Value": "N/A", "Demand": 0, "Trend": 4, "Tier": "S" } } // 计算两个字符串的Levenshtein编辑距离 function getEditDistance(str1, str2) { const len1 = str1.length const len2 = str2.length const dp = Array.from({ length: len1 + 1 }, () => new Array(len2 + 1).fill(0)) for (let i = 0; i <= len1; i++) dp[i][0] = i for (let j = 0; j <= len2; j++) dp[0][j] = j for (let i = 1; i <= len1; i++) { for (let j = 1; j <= len2; j++) { const cost = str1[i-1] === str2[j-1] ? 0 : 1 dp[i][j] = Math.min( dp[i-1][j] + 1, dp[i][j-1] + 1, dp[i-1][j-1] + cost ) } } return dp[len1][len2] } function search(input) { const trimmedInput = input.trim() if (!trimmedInput) return null const searchLower = trimmedInput.toLowerCase() let bestMatchKey = null let maxScore = -Infinity for (const itemName of Object.keys(itemData)) { const nameLower = itemName.toLowerCase() // 取键名和输入等长的前缀计算编辑距离,归一化得到基础相似度分 const namePrefix = nameLower.slice(0, searchLower.length) const distance = getEditDistance(searchLower, namePrefix) let score = 1 - distance / searchLower.length // 前缀完全匹配加权重,符合用户输入习惯 if (nameLower.startsWith(searchLower)) score += 0.5 // 输入是键名子串加权重 if (nameLower.includes(searchLower)) score += 0.3 if (score > maxScore) { maxScore = score bestMatchKey = itemName } } // 低于相似度阈值直接返回空,避免无关结果 return maxScore > 0.3 ? { [bestMatchKey]: itemData[bestMatchKey] } : null }
调用示例
search("legen") // 返回结果 // { "Legendary Borul (Alternative)": { "Value": 125000, "Demand": 2, "Trend": 1, "Tier": "S" } }
可调参数说明
- 权重值:前缀匹配、子串匹配的加分数值可根据实际测试效果调整,越看重对应规则数值设越高
- 相似度阈值:当前设为0.3,阈值越高匹配越严格,越低模糊匹配范围越大
- 若需要支持中文搜索,可在相似度计算前增加拼音转换、中文分词逻辑即可
内容的提问来源于stack exchange,提问作者pygon
相关产品推荐
相关产品推荐

