@leeoniya/ufuzzy
Advanced tools
Comparing version 1.0.0 to 1.0.1
@@ -7,3 +7,3 @@ /** | ||
* A tiny, efficient fuzzy matcher that doesn't suck | ||
* https://github.com/leeoniya/uFuzzy (v1.0.0) | ||
* https://github.com/leeoniya/uFuzzy (v1.0.1) | ||
*/ | ||
@@ -179,3 +179,3 @@ | ||
const prepQuery = (needle, capt = 0) => { | ||
const prepQuery = (needle, capt = 0, exactParts, interOR = false) => { | ||
// split on punct, whitespace, num-alpha, and upper-lower boundaries | ||
@@ -189,3 +189,3 @@ let parts = split(needle); | ||
if (intraMode == 1) { | ||
reTpl = parts.map(p => { | ||
reTpl = parts.map((p, pi) => { | ||
let { | ||
@@ -199,3 +199,3 @@ intraSlice, | ||
if (intraIns + intraSub + intraTrn + intraDel == 0) | ||
if (intraIns + intraSub + intraTrn + intraDel == 0 || exactParts?.[pi] == 1) | ||
return p; | ||
@@ -246,3 +246,7 @@ | ||
return '(?:' + p + '|' + variants.join('|') + ')'; | ||
let reTpl = '(?:' + p + '|' + variants.join('|') + ')'; | ||
// console.log(reTpl); | ||
return reTpl; | ||
}); | ||
@@ -260,3 +264,3 @@ } | ||
reTpl = parts.map(p => p.split('').map((c, i, chars) => { | ||
reTpl = parts.map((p, pi) => exactParts?.[pi] == 1 ? p : p.split('').map((c, i, chars) => { | ||
// neg lookahead to prefer matching 'Test' instead of 'tTest' in ManifestTest or fittest | ||
@@ -282,17 +286,17 @@ // but skip when search term contains leading repetition (aardvark, aaa) | ||
if (capt > 0) { | ||
// sadly, we also have to capture the inter-term junk via parenth-wrapping .*? | ||
// to accum other capture groups' indices for \b boosting during scoring | ||
reTpl = '(' + reTpl.join(')(' + interCharsTpl + ')(') + ')'; | ||
if (interOR) { | ||
// this is basically for doing .matchAll() occurence counting and highlihting without needing permuted ooo needles | ||
reTpl = preTpl + '(' + reTpl.join(')' + sufTpl + '|' + preTpl + '(') + ')' + sufTpl; | ||
} | ||
else { | ||
// sadly, we also have to capture the inter-term junk via parenth-wrapping .*? | ||
// to accum other capture groups' indices for \b boosting during scoring | ||
reTpl = '(' + reTpl.join(')(' + interCharsTpl + ')(') + ')'; | ||
reTpl = '(.?' + preTpl + ')' + reTpl + '(' + sufTpl + '.*)'; // nit: trailing capture here assumes interIns = Inf | ||
} | ||
} | ||
else | ||
else { | ||
reTpl = reTpl.join(interCharsTpl); | ||
if (capt > 0) { | ||
if (interLft == 2) | ||
reTpl = '(' + preTpl + ')' + reTpl + '(' + sufTpl + ')'; | ||
else | ||
reTpl = '(.?)' + reTpl + '(.?)'; | ||
reTpl = preTpl + reTpl + sufTpl; | ||
} | ||
else | ||
reTpl = preTpl + reTpl + sufTpl; | ||
@@ -331,4 +335,4 @@ // console.log(reTpl); | ||
let [query, parts] = prepQuery(needle, 1); | ||
let [queryR] = prepQuery(needle, 2); | ||
let partsLen = parts.length; | ||
let [queryR] = prepQuery(needle, 2); | ||
@@ -395,2 +399,5 @@ let len = idxs.length; | ||
// will be populated if we need to re-generate a query with some exact terms | ||
let useExactParts = null; | ||
for (let j = 0, k = 2; j < partsLen; j++, k+=2) { | ||
@@ -403,2 +410,49 @@ let group = m[k].toLowerCase(); | ||
// this won't handle the case when an exact match exists across the boundary of the current group and the next junk | ||
// e.g. blob,ob when searching for 'bob' but finding the earlier `blob` (with extra insertion) | ||
if (!fullMatch && groupLen >= termLen && m[k+1].length >= termLen) { | ||
// probe for exact match in inter junk | ||
let idxOf = m[k+1].toLowerCase().indexOf(term); | ||
if (idxOf > -1) { | ||
// so here we have three options: | ||
// 1. mutate the current match to be better. | ||
// this doesn't help the range regex below, which would need different adjustement logic, since | ||
// its capture groups are more granular | ||
// 2. re-generate a new regex with some terms flagged as exact rather than a group of alterations | ||
// this is more expensive since we need to re-process, but will be seamless for range query, | ||
// but would require popping out of this loop and throwing away exisitng info counters | ||
// 3. do a combo of the above | ||
// we can stay in this loop, but also gen a more explicit ranges regex that will result in the | ||
// better match | ||
// this whole section probably risks violating interIns < Inf, since better terms might be too far away | ||
// we could test for this here to choose not to re-process, but it's pretty unusual to reduce interIns | ||
// (usually to only accept tighter matches). the match improvement here is likely better. | ||
// debugger; | ||
// shift the current group into the prior junk, adjust idxAcc & start | ||
let prepend = m[k] + m[k+1].slice(0, idxOf); | ||
m[k-1] += prepend; | ||
idxAcc += prepend.length; | ||
if (j == 0) | ||
start = idxAcc; | ||
// update current group and next junk | ||
m[k] = m[k+1].slice(idxOf, idxOf + termLen); | ||
m[k+1] = m[k+1].slice(idxOf + termLen); | ||
group = m[k].toLowerCase(); | ||
groupLen = termLen; | ||
fullMatch = true; | ||
if (useExactParts == null) | ||
useExactParts = Array(partsLen).fill(0); | ||
useExactParts[j] = 1; | ||
} | ||
} | ||
if (mayDiscard || fullMatch) { | ||
@@ -479,2 +533,4 @@ // does group's left and/or right land on \b | ||
if (!disc) { | ||
let idxQueryR = useExactParts != null ? prepQuery(needle, 2, useExactParts)[0] : queryR; | ||
info.idx[ii] = idxs[i]; | ||
@@ -494,3 +550,3 @@ info.interLft2[ii] = lft2; | ||
// ranges | ||
let m = mhstr.match(queryR); | ||
let m = mhstr.match(idxQueryR); | ||
let ranges = info.ranges[ii] = []; | ||
@@ -580,2 +636,5 @@ | ||
// interOR | ||
// console.log(prepQuery(needle, 1, null, true)); | ||
// non-ooo or ooo w/single term | ||
@@ -582,0 +641,0 @@ if (needles == null) { |
@@ -7,3 +7,3 @@ /** | ||
* A tiny, efficient fuzzy matcher that doesn't suck | ||
* https://github.com/leeoniya/uFuzzy (v1.0.0) | ||
* https://github.com/leeoniya/uFuzzy (v1.0.1) | ||
*/ | ||
@@ -177,3 +177,3 @@ | ||
const prepQuery = (needle, capt = 0) => { | ||
const prepQuery = (needle, capt = 0, exactParts, interOR = false) => { | ||
// split on punct, whitespace, num-alpha, and upper-lower boundaries | ||
@@ -187,3 +187,3 @@ let parts = split(needle); | ||
if (intraMode == 1) { | ||
reTpl = parts.map(p => { | ||
reTpl = parts.map((p, pi) => { | ||
let { | ||
@@ -197,3 +197,3 @@ intraSlice, | ||
if (intraIns + intraSub + intraTrn + intraDel == 0) | ||
if (intraIns + intraSub + intraTrn + intraDel == 0 || exactParts?.[pi] == 1) | ||
return p; | ||
@@ -244,3 +244,7 @@ | ||
return '(?:' + p + '|' + variants.join('|') + ')'; | ||
let reTpl = '(?:' + p + '|' + variants.join('|') + ')'; | ||
// console.log(reTpl); | ||
return reTpl; | ||
}); | ||
@@ -258,3 +262,3 @@ } | ||
reTpl = parts.map(p => p.split('').map((c, i, chars) => { | ||
reTpl = parts.map((p, pi) => exactParts?.[pi] == 1 ? p : p.split('').map((c, i, chars) => { | ||
// neg lookahead to prefer matching 'Test' instead of 'tTest' in ManifestTest or fittest | ||
@@ -280,17 +284,17 @@ // but skip when search term contains leading repetition (aardvark, aaa) | ||
if (capt > 0) { | ||
// sadly, we also have to capture the inter-term junk via parenth-wrapping .*? | ||
// to accum other capture groups' indices for \b boosting during scoring | ||
reTpl = '(' + reTpl.join(')(' + interCharsTpl + ')(') + ')'; | ||
if (interOR) { | ||
// this is basically for doing .matchAll() occurence counting and highlihting without needing permuted ooo needles | ||
reTpl = preTpl + '(' + reTpl.join(')' + sufTpl + '|' + preTpl + '(') + ')' + sufTpl; | ||
} | ||
else { | ||
// sadly, we also have to capture the inter-term junk via parenth-wrapping .*? | ||
// to accum other capture groups' indices for \b boosting during scoring | ||
reTpl = '(' + reTpl.join(')(' + interCharsTpl + ')(') + ')'; | ||
reTpl = '(.?' + preTpl + ')' + reTpl + '(' + sufTpl + '.*)'; // nit: trailing capture here assumes interIns = Inf | ||
} | ||
} | ||
else | ||
else { | ||
reTpl = reTpl.join(interCharsTpl); | ||
if (capt > 0) { | ||
if (interLft == 2) | ||
reTpl = '(' + preTpl + ')' + reTpl + '(' + sufTpl + ')'; | ||
else | ||
reTpl = '(.?)' + reTpl + '(.?)'; | ||
reTpl = preTpl + reTpl + sufTpl; | ||
} | ||
else | ||
reTpl = preTpl + reTpl + sufTpl; | ||
@@ -329,4 +333,4 @@ // console.log(reTpl); | ||
let [query, parts] = prepQuery(needle, 1); | ||
let [queryR] = prepQuery(needle, 2); | ||
let partsLen = parts.length; | ||
let [queryR] = prepQuery(needle, 2); | ||
@@ -393,2 +397,5 @@ let len = idxs.length; | ||
// will be populated if we need to re-generate a query with some exact terms | ||
let useExactParts = null; | ||
for (let j = 0, k = 2; j < partsLen; j++, k+=2) { | ||
@@ -401,2 +408,49 @@ let group = m[k].toLowerCase(); | ||
// this won't handle the case when an exact match exists across the boundary of the current group and the next junk | ||
// e.g. blob,ob when searching for 'bob' but finding the earlier `blob` (with extra insertion) | ||
if (!fullMatch && groupLen >= termLen && m[k+1].length >= termLen) { | ||
// probe for exact match in inter junk | ||
let idxOf = m[k+1].toLowerCase().indexOf(term); | ||
if (idxOf > -1) { | ||
// so here we have three options: | ||
// 1. mutate the current match to be better. | ||
// this doesn't help the range regex below, which would need different adjustement logic, since | ||
// its capture groups are more granular | ||
// 2. re-generate a new regex with some terms flagged as exact rather than a group of alterations | ||
// this is more expensive since we need to re-process, but will be seamless for range query, | ||
// but would require popping out of this loop and throwing away exisitng info counters | ||
// 3. do a combo of the above | ||
// we can stay in this loop, but also gen a more explicit ranges regex that will result in the | ||
// better match | ||
// this whole section probably risks violating interIns < Inf, since better terms might be too far away | ||
// we could test for this here to choose not to re-process, but it's pretty unusual to reduce interIns | ||
// (usually to only accept tighter matches). the match improvement here is likely better. | ||
// debugger; | ||
// shift the current group into the prior junk, adjust idxAcc & start | ||
let prepend = m[k] + m[k+1].slice(0, idxOf); | ||
m[k-1] += prepend; | ||
idxAcc += prepend.length; | ||
if (j == 0) | ||
start = idxAcc; | ||
// update current group and next junk | ||
m[k] = m[k+1].slice(idxOf, idxOf + termLen); | ||
m[k+1] = m[k+1].slice(idxOf + termLen); | ||
group = m[k].toLowerCase(); | ||
groupLen = termLen; | ||
fullMatch = true; | ||
if (useExactParts == null) | ||
useExactParts = Array(partsLen).fill(0); | ||
useExactParts[j] = 1; | ||
} | ||
} | ||
if (mayDiscard || fullMatch) { | ||
@@ -477,2 +531,4 @@ // does group's left and/or right land on \b | ||
if (!disc) { | ||
let idxQueryR = useExactParts != null ? prepQuery(needle, 2, useExactParts)[0] : queryR; | ||
info.idx[ii] = idxs[i]; | ||
@@ -492,3 +548,3 @@ info.interLft2[ii] = lft2; | ||
// ranges | ||
let m = mhstr.match(queryR); | ||
let m = mhstr.match(idxQueryR); | ||
let ranges = info.ranges[ii] = []; | ||
@@ -578,2 +634,5 @@ | ||
// interOR | ||
// console.log(prepQuery(needle, 1, null, true)); | ||
// non-ooo or ooo w/single term | ||
@@ -580,0 +639,0 @@ if (needles == null) { |
@@ -7,3 +7,3 @@ /** | ||
* A tiny, efficient fuzzy matcher that doesn't suck | ||
* https://github.com/leeoniya/uFuzzy (v1.0.0) | ||
* https://github.com/leeoniya/uFuzzy (v1.0.1) | ||
*/ | ||
@@ -180,3 +180,3 @@ | ||
const prepQuery = (needle, capt = 0) => { | ||
const prepQuery = (needle, capt = 0, exactParts, interOR = false) => { | ||
// split on punct, whitespace, num-alpha, and upper-lower boundaries | ||
@@ -190,3 +190,3 @@ let parts = split(needle); | ||
if (intraMode == 1) { | ||
reTpl = parts.map(p => { | ||
reTpl = parts.map((p, pi) => { | ||
let { | ||
@@ -200,3 +200,3 @@ intraSlice, | ||
if (intraIns + intraSub + intraTrn + intraDel == 0) | ||
if (intraIns + intraSub + intraTrn + intraDel == 0 || exactParts?.[pi] == 1) | ||
return p; | ||
@@ -247,3 +247,7 @@ | ||
return '(?:' + p + '|' + variants.join('|') + ')'; | ||
let reTpl = '(?:' + p + '|' + variants.join('|') + ')'; | ||
// console.log(reTpl); | ||
return reTpl; | ||
}); | ||
@@ -261,3 +265,3 @@ } | ||
reTpl = parts.map(p => p.split('').map((c, i, chars) => { | ||
reTpl = parts.map((p, pi) => exactParts?.[pi] == 1 ? p : p.split('').map((c, i, chars) => { | ||
// neg lookahead to prefer matching 'Test' instead of 'tTest' in ManifestTest or fittest | ||
@@ -283,17 +287,17 @@ // but skip when search term contains leading repetition (aardvark, aaa) | ||
if (capt > 0) { | ||
// sadly, we also have to capture the inter-term junk via parenth-wrapping .*? | ||
// to accum other capture groups' indices for \b boosting during scoring | ||
reTpl = '(' + reTpl.join(')(' + interCharsTpl + ')(') + ')'; | ||
if (interOR) { | ||
// this is basically for doing .matchAll() occurence counting and highlihting without needing permuted ooo needles | ||
reTpl = preTpl + '(' + reTpl.join(')' + sufTpl + '|' + preTpl + '(') + ')' + sufTpl; | ||
} | ||
else { | ||
// sadly, we also have to capture the inter-term junk via parenth-wrapping .*? | ||
// to accum other capture groups' indices for \b boosting during scoring | ||
reTpl = '(' + reTpl.join(')(' + interCharsTpl + ')(') + ')'; | ||
reTpl = '(.?' + preTpl + ')' + reTpl + '(' + sufTpl + '.*)'; // nit: trailing capture here assumes interIns = Inf | ||
} | ||
} | ||
else | ||
else { | ||
reTpl = reTpl.join(interCharsTpl); | ||
if (capt > 0) { | ||
if (interLft == 2) | ||
reTpl = '(' + preTpl + ')' + reTpl + '(' + sufTpl + ')'; | ||
else | ||
reTpl = '(.?)' + reTpl + '(.?)'; | ||
reTpl = preTpl + reTpl + sufTpl; | ||
} | ||
else | ||
reTpl = preTpl + reTpl + sufTpl; | ||
@@ -332,4 +336,4 @@ // console.log(reTpl); | ||
let [query, parts] = prepQuery(needle, 1); | ||
let [queryR] = prepQuery(needle, 2); | ||
let partsLen = parts.length; | ||
let [queryR] = prepQuery(needle, 2); | ||
@@ -396,2 +400,5 @@ let len = idxs.length; | ||
// will be populated if we need to re-generate a query with some exact terms | ||
let useExactParts = null; | ||
for (let j = 0, k = 2; j < partsLen; j++, k+=2) { | ||
@@ -404,2 +411,49 @@ let group = m[k].toLowerCase(); | ||
// this won't handle the case when an exact match exists across the boundary of the current group and the next junk | ||
// e.g. blob,ob when searching for 'bob' but finding the earlier `blob` (with extra insertion) | ||
if (!fullMatch && groupLen >= termLen && m[k+1].length >= termLen) { | ||
// probe for exact match in inter junk | ||
let idxOf = m[k+1].toLowerCase().indexOf(term); | ||
if (idxOf > -1) { | ||
// so here we have three options: | ||
// 1. mutate the current match to be better. | ||
// this doesn't help the range regex below, which would need different adjustement logic, since | ||
// its capture groups are more granular | ||
// 2. re-generate a new regex with some terms flagged as exact rather than a group of alterations | ||
// this is more expensive since we need to re-process, but will be seamless for range query, | ||
// but would require popping out of this loop and throwing away exisitng info counters | ||
// 3. do a combo of the above | ||
// we can stay in this loop, but also gen a more explicit ranges regex that will result in the | ||
// better match | ||
// this whole section probably risks violating interIns < Inf, since better terms might be too far away | ||
// we could test for this here to choose not to re-process, but it's pretty unusual to reduce interIns | ||
// (usually to only accept tighter matches). the match improvement here is likely better. | ||
// debugger; | ||
// shift the current group into the prior junk, adjust idxAcc & start | ||
let prepend = m[k] + m[k+1].slice(0, idxOf); | ||
m[k-1] += prepend; | ||
idxAcc += prepend.length; | ||
if (j == 0) | ||
start = idxAcc; | ||
// update current group and next junk | ||
m[k] = m[k+1].slice(idxOf, idxOf + termLen); | ||
m[k+1] = m[k+1].slice(idxOf + termLen); | ||
group = m[k].toLowerCase(); | ||
groupLen = termLen; | ||
fullMatch = true; | ||
if (useExactParts == null) | ||
useExactParts = Array(partsLen).fill(0); | ||
useExactParts[j] = 1; | ||
} | ||
} | ||
if (mayDiscard || fullMatch) { | ||
@@ -480,2 +534,4 @@ // does group's left and/or right land on \b | ||
if (!disc) { | ||
let idxQueryR = useExactParts != null ? prepQuery(needle, 2, useExactParts)[0] : queryR; | ||
info.idx[ii] = idxs[i]; | ||
@@ -495,3 +551,3 @@ info.interLft2[ii] = lft2; | ||
// ranges | ||
let m = mhstr.match(queryR); | ||
let m = mhstr.match(idxQueryR); | ||
let ranges = info.ranges[ii] = []; | ||
@@ -581,2 +637,5 @@ | ||
// interOR | ||
// console.log(prepQuery(needle, 1, null, true)); | ||
// non-ooo or ooo w/single term | ||
@@ -583,0 +642,0 @@ if (needles == null) { |
@@ -1,2 +0,2 @@ | ||
/*! https://github.com/leeoniya/uFuzzy (v1.0.0) */ | ||
var uFuzzy=function(){"use strict";const t=new Intl.Collator("en").compare,e=1/0,n={interSplit:"[^A-Za-z0-9]+",intraSplit:"[a-z][A-Z]",intraBound:"[A-Za-z][0-9]|[0-9][A-Za-z]|[a-z][A-Z]",interLft:0,interRgt:0,interChars:".",interIns:e,intraChars:"[a-z\\d]",intraIns:0,intraMode:0,intraSlice:[1,e],intraSub:0,intraTrn:0,intraDel:0,intraFilt:()=>!0,sort:(e,n)=>{let{idx:l,chars:r,terms:i,interLft2:s,interLft1:a,start:h,intraIns:g,interIns:f}=e;return l.map(((t,e)=>e)).sort(((e,c)=>r[c]-r[e]||g[e]-g[c]||i[c]+s[c]+.5*a[c]-(i[e]+s[e]+.5*a[e])||f[e]-f[c]||h[e]-h[c]||t(n[l[e]],n[l[c]])))}},l=(t,n)=>0==n?"":1==n?t+"??":n==e?t+"*?":t+`{0,${n}}?`,r="(?:\\b|_)";function i(t){t=Object.assign({},n,t);const{interLft:e,interRgt:i,intraMode:s,intraSlice:h,intraIns:g,intraSub:f,intraTrn:c,intraDel:u,intraSplit:o,interSplit:p,intraBound:m,intraChars:b}=t;let{intraRules:S}=t;null==S&&(S=t=>{let e=n.intraSlice,l=0,r=0,i=0,s=0,a=t.length;return a>4?(e=h,l=g,r=f,i=c,s=u):3>a||(i=Math.min(c,1),4==a&&(l=Math.min(g,1))),{intraSlice:e,intraIns:l,intraSub:r,intraTrn:i,intraDel:s}});let I=!!o,R=RegExp(o,"g"),d=RegExp(p,"g"),x=RegExp("^"+p+"|"+p+"$","g");const A=t=>(t=t.replace(x,""),I&&(t=t.replace(R,(t=>t[0]+" "+t[1]))),t.split(d)),z=(n,a=0)=>{let h,f=A(n);if(1==s)h=f.map((t=>{let{intraSlice:e,intraIns:n,intraSub:r,intraTrn:i,intraDel:s}=S(t);if(n+r+i+s==0)return t;let[a,h]=e,g=t.slice(0,a),f=t.slice(h),c=t.slice(a,h);1==n&&1==g.length&&g!=c[0]&&(g+="(?!"+g+")");let u=c.length,o=[];if(r)for(let t=0;u>t;t++)o.push(g+c.slice(0,t)+b+c.slice(t+1)+f);if(i)for(let t=0;u-1>t;t++)c[t]!=c[t+1]&&o.push(g+c.slice(0,t)+c[t+1]+c[t]+c.slice(t+2)+f);if(s)for(let t=0;u>t;t++)o.push(g+c.slice(0,t+1)+"?"+c.slice(t+1)+f);if(n){let t=l(b,1);for(let e=0;u>e;e++)o.push(g+c.slice(0,e)+t+c.slice(e)+f)}return"(?:"+t+"|"+o.join("|")+")"}));else{let t=l(b,g);2==a&&g>0&&(t=")("+t+")("),h=f.map((e=>e.split("").map(((t,e,n)=>(1==g&&0==e&&n.length>1&&t[e]!=t[e+1]&&(t+="(?!"+t+")"),t))).join(t)))}let c=2==e?r:"",u=2==i?r:"",o=u+l(t.interChars,t.interIns)+c;return h=a>0?"("+h.join(")("+o+")(")+")":h.join(o),h=a>0?2==e?"("+c+")"+h+"("+u+")":"(.?)"+h+"(.?)":c+h+u,[RegExp(h,"i"),f]},E=(t,e,n)=>{let l=[],[r]=z(e);if(null!=n)for(let e=0;n.length>e;e++){let i=n[e];r.test(t[i])&&l.push(i)}else for(let e=0;t.length>e;e++)r.test(t[e])&&l.push(e);return l};let L=!!m,k=RegExp(p),y=RegExp(m);const C=(n,l,r)=>{let[s,a]=z(r,1),h=a.length,[g]=z(r,2),f=n.length,c=Array(f).fill(0),u={idx:Array(f),start:c.slice(),chars:c.slice(),terms:c.slice(),interIns:c.slice(),intraIns:c.slice(),interLft2:c.slice(),interRgt2:c.slice(),interLft1:c.slice(),interRgt1:c.slice(),ranges:Array(f)},o=1==e||1==i,p=0;for(let r=0;n.length>r;r++){let f=l[n[r]],c=f.match(s),m=c.index+c[1].length,b=m,S=!1,I=0,R=0,d=0,x=0,A=0,z=0,E=0,C=0;for(let n=0,l=2;h>n;n++,l+=2){let r=c[l].toLowerCase(),s=a[n],g=s.length,u=r.length,p=r==s;if(o||p){let t=b-1,n=b+u,l=!0,r=!0;if(-1==t||k.test(f[t]))p&&I++;else{if(2==e){S=!0;break}if(L&&y.test(f[t]+f[t+1]))p&&R++;else{if(1==e){S=!0;break}l=!1}}if(n==f.length||k.test(f[n]))p&&d++;else{if(2==i){S=!0;break}if(L&&y.test(f[n-1]+f[n]))p&&x++;else{if(1==i){S=!0;break}r=!1}}p&&(A+=g,l&&r&&z++)}if(u>g&&(C+=u-g),n>0&&(E+=c[l-1].length),!t.intraFilt(s,r,b)){S=!0;break}h-1>n&&(b+=u+c[l+1].length)}if(!S){u.idx[p]=n[r],u.interLft2[p]=I,u.interLft1[p]=R,u.interRgt2[p]=d,u.interRgt1[p]=x,u.chars[p]=A,u.terms[p]=z,u.interIns[p]=E,u.intraIns[p]=C,u.start[p]=m;let t=f.match(g),e=u.ranges[p]=[],l=t.index+t[1].length,i=l,s=l;for(let n=2;t.length>n;n++){let r=t[n].length;l+=r,n%2==0?s=l:r>0&&(e.push(i,s),i=s=l)}s>i&&e.push(i,s),p++}}if(n.length>p)for(let t in u)u[t]=u[t].slice(0,p);return u};return{search:(...e)=>((e,n,l=!1,r=1e3,i)=>{let s=null,h=null;if(l){let t=A(n);if(t.length>1){let n=t.slice().sort(((t,e)=>e.length-t.length));for(let t=0;n.length>t;t++){if(0==i?.length)return[[],null,null];i=E(e,n[t],i)}s=a(t).map((t=>t.join(" "))),h=[];let l=new Set;for(let t=0;s.length>t;t++)if(i.length>l.size){let n=i.filter((t=>!l.has(t))),r=E(e,s[t],n);for(let t=0;r.length>t;t++)l.add(r[t]);h.push(r)}else h.push([])}}null==s&&(s=[n],h=[i?.length>0?i:E(e,n)]);let g=null,f=null;if(r>=h.reduce(((t,e)=>t+e.length),0)){g={},f=[];for(let n=0;h.length>n;n++){let l=h[n];if(null==l||0==l.length)continue;let r=s[n],i=C(l,e,r),a=t.sort(i,e,r);if(n>0)for(let t=0;a.length>t;t++)a[t]+=f.length;for(let t in i)g[t]=(g[t]??[]).concat(i[t]);f=f.concat(a)}}return[[].concat(...h),g,f]})(...e),split:A,filter:E,info:C,sort:t.sort}}const s=(()=>{let t={A:"ÁÀÃÂÄĄ",a:"áàãâäą",E:"ÉÈÊËĖ",e:"éèêëę",I:"ÍÌÎÏĮ",i:"íìîïį",O:"ÓÒÔÕÖ",o:"óòôõö",U:"ÚÙÛÜŪŲ",u:"úùûüūų",C:"ÇČ",c:"çč",N:"Ñ",n:"ñ",S:"Š",s:"š"},e=new Map,n="";for(let l in t)t[l].split("").forEach((t=>{n+=t,e.set(t,l)}));let l=RegExp(`[${n}]`,"g");return t=>{let n=Array(t.length);for(let r=0;t.length>r;r++)n[r]=t[r].replace(l,(t=>e.get(t)));return n}})();function a(t){let e,n,l=(t=t.slice()).length,r=[t.slice()],i=Array(l).fill(0),s=1;for(;l>s;)s>i[s]?(e=s%2&&i[s],n=t[s],t[s]=t[e],t[e]=n,++i[s],s=1,r.push(t.slice())):(i[s]=0,++s);return r}const h=(t,e)=>e?`<mark>${t}</mark>`:t,g=(t,e)=>t+e;return i.latinize=s,i.permute=t=>a([...Array(t.length).keys()]).sort(((t,e)=>{for(let n=0;t.length>n;n++)if(t[n]!=e[n])return t[n]-e[n];return 0})).map((e=>e.map((e=>t[e])))),i.highlight=function(t,e,n=h,l="",r=g){l=r(l,n(t.substring(0,e[0]),!1))??l;for(let i=0;e.length>i;i+=2)l=r(l,n(t.substring(e[i],e[i+1]),!0))??l,e.length-3>i&&(l=r(l,n(t.substring(e[i+1],e[i+2]),!1))??l);return r(l,n(t.substring(e[e.length-1]),!1))??l},i}(); | ||
/*! https://github.com/leeoniya/uFuzzy (v1.0.1) */ | ||
var uFuzzy=function(){"use strict";const t=new Intl.Collator("en").compare,e=1/0,n={interSplit:"[^A-Za-z0-9]+",intraSplit:"[a-z][A-Z]",intraBound:"[A-Za-z][0-9]|[0-9][A-Za-z]|[a-z][A-Z]",interLft:0,interRgt:0,interChars:".",interIns:e,intraChars:"[a-z\\d]",intraIns:0,intraMode:0,intraSlice:[1,e],intraSub:0,intraTrn:0,intraDel:0,intraFilt:()=>!0,sort:(e,n)=>{let{idx:l,chars:r,terms:i,interLft2:s,interLft1:a,start:h,intraIns:f,interIns:g}=e;return l.map(((t,e)=>e)).sort(((e,c)=>r[c]-r[e]||f[e]-f[c]||i[c]+s[c]+.5*a[c]-(i[e]+s[e]+.5*a[e])||g[e]-g[c]||h[e]-h[c]||t(n[l[e]],n[l[c]])))}},l=(t,n)=>0==n?"":1==n?t+"??":n==e?t+"*?":t+`{0,${n}}?`,r="(?:\\b|_)";function i(t){t=Object.assign({},n,t);const{interLft:e,interRgt:i,intraMode:s,intraSlice:h,intraIns:f,intraSub:g,intraTrn:c,intraDel:o,intraSplit:u,interSplit:p,intraBound:m,intraChars:b}=t;let{intraRules:S}=t;null==S&&(S=t=>{let e=n.intraSlice,l=0,r=0,i=0,s=0,a=t.length;return a>4?(e=h,l=f,r=g,i=c,s=o):3>a||(i=Math.min(c,1),4==a&&(l=Math.min(f,1))),{intraSlice:e,intraIns:l,intraSub:r,intraTrn:i,intraDel:s}});let d=!!u,I=RegExp(u,"g"),R=RegExp(p,"g"),x=RegExp("^"+p+"|"+p+"$","g");const A=t=>(t=t.replace(x,""),d&&(t=t.replace(I,(t=>t[0]+" "+t[1]))),t.split(R)),L=(n,a=0,h,g=!1)=>{let c,o=A(n);if(1==s)c=o.map(((t,e)=>{let{intraSlice:n,intraIns:r,intraSub:i,intraTrn:s,intraDel:a}=S(t);if(r+i+s+a==0||1==h?.[e])return t;let[f,g]=n,c=t.slice(0,f),o=t.slice(g),u=t.slice(f,g);1==r&&1==c.length&&c!=u[0]&&(c+="(?!"+c+")");let p=u.length,m=[];if(i)for(let t=0;p>t;t++)m.push(c+u.slice(0,t)+b+u.slice(t+1)+o);if(s)for(let t=0;p-1>t;t++)u[t]!=u[t+1]&&m.push(c+u.slice(0,t)+u[t+1]+u[t]+u.slice(t+2)+o);if(a)for(let t=0;p>t;t++)m.push(c+u.slice(0,t+1)+"?"+u.slice(t+1)+o);if(r){let t=l(b,1);for(let e=0;p>e;e++)m.push(c+u.slice(0,e)+t+u.slice(e)+o)}return"(?:"+t+"|"+m.join("|")+")"}));else{let t=l(b,f);2==a&&f>0&&(t=")("+t+")("),c=o.map(((e,n)=>1==h?.[n]?e:e.split("").map(((t,e,n)=>(1==f&&0==e&&n.length>1&&t[e]!=t[e+1]&&(t+="(?!"+t+")"),t))).join(t)))}let u=2==e?r:"",p=2==i?r:"",m=p+l(t.interChars,t.interIns)+u;return a>0?g?c=u+"("+c.join(")"+p+"|"+u+"(")+")"+p:(c="("+c.join(")("+m+")(")+")",c="(.?"+u+")"+c+"("+p+".*)"):(c=c.join(m),c=u+c+p),[RegExp(c,"i"),o]},z=(t,e,n)=>{let l=[],[r]=L(e);if(null!=n)for(let e=0;n.length>e;e++){let i=n[e];r.test(t[i])&&l.push(i)}else for(let e=0;t.length>e;e++)r.test(t[e])&&l.push(e);return l};let y=!!m,C=RegExp(p),E=RegExp(m);const k=(n,l,r)=>{let[s,a]=L(r,1),[h]=L(r,2),f=a.length,g=n.length,c=Array(g).fill(0),o={idx:Array(g),start:c.slice(),chars:c.slice(),terms:c.slice(),interIns:c.slice(),intraIns:c.slice(),interLft2:c.slice(),interRgt2:c.slice(),interLft1:c.slice(),interRgt1:c.slice(),ranges:Array(g)},u=1==e||1==i,p=0;for(let g=0;n.length>g;g++){let c=l[n[g]],m=c.match(s),b=m.index+m[1].length,S=b,d=!1,I=0,R=0,x=0,A=0,z=0,k=0,j=0,w=0,M=null;for(let n=0,l=2;f>n;n++,l+=2){let r=m[l].toLowerCase(),s=a[n],h=s.length,g=r.length,o=r==s;if(!o&&g>=h&&m[l+1].length>=h){let t=m[l+1].toLowerCase().indexOf(s);if(t>-1){let e=m[l]+m[l+1].slice(0,t);m[l-1]+=e,S+=e.length,0==n&&(b=S),m[l]=m[l+1].slice(t,t+h),m[l+1]=m[l+1].slice(t+h),r=m[l].toLowerCase(),g=h,o=!0,null==M&&(M=Array(f).fill(0)),M[n]=1}}if(u||o){let t=S-1,n=S+g,l=!0,r=!0;if(-1==t||C.test(c[t]))o&&I++;else{if(2==e){d=!0;break}if(y&&E.test(c[t]+c[t+1]))o&&R++;else{if(1==e){d=!0;break}l=!1}}if(n==c.length||C.test(c[n]))o&&x++;else{if(2==i){d=!0;break}if(y&&E.test(c[n-1]+c[n]))o&&A++;else{if(1==i){d=!0;break}r=!1}}o&&(z+=h,l&&r&&k++)}if(g>h&&(w+=g-h),n>0&&(j+=m[l-1].length),!t.intraFilt(s,r,S)){d=!0;break}f-1>n&&(S+=g+m[l+1].length)}if(!d){let t=null!=M?L(r,2,M)[0]:h;o.idx[p]=n[g],o.interLft2[p]=I,o.interLft1[p]=R,o.interRgt2[p]=x,o.interRgt1[p]=A,o.chars[p]=z,o.terms[p]=k,o.interIns[p]=j,o.intraIns[p]=w,o.start[p]=b;let e=c.match(t),l=o.ranges[p]=[],i=e.index+e[1].length,s=i,a=i;for(let t=2;e.length>t;t++){let n=e[t].length;i+=n,t%2==0?a=i:n>0&&(l.push(s,a),s=a=i)}a>s&&l.push(s,a),p++}}if(n.length>p)for(let t in o)o[t]=o[t].slice(0,p);return o};return{search:(...e)=>((e,n,l=!1,r=1e3,i)=>{let s=null,h=null;if(l){let t=A(n);if(t.length>1){let n=t.slice().sort(((t,e)=>e.length-t.length));for(let t=0;n.length>t;t++){if(0==i?.length)return[[],null,null];i=z(e,n[t],i)}s=a(t).map((t=>t.join(" "))),h=[];let l=new Set;for(let t=0;s.length>t;t++)if(i.length>l.size){let n=i.filter((t=>!l.has(t))),r=z(e,s[t],n);for(let t=0;r.length>t;t++)l.add(r[t]);h.push(r)}else h.push([])}}null==s&&(s=[n],h=[i?.length>0?i:z(e,n)]);let f=null,g=null;if(r>=h.reduce(((t,e)=>t+e.length),0)){f={},g=[];for(let n=0;h.length>n;n++){let l=h[n];if(null==l||0==l.length)continue;let r=s[n],i=k(l,e,r),a=t.sort(i,e,r);if(n>0)for(let t=0;a.length>t;t++)a[t]+=g.length;for(let t in i)f[t]=(f[t]??[]).concat(i[t]);g=g.concat(a)}}return[[].concat(...h),f,g]})(...e),split:A,filter:z,info:k,sort:t.sort}}const s=(()=>{let t={A:"ÁÀÃÂÄĄ",a:"áàãâäą",E:"ÉÈÊËĖ",e:"éèêëę",I:"ÍÌÎÏĮ",i:"íìîïį",O:"ÓÒÔÕÖ",o:"óòôõö",U:"ÚÙÛÜŪŲ",u:"úùûüūų",C:"ÇČ",c:"çč",N:"Ñ",n:"ñ",S:"Š",s:"š"},e=new Map,n="";for(let l in t)t[l].split("").forEach((t=>{n+=t,e.set(t,l)}));let l=RegExp(`[${n}]`,"g");return t=>{let n=Array(t.length);for(let r=0;t.length>r;r++)n[r]=t[r].replace(l,(t=>e.get(t)));return n}})();function a(t){let e,n,l=(t=t.slice()).length,r=[t.slice()],i=Array(l).fill(0),s=1;for(;l>s;)s>i[s]?(e=s%2&&i[s],n=t[s],t[s]=t[e],t[e]=n,++i[s],s=1,r.push(t.slice())):(i[s]=0,++s);return r}const h=(t,e)=>e?`<mark>${t}</mark>`:t,f=(t,e)=>t+e;return i.latinize=s,i.permute=t=>a([...Array(t.length).keys()]).sort(((t,e)=>{for(let n=0;t.length>n;n++)if(t[n]!=e[n])return t[n]-e[n];return 0})).map((e=>e.map((e=>t[e])))),i.highlight=function(t,e,n=h,l="",r=f){l=r(l,n(t.substring(0,e[0]),!1))??l;for(let i=0;e.length>i;i+=2)l=r(l,n(t.substring(e[i],e[i+1]),!0))??l,e.length-3>i&&(l=r(l,n(t.substring(e[i+1],e[i+2]),!1))??l);return r(l,n(t.substring(e[e.length-1]),!1))??l},i}(); |
{ | ||
"name": "@leeoniya/ufuzzy", | ||
"version": "1.0.0", | ||
"version": "1.0.1", | ||
"description": "A tiny, efficient fuzzy matcher that doesn't suck", | ||
@@ -41,5 +41,5 @@ "main": "./dist/uFuzzy.cjs.js", | ||
"devDependencies": { | ||
"@rollup/plugin-terser": "^0.3.0", | ||
"rollup": "^3.10.1" | ||
"@rollup/plugin-terser": "^0.4.0", | ||
"rollup": "^3.11.0" | ||
} | ||
} |
@@ -24,3 +24,3 @@ ## ▒ μFuzzy | ||
- **Concise set of options** that don't interact in mysterious ways to drastically alter combined behavior. | ||
- **Fast with low resource usage** - there's no index to build, so startup is below 1ms with near-zero memory overhead. Searching a three-term phrase in a 162,000 phrase dataset takes 12ms with in-order terms or 50ms with out-of-order terms. | ||
- **Fast with low resource usage** - there's no index to build, so startup is below 1ms with near-zero memory overhead. Searching a three-term phrase in a 162,000 phrase dataset takes 13ms with out-of-order terms. | ||
- **Micro, with zero dependencies** - currently [~5.5KB min](https://github.com/leeoniya/uFuzzy/blob/main/dist/uFuzzy.iife.min.js) | ||
@@ -483,3 +483,3 @@ | ||
</td> | ||
<td>★ 1.9k</td> | ||
<td>★ 2k</td> | ||
<td>5.5KB</td> | ||
@@ -486,0 +486,0 @@ <td>0.3ms</td> |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
107986
2039