Dizeleri Karşılaştır Javascript Olasıların Getirisi Yüzdesi


87

İki dizeyi karşılaştırabilen ve benzer olma olasılıklarını döndürebilen bir JavaScript işlevi arıyorum. Soundex'e baktım ama bu çok kelimeli diziler veya isim olmayanlar için pek iyi değil. Şunun gibi bir işlev arıyorum:

function compare(strA,strB){

}

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

İşlev, sayılar, çok kelimeli değerler ve adlar dahil olmak üzere her tür dizeyle çalışır. Belki kullanabileceğim basit bir algoritma vardır?

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


Bunu test ediyorum, hala mükemmel olanı bulmakta zorlanıyorum. Bunları kıran klasik örnek. Sorunun "ilk iki başkan nedir?" Olduğunu söyleyin. ve birisi "adams ve Washington" diye cevap veriyor. "George Washington John Adams" ile bir dizi karşılaştırması kabaca% 50 olmalıdır.
Brad Ruderman

oof, jQuery'ye bağlı mı?
Shawn Whinnery

Yanıtlar:


138

İşte Levenshtein mesafesine dayalı bir cevap 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);
}

Düzenleme mesafesini hesaplamak için

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

Kullanım

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

0.8571428571428571 döndürür


Aşağıda onunla oynayabilirsiniz:


Birkaç kelime için bir iyileştirme: var benzerlik2 = işlev (s1, s2) {var split1 = s1.split (''); var split2 = s2.split (''); var toplamı = 0; var max = 0; var temp = 0; for (var i = 0; i <split1.length; i ++) {max = 0; for (var j = 0; j <split2.length; j ++) {temp = benzerlik (split1 [i], split2 [j]); eğer (maks <sıcaklık) maks = sıcaklık; } console.log (maks.); toplam + = max / split1.length; } getiri toplamı; };
infinito84

@ overlord1234 Yukarıdaki yöntem aşağıdaki gibi dizeler için çalışıyor mu: 9e27dbb9ff6eea70821c02b4457cbc6b7eb8e12a64f46c192c3a05f1bc1519acd101193dac157c6233d9d773f9b364ca210d6287f9efa82
hyperfkcb

Anlamsal bir bağlılık olmadan dizelerle çalışır. Lütfen satır içi kod parçacığını deneyin ve çalıştırın (David'e teşekkürler). Yukarıda belirtilen dizeleri girdiğimde 0.17857142857142858 benzerlik elde ediyorum.
overlord1234

@hyperfkcb, kaç karakterin yanlış konumda olduğunu (az ya da çok) sayan düzenleme mesafesi algoritmasını uyguluyor, bu nedenle yüzdeyi hesaplamak için daha uzun olası düzenleme mesafesi değerini (daha uzun uzunluk) alıp (daha uzun uzunluk - mesafe düzenleme) yapıyor / longLength.
infinito84

Ancak, uzun dizeler için çok yavaştır.
2019

14

İşte bir karşılaştırma yapan ve denkliğe göre bir yüzde değeri döndüren çok basit bir işlev. Olası tüm senaryolar için test edilmemiş olsa da, başlamanıza yardımcı olabilir.

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%

9
Bununla ilgili sorun, "atest" ve "test" in% 0 döndürmesidir ki bunun doğru olmadığını biliyoruz.
peresisUser

8

Kullanımı bu dize benzerlik için kütüphane benim için bir cazibe gibi çalıştı!

İşte Örnek -

var similarity = stringSimilarity.compareTwoStrings("Apples","apple");    // => 0.88

6
Bu harika, ancak stringSimilarity'nin projenize 1000'den fazla dosya içeren lodash adlı bir bağımlılığı vardır, böylece dizge benzerliği elde edebilirsiniz.
GrumpyCrouton

2
Evet, yerel olarak bir paket eklerken olur. Ancak bunun yerine, daha küçük paket boyutu için CDN kullanabiliriz . İşte CDN bağlantıları - jsdelivr.com/package/npm/lodash - jsdelivr.com/package/npm/string-similarity
Tushar Walzade

2
Onlar lodash dahil olmak üzere çoğu bağımlılıkları, kaldırdık
Lovenkrands

7

Hızlı bir şekilde yazdığım ve amaçlarınız için yeterince iyi olabileceğini yazdım:

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

Bu, tamamen farklı veya eksik karakterlerle aynı ancak farklı durumdaki karakterlerin dörtte biri kadar ağırdır. 0 ile 1 arasında bir sayı döndürür, yani dizeler aynıdır. 0, benzerlikleri olmadığı anlamına gelir. Örnekler:

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

6
O kadar doğru değil: Karşılaştır ("Apple", "zApple") = 0,07, Karşılaştır ("Elma", "Applez") = 0,84
Kousha

3
@Kousha, konumsal. "Apple" ve "zApple" yalnızca bir ortak harf içerir (ikincisi p).
Paul

2
@Paulpro Apple ve zApple'ın mantıksal olarak ortak beş harfi vardır. Bu sizin uygulama hatanızdır. Apple, zApple, Applez benzer.
Kousha

2
@Kousha, zApple konumsal olduğu için bu algoritmaya göre benzer değildir. Bu, algoritmayı yanlış yapmaz.
Paul

8
@Paulpro: Bu ... senin algoritma yanlış yapmak ama bunu bu soru için kötü bir cevap yapar gelmez
Marcos

6

Nasıl işlevi hakkında similar_textgelen PHP.js kütüphanesinde ?

Aynı ada sahip bir PHP işlevine dayanmaktadır .

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

1
Eşleşen karaktere göre benzerlik döndürülür mü? Benzerliği nasıl değerlendiriyor
hyperfkcb

3

İki dizi arasındaki benzerlik derecesini bulmak için; Biz birden fazla ya da iki yöntem kullanabilirsiniz ama çoğunlukla kullanımına yönelik eğimli am ' Zar en Katsayısı ' . Hangisi daha iyi! Bildiğim kadarıyla ' Levenshtein mesafesi ' kullanmaktan daha iyi

Bu ' string-benzerlik ' paketini npm'den kullanarak yukarıda söylediklerim üzerinde çalışabileceksiniz.

bazı kolay kullanım örnekleri

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

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

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

daha fazlası için lütfen yukarıda verilen bağlantıyı ziyaret edin. Teşekkür ederim.


1
Bir çözüme bağlantı memnuniyetle karşılanır, ancak lütfen cevabınızın o olmadan yararlı olduğundan emin olun: Bağlantının etrafına bağlam ekleyin, böylece kullanıcı arkadaşlarınız bunun ne olduğu ve neden orada olduğu konusunda fikir sahibi olurlar, ardından sayfanın en alakalı bölümünü alıntılayın ' hedef sayfanın mevcut olmaması durumunda yeniden bağlanma. Bir bağlantıdan biraz daha fazla olan cevaplar silinebilir .
David Buck

1

fuzzyset - JavaScript için belirsiz bir dize kümesi. fuzzyset, olası yanlış yazımları ve yaklaşık dize eşleşmelerini belirlemek için verilere karşı tam metin aramasına benzer bir şey gerçekleştiren bir veri yapısıdır. Bunun bir python kitaplığının javascript bağlantı noktası olduğuna dikkat edin.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.