2013-06-04 2 views

Antwort

11

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.

+0

diese verdient mehr Upvotes. sehr nützliche Antwort. – r3wt

23

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.

+2

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

+11

Ist nicht genau das, was von erwartet wird eine unscharfe Suche? – Kloar

+0

@Kloar Er meint, dass 'ABS' 'Any Bachelor Sunrise' entsprechen sollte, aber' ABS' sollte nur passen, sagen wir, 'ABS Building Co.' – rvighne

3

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')) 
    } 
}) 
1
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>

+0

Dies ist Vanille Javascript und nicht wählen 2 - Entschuldigung –

0
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

7

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.

  • Blog Post
  • Interactive Demo
  • 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]; 
    } 
    
0

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 
    } 
}