131 votes

Comparez les chaînes de caractères Javascript Return %of Likely

Je recherche une fonction JavaScript capable de comparer deux chaînes de caractères et de renvoyer la probabilité qu'elles soient identiques. J'ai regardé soundex mais ce n'est pas vraiment génial pour les chaînes de plusieurs mots ou les non-noms. Je cherche une fonction comme :

    function compare(strA,strB){

    }

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

La fonction fonctionnerait avec tous les types de chaînes, y compris les nombres, les valeurs à plusieurs mots et les noms. Peut-être existe-t-il un algorithme simple que je pourrais utiliser ?

En fin de compte, aucun d'entre eux n'a répondu à mes attentes et j'ai donc utilisé celui-ci :

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

6voto

VisioN Points 62518

Qu'en est-il de la fonction similar_text de Bibliothèque PHP.js ?

Il est basé sur une fonction PHP avec le même nom .

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

2voto

Octavio Díaz Points 21

fuzzyset - Un ensemble de chaînes floues pour javascript. fuzzyset est une structure de données qui effectue une sorte de recherche plein texte sur des données pour déterminer les fautes d'orthographe probables et la correspondance approximative des chaînes de caractères. Notez qu'il s'agit d'un portage javascript d'une bibliothèque python.

2voto

Scott Sauyet Points 4228

Dans une certaine mesure, j'aime les idées de Coefficient de dé intégrée dans le similarité des chaînes de caractères module. Mais j'ai l'impression qu'en ne considérant que les bigrammes et en ne tenant pas compte de leurs multiplicités, on passe à côté de données importantes. Vous trouverez ci-dessous une version qui prend également en compte les multiplicités, et je pense qu'il s'agit d'une implémentation plus simple dans l'ensemble. Je n'essaie pas d'utiliser leur API, offrant seulement une fonction qui compare deux chaînes de caractères après une certaine manipulation (suppression des caractères non alphanumériques, mise en minuscule de tout, et compression mais non suppression des espaces blancs), construite au-dessus d'une fonction qui les compare sans cette manipulation. Il serait assez facile d'intégrer cette fonction dans leur API, mais je n'en vois pas l'utilité.

const stringSimilarity = (a, b) =>
  _stringSimilarity (prep (a), prep (b))

const _stringSimilarity = (a, b) => {
  const bg1 = bigrams (a)
  const bg2 = bigrams (b)
  const c1 = count (bg1)
  const c2 = count (bg2)
  const combined = uniq ([... bg1, ... bg2]) 
    .reduce ((t, k) => t + (Math .min (c1 [k] || 0, c2 [k] || 0)), 0)
  return 2 * combined / (Math .max (bg1 .length + bg2 .length, 1))
}

const prep = (str) => // TODO: unicode support?
  str .toLowerCase () .replace (/[^\w\s]/g, ' ') .replace (/\s+/g, ' ')

const bigrams = (str) => 
  [...str] .slice (0, -1) .map ((c, i) => c + str [i + 1])

const count = (xs) => 
  xs .reduce ((a, x) => ((a [x] = (a [x] || 0) + 1), a), {})

const uniq = (xs) => 
  [... new Set (xs)]

console .log (stringSimilarity (
  'foobar', 
  'Foobar'
)) //=> 1

console .log (stringSimilarity (
  "healed", 
  "sealed"
))//=> 0.8

console .log (stringSimilarity (
  "Olive-green table for sale, in extremely good condition.",
  "For sale: table in very good  condition, olive green in colour."
)) //=> 0.7787610619469026

console .log (stringSimilarity (
  "Olive-green table for sale, in extremely good condition.",
  "For sale: green Subaru Impreza, 210,000 miles"
)) //=> 0.38636363636363635

console .log (stringSimilarity (
  "Olive-green table for sale, in extremely good condition.",
  "Wanted: mountain bike with at least 21 gears."
)) //=> 0.1702127659574468

console .log (stringSimilarity (
  "The rain in Spain falls mainly on the plain.",
  "The run in Spun falls munly on the plun.",
)) //=> 0.7560975609756098

console .log (stringSimilarity (
  "Fa la la la la, la la la la",
  "Fa la la la la, la la",
)) //=> 0.8636363636363636

console .log (stringSimilarity (
  "car crash",
  "carcrash",
)) //=> 0.8

console .log (stringSimilarity (
  "Now is the time for all good men to come to the aid of their party.",
  "Huh?",
)) //=> 0

.as-console-wrapper {max-height: 100% !important; top: 0}

Certains cas de test proviennent de string-similarity, d'autres sont de mon cru. Ils montrent quelques différences significatives par rapport à ce paquet, mais rien de fâcheux. La seule différence que je relèverais est la différence entre "car crash" y "carcrash" que la similarité des chaînes de caractères considère comme identiques et que je rapporte avec une similarité de 0.8 . Ma version trouve plus de similitudes dans tous les cas de test vert olive que la similitude de chaîne, mais comme il s'agit en tout état de cause de nombres assez arbitraires, je ne suis pas sûr que cela fasse une grande différence ; ils les placent certainement dans le même ordre relatif.

2voto

Nigrimmist Points 41

similarité des chaînes de caractères lib vs Haut de la page (par @overloard1234) comparaison des performances vous trouverez ci-dessous

Sur la base du conseil de @Tushar Walzade d'utiliser similarité des chaînes de caractères vous pouvez trouver, par exemple, que

stringSimilatityLib.findBestMatch('KIA','Kia').bestMatch.rating 

renvoie 0,0

Il semblerait donc que il est préférable de le comparer en minuscules .

Meilleure utilisation de la base (pour les tableaux) :

findBestMatch(str, strArr) {
   const lowerCaseArr = strArr.map(element => element.toLowerCase());//creating lower case array
   const match = stringSimilatityLib.findBestMatch(str.toLowerCase(), lowerCaseArr).bestMatch; //trying to find bestMatch
   if (match.rating > 0) {
      const foundIndex = lowerCaseArr.findIndex(x => x === match.target); //finding the index of found best case
      return strArr[foundIndex]; //returning initial value from array
   }
    return null;
},

Performance

Par ailleurs, j'ai comparé la première réponse ici (faite par @overloard1234) et la librairie string-similarity (v4.0.4).

Les résultats sont disponibles ici : https://jsbench.me/szkzojoskq/1

Perf tests

Résultat : la similarité des chaînes de caractères est ~ deux fois plus rapide

Juste pour le plaisir : la v2.0 de la bibliothèque string-similarity est plus lente que la dernière 4.0.4 environ 2,2 fois. Donc mettez-la à jour, si vous utilisez encore < 3.0 :)

0voto

Parth Parmar Points 1
  const str1 = " pARTH PARmar r  ";
  const str2 = "  parmar r par     ";

  function calculateSimilarity(str1 = "", str2 = "") {
    let longer = str1.trim();
    let shorter = str2.trim();

    let a1 = longer.toLowerCase().split(" ");
    let b1 = shorter.toLowerCase().split(" ");
    let result = a1.every((aa, i) => aa[0] === b1[i][0]);

    if (longer.length < shorter.length)  [longer,shorter] = [shorter,longer];

    var arr = [];
    let count = 0;
    for(var i = 0;i<longer.length;i++){
      if(shorter && shorter.includes(longer[i])) {
        shorter = shorter.replace(longer[i],"")
        count++
      };
    }

    return {
      score : (count*100)/longer.length,
      result
    }
  }

  console.log(calculateSimilarity(str1, str2));

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X