Wie kann ich eine Sublime-ähnliche unscharfe Suche auf select2 implementieren?Wie man erhabenen Text wie Fuzzy-Suche implementieren?
Beispiel, Eingabe von „sta jav sub“ würde passen „Stackoverflow Javascript erhaben wie“
Wie kann ich eine Sublime-ähnliche unscharfe Suche auf select2 implementieren?Wie man erhabenen Text wie Fuzzy-Suche implementieren?
Beispiel, Eingabe von „sta jav sub“ würde passen „Stackoverflow Javascript erhaben wie“
select2 können Sie Ihre eigene „Matcher“ -Funktionen (as seen on their docs) implementieren, dass die Verwendung und einige regexp Sie etwas tun können, wie:
$("#element").select2({
matcher: function(term, text, opt) {
//We call to uppercase to do a case insensitive match
//We replace every group of whitespace characters with a .+
//matching any number of characters
return text.toUpperCase().match(term.toUpperCase().replace(/\s+/g, '.+'));
}
});
A-Matcher-Funktion wird gegen jedes select2 Listenelement aufgerufen, wenn das Filtern/die Liste der Suche, Sie verwendet, dass jede Art von benutzerdefinierter Suche implementieren könnten.
Hier ist eine alternative übereinstimmende Funktion. http://jsfiddle.net/trevordixon/pXzj3/4/
function match(search, text) {
search = search.toUpperCase();
text = text.toUpperCase();
var j = -1; // remembers position of last found character
// consider each search character one at a time
for (var i = 0; i < search.length; i++) {
var l = search[i];
if (l == ' ') continue; // ignore spaces
j = text.indexOf(l, j+1); // search for character & update position
if (j == -1) return false; // if it's not found, exclude this item
}
return true;
}
Dieses ist ein bisschen schneller (zumindest wenn ich this test in Chrome auf OS X laufen), die wichtig sein kann, wenn Sie viele Einzelteile sind Filterung.
Ich mag Ihre Implementierung, aber Ihre wird nicht versuchen, die Zeichen in der Suchzeichenfolge übereinstimmen, ej ABS würde Any Bachelor Sunrise entsprechen, die möglicherweise oder nicht sein, was Sie erwarten würden :) – albertein
Ist nicht genau das, was von erwartet wird eine unscharfe Suche? – Kloar
@Kloar Er meint, dass 'ABS' 'Any Bachelor Sunrise' entsprechen sollte, aber' ABS' sollte nur passen, sagen wir, 'ABS Building Co.' – rvighne
alberteins Antwort stimmt nicht mit der Trevor-Version überein, da die ursprüngliche Funktion eine Übereinstimmung auf Zeichenbasis und nicht auf Wortbasis durchführt. Hier ist ein einfacher ein Matching auf Zeichenbasis:
$("#element").select2({
matcher: function(term, text, opts) {
var pattern = term.replace(/\s+/g, '').split('').join('.*');
text.match(new RegExp(pattern, 'i'))
}
})
var fuzzysearch = function (querystrings, values) {
return !querystrings.some(function (q) {
return !values.some(function (v) {
return v.toLocaleLowerCase().indexOf(q) !== -1;
});
});
}
Beispiel der Suche nach Titel und Autor in Buchsammlung http://jsfiddle.net/runjep/r887etnh/2/
Für eine 9kb Alternative, die das Suchergebnis zählt: http://kiro.me/projects/fuse.html
Sie Möglicherweise benötigen Sie ein Polyfill für die "einige" Funktion https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
var books = [{
id: 1,
title: 'The Great Gatsby',
author: 'F. Scott Fitzgerald'
}, {
id: 2,
title: 'The DaVinci Code',
author: 'Dan Brown'
}, {
id: 3,
title: 'Angels & Demons',
author: 'Dan Brown'
}];
search = function() {
var queryarray = document.getElementById('inp').value.trim().toLowerCase().split(' ');
var res = books.filter(function (b) {
return fs(queryarray, [b.title, b.author]);
});
document.getElementById('res').innerHTML = res.map(function (b) {
return b.title + ' <i> ' + b.author + '</i>';
}).join('<br/> ');
}
fs = function (qs, vals) {
return !qs.some(function (q) {
return !vals.some(function (v) {
return v.toLocaleLowerCase().indexOf(q) !== -1;
});
});
}
<input id="inp" />
<button id="but" onclick="search()">Search</button>
<div id="res"></div>
Dies ist Vanille Javascript und nicht wählen 2 - Entschuldigung –
function fuzzyMe(term, query) {
var score = 0;
var termLength = term.length;
var queryLength = query.length;
var highlighting = '';
var ti = 0;
// -1 would not work as this would break the calculations of bonus
// points for subsequent character matches. Something like
// Number.MIN_VALUE would be more appropriate, but unfortunately
// Number.MIN_VALUE + 1 equals 1...
var previousMatchingCharacter = -2;
for (var qi = 0; qi < queryLength && ti < termLength; qi++) {
var qc = query.charAt(qi);
var lowerQc = qc.toLowerCase();
for (; ti < termLength; ti++) {
var tc = term.charAt(ti);
if (lowerQc === tc.toLowerCase()) {
score++;
if ((previousMatchingCharacter + 1) === ti) {
score += 2;
}
highlighting += "<em>" + tc + "</em>";
previousMatchingCharacter = ti;
ti++;
break;
} else {
highlighting += tc;
}
}
}
highlighting += term.substring(ti, term.length);
return {
score: score,
term: term,
query: query,
highlightedTerm: highlighting
};
}
Die oben der Verschwommenheit kümmert. Dann können Sie einfach durchlaufen alle ausgewählten 2 Elemente
$("#element").select2({
matcher: function(term, text, opt) {
return fuzzyMe(term, text).highlightedTerm;
}
});
Kredit für Fuzzy-Code -: https://github.com/bripkens/fuzzy.js
ich einige schrieb, die sehr lange Sublime Text des Fuzzy-Match funktioniert. Um dies zu erreichen, sind ein paar Dinge nötig.
Zuerst alle Zeichen aus einem Muster in der Reihenfolge übereinstimmen. Zweitens stimmt der Punktestand überein, so dass bestimmte übereinstimmende Charaktere mehr Punkte wert sind als andere.
Ich kam auf ein paar Faktoren, um zu überprüfen. "CamelCase" Buchstaben oder Buchstaben nach einem Trennzeichen (Leerzeichen oder Unterstrich) sind viele Punkte wert. Konsekutivspiele sind mehr wert. Ergebnisse, die in der Nähe des Starts gefunden wurden, sind mehr wert.
Ein kritisch wichtiger Trick ist es, den am besten passenden Charakter zu finden. Was nicht unbedingt der erste ist. Betrachten Sie fuzzy_match ("tk", "The Black Knight"). Es gibt zwei Ks, die angepasst werden könnten. Die zweite ist mehr Punkte wert, weil sie einem Leerzeichen folgt.
JavaScript-Code ist unten.Es gibt einige Nuancen, die in einem Blogpost genauer beschrieben werden. Es gibt auch eine interaktive Demo. Und vollständige Quelle (einschließlich Demo, sowie C++ - Implementierung) auf GitHub.
// Returns [bool, score, formattedStr]
// bool: true if each character in pattern is found sequentially within str
// score: integer; higher is better match. Value has no intrinsic meaning. Range varies with pattern.
// Can only compare scores with same search pattern.
// formattedStr: input str with matched characters marked in <b> tags. Delete if unwanted.
function fuzzy_match(pattern, str) {
// Score consts
var adjacency_bonus = 5; // bonus for adjacent matches
var separator_bonus = 10; // bonus if match occurs after a separator
var camel_bonus = 10; // bonus if match is uppercase and prev is lower
var leading_letter_penalty = -3; // penalty applied for every letter in str before the first match
var max_leading_letter_penalty = -9; // maximum penalty for leading letters
var unmatched_letter_penalty = -1; // penalty for every letter that doesn't matter
// Loop variables
var score = 0;
var patternIdx = 0;
var patternLength = pattern.length;
var strIdx = 0;
var strLength = str.length;
var prevMatched = false;
var prevLower = false;
var prevSeparator = true; // true so if first letter match gets separator bonus
// Use "best" matched letter if multiple string letters match the pattern
var bestLetter = null;
var bestLower = null;
var bestLetterIdx = null;
var bestLetterScore = 0;
var matchedIndices = [];
// Loop over strings
while (strIdx != strLength) {
var patternChar = patternIdx != patternLength ? pattern.charAt(patternIdx) : null;
var strChar = str.charAt(strIdx);
var patternLower = patternChar != null ? patternChar.toLowerCase() : null;
var strLower = strChar.toLowerCase();
var strUpper = strChar.toUpperCase();
var nextMatch = patternChar && patternLower == strLower;
var rematch = bestLetter && bestLower == strLower;
var advanced = nextMatch && bestLetter;
var patternRepeat = bestLetter && patternChar && bestLower == patternLower;
if (advanced || patternRepeat) {
score += bestLetterScore;
matchedIndices.push(bestLetterIdx);
bestLetter = null;
bestLower = null;
bestLetterIdx = null;
bestLetterScore = 0;
}
if (nextMatch || rematch) {
var newScore = 0;
// Apply penalty for each letter before the first pattern match
// Note: std::max because penalties are negative values. So max is smallest penalty.
if (patternIdx == 0) {
var penalty = Math.max(strIdx * leading_letter_penalty, max_leading_letter_penalty);
score += penalty;
}
// Apply bonus for consecutive bonuses
if (prevMatched)
newScore += adjacency_bonus;
// Apply bonus for matches after a separator
if (prevSeparator)
newScore += separator_bonus;
// Apply bonus across camel case boundaries. Includes "clever" isLetter check.
if (prevLower && strChar == strUpper && strLower != strUpper)
newScore += camel_bonus;
// Update patter index IFF the next pattern letter was matched
if (nextMatch)
++patternIdx;
// Update best letter in str which may be for a "next" letter or a "rematch"
if (newScore >= bestLetterScore) {
// Apply penalty for now skipped letter
if (bestLetter != null)
score += unmatched_letter_penalty;
bestLetter = strChar;
bestLower = bestLetter.toLowerCase();
bestLetterIdx = strIdx;
bestLetterScore = newScore;
}
prevMatched = true;
}
else {
// Append unmatch characters
formattedStr += strChar;
score += unmatched_letter_penalty;
prevMatched = false;
}
// Includes "clever" isLetter check.
prevLower = strChar == strLower && strLower != strUpper;
prevSeparator = strChar == '_' || strChar == ' ';
++strIdx;
}
// Apply score for last match
if (bestLetter) {
score += bestLetterScore;
matchedIndices.push(bestLetterIdx);
}
// Finish out formatted string after last pattern matched
// Build formated string based on matched letters
var formattedStr = "";
var lastIdx = 0;
for (var i = 0; i < matchedIndices.length; ++i) {
var idx = matchedIndices[i];
formattedStr += str.substr(lastIdx, idx - lastIdx) + "<b>" + str.charAt(idx) + "</b>";
lastIdx = idx + 1;
}
formattedStr += str.substr(lastIdx, str.length - lastIdx);
var matched = patternIdx == patternLength;
return [matched, score, formattedStr];
}
hatten Schwierigkeiten mit neuer select2 hier, was
arbeitete$("#foo").select2({
matcher: matcher
});
function matcher(params, data) {
// return all opts if seachbox is empty
if(!params.term) {
return data;
} else if(data) {
var term = params.term.toUpperCase();
var option = data.text.toUpperCase();
var j = -1; // remembers position of last found character
// consider each search character one at a time
for (var i = 0; i < term.length; i++) {
var l = term[i];
if (l == ' ') continue; // ignore spaces
j = option.indexOf(l, j+1); // search for character & update position
if (j == -1) return false; // if it's not found, exclude this item
}
return data; // return option
}
}
diese verdient mehr Upvotes. sehr nützliche Antwort. – r3wt