Vergleichen Sie Strings Javascript Return% of Likely

82

Ich suche nach einer JavaScript-Funktion, die zwei Zeichenfolgen vergleichen und die Wahrscheinlichkeit zurückgeben kann, dass sie gleich sind. Ich habe mir Soundex angesehen, aber das ist nicht besonders gut für Zeichenfolgen mit mehreren Wörtern oder Nicht-Namen. Ich suche eine Funktion wie:

function compare(strA,strB){

}

compare("Apples","apple") = Some X Percentage.

Die Funktion funktioniert mit allen Arten von Zeichenfolgen, einschließlich Zahlen, Mehrwortwerten und Namen. Vielleicht gibt es einen einfachen Algorithmus, den ich verwenden könnte?

Ultimately none of these served my purpose so I used this:

 function compare(c, u) {
        var incept = false;
        var ca = c.split(",");
        u = clean(u);
        //ca = correct answer array (Collection of all correct answer)
        //caa = a single correct answer word array (collection of words of a single correct answer)
        //u = array of user answer words cleaned using custom clean function
        for (var z = 0; z < ca.length; z++) {
            caa = $.trim(ca[z]).split(" ");
            var pc = 0;
            for (var x = 0; x < caa.length; x++) {
                for (var y = 0; y < u.length; y++) {
                    if (soundex(u[y]) != null && soundex(caa[x]) != null) {
                        if (soundex(u[y]) == soundex(caa[x])) {
                            pc = pc + 1;
                        }
                    }
                    else {
                        if (u[y].indexOf(caa[x]) > -1) {
                            pc = pc + 1;
                        }
                    }
                }
            }
            if ((pc / caa.length) > 0.5) {
                return true;
            }
        }
        return false;
    }

    // create object listing the SOUNDEX values for each letter
    // -1 indicates that the letter is not coded, but is used for coding
    //  0 indicates that the letter is omitted for modern census archives
    //                              but acts like -1 for older census archives
    //  1 is for BFPV
    //  2 is for CGJKQSXZ
    //  3 is for DT
    //  4 is for L
    //  5 is for MN my home state
    //  6 is for R
    function makesoundex() {
        this.a = -1
        this.b = 1
        this.c = 2
        this.d = 3
        this.e = -1
        this.f = 1
        this.g = 2
        this.h = 0
        this.i = -1
        this.j = 2
        this.k = 2
        this.l = 4
        this.m = 5
        this.n = 5
        this.o = -1
        this.p = 1
        this.q = 2
        this.r = 6
        this.s = 2
        this.t = 3
        this.u = -1
        this.v = 1
        this.w = 0
        this.x = 2
        this.y = -1
        this.z = 2
    }

    var sndx = new makesoundex()

    // check to see that the input is valid
    function isSurname(name) {
        if (name == "" || name == null) {
            return false
        } else {
            for (var i = 0; i < name.length; i++) {
                var letter = name.charAt(i)
                if (!(letter >= 'a' && letter <= 'z' || letter >= 'A' && letter <= 'Z')) {
                    return false
                }
            }
        }
        return true
    }

    // Collapse out directly adjacent sounds
    // 1. Assume that surname.length>=1
    // 2. Assume that surname contains only lowercase letters
    function collapse(surname) {
        if (surname.length == 1) {
            return surname
        }
        var right = collapse(surname.substring(1, surname.length))
        if (sndx[surname.charAt(0)] == sndx[right.charAt(0)]) {
            return surname.charAt(0) + right.substring(1, right.length)
        }
        return surname.charAt(0) + right
    }

    // Collapse out directly adjacent sounds using the new National Archives method
    // 1. Assume that surname.length>=1
    // 2. Assume that surname contains only lowercase letters
    // 3. H and W are completely ignored
    function omit(surname) {
        if (surname.length == 1) {
            return surname
        }
        var right = omit(surname.substring(1, surname.length))
        if (!sndx[right.charAt(0)]) {
            return surname.charAt(0) + right.substring(1, right.length)
        }
        return surname.charAt(0) + right
    }

    // Output the coded sequence
    function output_sequence(seq) {
        var output = seq.charAt(0).toUpperCase() // Retain first letter
        output += "-" // Separate letter with a dash
        var stage2 = seq.substring(1, seq.length)
        var count = 0
        for (var i = 0; i < stage2.length && count < 3; i++) {
            if (sndx[stage2.charAt(i)] > 0) {
                output += sndx[stage2.charAt(i)]
                count++
            }
        }
        for (; count < 3; count++) {
            output += "0"
        }
        return output
    }

    // Compute the SOUNDEX code for the surname
    function soundex(value) {
        if (!isSurname(value)) {
            return null
        }
        var stage1 = collapse(value.toLowerCase())
        //form.result.value=output_sequence(stage1);

        var stage1 = omit(value.toLowerCase())
        var stage2 = collapse(stage1)
        return output_sequence(stage2);

    }

    function clean(u) {
        var u = u.replace(/\,/g, "");
        u = u.toLowerCase().split(" ");
        var cw = ["ARRAY OF WORDS TO BE EXCLUDED FROM COMPARISON"];
        var n = [];
        for (var y = 0; y < u.length; y++) {
            var test = false;
            for (var z = 0; z < cw.length; z++) {
                if (u[y] != "" && u[y] != cw[z]) {
                    test = true;
                    break;
                }
            }
            if (test) {
    //Don't use & or $ in comparison
                var val = u[y].replace("$", "").replace("&", "");
                n.push(val);
            }
        }
        return n;
    }
Brad Ruderman
quelle
Ich teste das aus und habe immer noch Probleme, das perfekte zu finden. Das klassische Beispiel, das diese bricht. Sagen Sie die Frage ist "Was sind die ersten beiden Präsidenten?" und jemand antwortet "adams and washington". Ein String-Vergleich zu "George Washington John Adams" sollte ungefähr 50% betragen.
Brad Ruderman
oof, hängt von jQuery ab?
Shawn Whinnery

Antworten:

135

Hier ist eine Antwort basierend auf der Levenshtein-Entfernung https://en.wikipedia.org/wiki/Levenshtein_distance

function similarity(s1, s2) {
  var longer = s1;
  var shorter = s2;
  if (s1.length < s2.length) {
    longer = s2;
    shorter = s1;
  }
  var longerLength = longer.length;
  if (longerLength == 0) {
    return 1.0;
  }
  return (longerLength - editDistance(longer, shorter)) / parseFloat(longerLength);
}

Zur Berechnung der Bearbeitungsentfernung

function editDistance(s1, s2) {
  s1 = s1.toLowerCase();
  s2 = s2.toLowerCase();

  var costs = new Array();
  for (var i = 0; i <= s1.length; i++) {
    var lastValue = i;
    for (var j = 0; j <= s2.length; j++) {
      if (i == 0)
        costs[j] = j;
      else {
        if (j > 0) {
          var newValue = costs[j - 1];
          if (s1.charAt(i - 1) != s2.charAt(j - 1))
            newValue = Math.min(Math.min(newValue, lastValue),
              costs[j]) + 1;
          costs[j - 1] = lastValue;
          lastValue = newValue;
        }
      }
    }
    if (i > 0)
      costs[s2.length] = lastValue;
  }
  return costs[s2.length];
}

Verwendung

similarity('Stack Overflow','Stack Ovrflw')

gibt 0,8571428571428571 zurück


Sie können unten damit spielen:

Overlord1234
quelle
Eine Verbesserung für mehrere Wörter: var Ähnlichkeit2 = Funktion (s1, s2) {var split1 = s1.split (''); var split2 = s2.split (''); var sum = 0; var max = 0; var temp = 0; für (var i = 0; i <split1.length; i ++) {max = 0; für (var j = 0; j <split2.length; j ++) {temp = Ähnlichkeit (split1 [i], split2 [j]); if (max <temp) max = temp; } console.log (max); Summe + = max / split1.length; } return sum; };
infinito84
@ overrord1234 Funktioniert die obige Methode für Zeichenfolgen wie diese: 9e27dbb9ff6eea70821c02b4457cbc6b7eb8e12a64f46c192c3a05f1bc1519acd101193dac157c6233d9d773f9b364ca210d6287f9ef9ef00
Hyperfkcb
Es funktioniert mit Strings ohne Semantik. Bitte versuchen Sie, das Inline-Code-Snippet auszuführen (danke an David). Ich erhalte eine Ähnlichkeit von 0,17857142857142858, wenn ich die oben genannten Zeichenfolgen eingebe.
Overlord1234
@hyperfkcb implementiert er den Bearbeitungsabstandsalgorithmus, der zählt, wie viele Zeichen sich an der falschen Position befinden (mehr oder weniger), sodass er zur Berechnung des Prozentsatzes den länger möglichen Wert für die Bearbeitungsentfernung (längerLänge) verwendet und dies tut (längerLänge - Bearbeitungsabstand) / längere Länge.
infinito84
Für lange Saiten ist es jedoch zu langsam.
Upupming
16

Hier ist eine sehr einfache Funktion, die einen Vergleich durchführt und einen Prozentsatz basierend auf der Äquivalenz zurückgibt. Es wurde zwar nicht für alle möglichen Szenarien getestet, kann Ihnen jedoch den Einstieg erleichtern.

function similar(a,b) {
    var equivalency = 0;
    var minLength = (a.length > b.length) ? b.length : a.length;    
    var maxLength = (a.length < b.length) ? b.length : a.length;    
    for(var i = 0; i < minLength; i++) {
        if(a[i] == b[i]) {
            equivalency++;
        }
    }
    

    var weight = equivalency / maxLength;
    return (weight * 100) + "%";
}
alert(similar("test","tes"));   // 75%
alert(similar("test","test"));  // 100%
alert(similar("test","testt")); // 80%
alert(similar("test","tess"));  // 75%
jmort253
quelle
7
Das Problem dabei ist, dass "atest" und "test" 0% zurückgeben, von denen wir wissen, dass sie nicht wahr sind.
PeresisUser
8

Die Verwendung dieser Bibliothek für die Ähnlichkeit von Zeichenfolgen hat für mich wie ein Zauber gewirkt!

Hier ist das Beispiel -

var similarity = stringSimilarity.compareTwoStrings("Apples","apple");    // => 0.88
Tushar Walzade
quelle
6
Das ist großartig, außer dass stringSimilarity eine Abhängigkeit namens lodash hat, die über 1.000 Dateien enthält, die in Ihr Projekt eingefügt werden, damit Sie String-Ähnlichkeit erhalten.
GrumpyCrouton
2
Ja, es passiert beim lokalen Hinzufügen eines Pakets. Stattdessen können wir CDN für eine geringere Bundle-Größe verwenden. Hier sind die CDN-Links - jsdelivr.com/package/npm/lodash - jsdelivr.com/package/npm/string-similarity
Tushar Walzade
2
Sie haben die meisten Abhängigkeiten entfernt, einschließlich lodash
Lovenkrands
7

Nur eine, die ich schnell geschrieben habe und die für Ihre Zwecke gut genug sein könnte:

function Compare(strA,strB){
    for(var result = 0, i = strA.length; i--;){
        if(typeof strB[i] == 'undefined' || strA[i] == strB[i]);
        else if(strA[i].toLowerCase() == strB[i].toLowerCase())
            result++;
        else
            result += 4;
    }
    return 1 - (result + 4*Math.abs(strA.length - strB.length))/(2*(strA.length+strB.length));
}

Dies wiegt Zeichen, die gleich, aber unterschiedlich sind, ein Viertel so stark wie Zeichen, die völlig unterschiedlich sind oder fehlen. Es wird eine Zahl zwischen 0 und 1 zurückgegeben, 1 was bedeutet, dass die Zeichenfolgen identisch sind. 0 bedeutet, dass sie keine Ähnlichkeiten haben. Beispiele:

Compare("Apple", "Apple")    // 1
Compare("Apples", "Apple")   // 0.8181818181818181
Compare("Apples", "apple")   // 0.7727272727272727
Compare("a", "A")            // 0.75
Compare("Apples", "appppp")  // 0.45833333333333337
Compare("a", "b")            // 0
Paul
quelle
6
Nicht so genau: Vergleichen ("Apple", "zApple") = 0,07, während Vergleichen ("Apple", "Applez") = 0,84
Kousha
3
@Kousha, es ist positionell. "Apple" und "zApple" haben nur einen Buchstaben gemeinsam (den zweiten p).
Paul
2
@Paulpro Apple und zApple haben logischerweise fünf Buchstaben gemeinsam. Es ist Ihr Implementierungsfehler. Apple, zApple, Applez sind ähnlich.
Kousha
2
@Kousha, zApple ist nach diesem Algorithmus nicht ähnlich, da es positionell ist. Das macht den Algorithmus nicht falsch.
Paul
8
@ Paulpro: Das macht Ihren Algorithmus nicht falsch, aber es macht es eine schlechte Antwort auf diese Frage ...
MarcoS
6

Wie wäre es mit einer Funktion similar_textaus der PHP.js-Bibliothek ?

Es basiert auf einer PHP - Funktion mit dem gleichen Namen .

function similar_text (first, second) {
    // Calculates the similarity between two strings  
    // discuss at: http://phpjs.org/functions/similar_text

    if (first === null || second === null || typeof first === 'undefined' || typeof second === 'undefined') {
        return 0;
    }

    first += '';
    second += '';

    var pos1 = 0,
        pos2 = 0,
        max = 0,
        firstLength = first.length,
        secondLength = second.length,
        p, q, l, sum;

    max = 0;

    for (p = 0; p < firstLength; p++) {
        for (q = 0; q < secondLength; q++) {
            for (l = 0;
            (p + l < firstLength) && (q + l < secondLength) && (first.charAt(p + l) === second.charAt(q + l)); l++);
            if (l > max) {
                max = l;
                pos1 = p;
                pos2 = q;
            }
        }
    }

    sum = max;

    if (sum) {
        if (pos1 && pos2) {
            sum += this.similar_text(first.substr(0, pos2), second.substr(0, pos2));
        }

        if ((pos1 + max < firstLength) && (pos2 + max < secondLength)) {
            sum += this.similar_text(first.substr(pos1 + max, firstLength - pos1 - max), second.substr(pos2 + max, secondLength - pos2 - max));
        }
    }

    return sum;
}
Vision
quelle
1
Wird die Ähnlichkeit basierend auf dem übereinstimmenden Zeichen zurückgegeben? Wie bewertet es die Ähnlichkeit
hyperfkcb
2

Grad der Ähnlichkeit zwischen zwei Zeichenfolgen zu finden; wir können mehr als ein oder zwei Methoden verwenden , aber ich bin vor allem auf die Verwendung von geneigt Dice-Koeffizient . Welches ist besser! Gut in meinem Wissen als " Levenshtein Abstand " zu verwenden

Mit diesem ' String-Ähnlichkeitspaket ' von npm können Sie an dem arbeiten, was ich oben gesagt habe.

Einige einfache Verwendungsbeispiele sind

var stringSimilarity = require('string-similarity');

var similarity = stringSimilarity.compareTwoStrings('healed', 'sealed'); 

var matches = stringSimilarity.findBestMatch('healed', ['edward', 'sealed', 'theatre']);

Für weitere Informationen besuchen Sie bitte den oben angegebenen Link. Danke.

Leon Grinsen
quelle
1
Ein Link zu einer Lösung ist willkommen, aber stellen Sie sicher, dass Ihre Antwort ohne sie nützlich ist: Fügen Sie dem Link einen Kontext hinzu, damit Ihre Mitbenutzer eine Vorstellung davon haben, was es ist und warum es dort ist, und zitieren Sie dann den relevantesten Teil der Seite, die Sie verwenden. erneutes Verknüpfen mit, falls die Zielseite nicht verfügbar ist. Antworten, die kaum mehr als ein Link sind, können gelöscht werden .
David Buck
1

fuzzyset - Ein Fuzzy-String für Javascript. fuzzyset ist eine Datenstruktur, die eine Volltextsuche für Daten durchführt, um wahrscheinliche Rechtschreibfehler und ungefähre Zeichenfolgenübereinstimmungen zu ermitteln. Beachten Sie, dass dies ein Javascript-Port einer Python-Bibliothek ist.

Octavio Díaz
quelle