So sánh chuỗi Javascript Trả lại% khả năng


82

Tôi đang tìm kiếm một hàm JavaScript có thể so sánh hai chuỗi và trả lại khả năng chúng giống nhau. Tôi đã xem xét soundex nhưng điều đó không thực sự tuyệt vời đối với chuỗi nhiều từ hoặc không phải tên. Tôi đang tìm kiếm một chức năng như:

function compare(strA,strB){

}

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

Hàm sẽ hoạt động với tất cả các loại chuỗi, bao gồm số, giá trị nhiều từ và tên. Có lẽ có một thuật toán đơn giản tôi có thể sử dụng?

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


Tôi đang thử nghiệm cái này, vẫn gặp sự cố khi tìm ra cái hoàn hảo. Ví dụ cổ điển phá vỡ những điều này. Nói câu hỏi là "Hai vị tổng thống đầu tiên là gì?" và ai đó trả lời "adams and washington". Một chuỗi so sánh với "george washington john adams" phải là khoảng 50%.
Brad Ruderman

oof, phụ thuộc vào jQuery?
Shawn Whinnery

Câu trả lời:


135

Đây là câu trả lời dựa trên khoảng cách Levenshtein 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);
}

Để tính toán khoảng cách chỉnh sửa

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

Sử dụng

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

trả về 0,8571428571428571


Bạn có thể chơi với nó dưới đây:


Một cải tiến cho một số từ: var tương tự2 = function (s1, s2) {var split1 = s1.split (''); var split2 = s2.split (''); var sum = 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 = same (split1 [i], split2 [j]); if (max <temp) max = temp; } console.log (max); sum + = max / split1.length; } trả về tổng; };
infinito84

@ overlord1234 Phương thức trên có hoạt động với chuỗi như thế này không: 9e27dbb9ff6eea70821c02b4457cbc6b7eb8e12a64f46c192c3a05f1bc1519acd101193dac157c6233d9d773f9b364ca210bea46782fc965ef6700b
hyperfkcb

Nó hoạt động với các chuỗi mà không có ngữ nghĩa kèm theo. Vui lòng thử và chạy đoạn mã nội dòng (cảm ơn David). Tôi nhận được điểm tương tự là 0,17857142857142858 khi tôi nhập các chuỗi nói trên.
overlord1234

@hyperfkcb anh ấy đang triển khai thuật toán chỉnh sửa khoảng cách, tính toán có bao nhiêu ký tự ở vị trí sai (nhiều hơn hoặc ít hơn), vì vậy, để tính tỷ lệ phần trăm anh ấy đang lấy giá trị khoảng cách chỉnh sửa dài hơn có thể (longLength) và thực hiện (longLength - editDistance) / dài hơn.
infinito84

Tuy nhiên, nó quá chậm đối với các chuỗi dài.
cập nhật

16

Đây là một hàm rất đơn giản thực hiện so sánh và trả về tỷ lệ phần trăm dựa trên sự tương đương. Mặc dù nó chưa được thử nghiệm cho tất cả các trường hợp có thể xảy ra, nhưng nó có thể giúp bạn bắt đầu.

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%

7
Vấn đề với điều này là "atest" và "test" trả về 0%, mà chúng tôi biết là không đúng.
peresisUser

8

Với tôi, việc sử dụng thư viện này để tìm sự giống nhau về chuỗi giống như một cái duyên!

Đây là ví dụ -

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

6
Điều đó thật tuyệt, ngoại trừ stringSimilarity có một phần phụ thuộc được gọi là lodash chứa hơn 1.000 tệp đang được đưa vào dự án của bạn để bạn có thể nhận được sự giống nhau về chuỗi.
GrumpyCrouton

2
Vâng, nó xảy ra khi thêm một gói cục bộ. Nhưng thay vào đó, chúng ta có thể sử dụng CDN để có kích thước gói nhỏ hơn. Đây là các liên kết CDN - jsdelivr.com/package/npm/lodash - jsdelivr.com/package/npm/string-similarity
Tushar Walzade

2
Họ đã gỡ bỏ hầu hết phụ thuộc, bao gồm lodash
Lovenkrands

7

Chỉ một bài tôi viết nhanh có thể đủ tốt cho mục đích của bạn:

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

Điều này có trọng lượng các ký tự giống nhau nhưng khác chữ hoa 1 phần tư nặng bằng các ký tự hoàn toàn khác hoặc thiếu. Nó trả về một số từ 0 đến 1, 1 nghĩa là các chuỗi giống hệt nhau. 0 nghĩa là chúng không có điểm giống nhau. Ví dụ:

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
Không chính xác lắm: So sánh ("Apple", "zApple") = 0,07, trong khi So sánh ("Apple", "Applez") = 0,84
Kousha

3
@Kousha, đúng là có tư thế. "Apple" và "zApple" chỉ có một điểm chung duy nhất (chữ cái thứ hai p).
Paul

2
@Paulpro Apple và zApple có năm chữ cái chung về mặt logic. Đó là lỗi thực hiện của bạn. Apple, zApple, Applez cũng tương tự.
Kousha

2
@Kousha, zApple không tương tự theo thuật toán này, vì nó có vị trí. Điều đó không làm cho thuật toán không chính xác.
Paul

8
@Paulpro: Điều đó không làm cho thuật toán của bạn không chính xác, nhưng làm cho nó trở thành một câu trả lời kém cho câu hỏi này ...
MarcoS

6

Làm thế nào về chức năng similar_texttừ thư viện PHP.js ?

Nó dựa trên một hàm PHP có cùng tên .

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
Tương tự có được trả về dựa trên ký tự phù hợp không? Làm thế nào để nó đánh giá sự tương đồng
hyperfkcb

2

Để Tìm mức độ giống nhau giữa hai chuỗi; chúng ta có thể sử dụng nhiều hơn một hoặc hai phương pháp nhưng tôi chủ yếu nghiêng về việc sử dụng ' Hệ số xúc xắc ' . cái nào tốt hơn! theo hiểu biết của tôi tốt hơn là sử dụng ' khoảng cách Levenshtein '

Sử dụng gói ' chuỗi tương tự ' này từ npm, bạn sẽ có thể làm việc với những gì tôi đã nói ở trên.

một số ví dụ sử dụng dễ dàng là

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

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

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

để biết thêm vui lòng truy cập liên kết được đưa ra ở trên. Cảm ơn bạn.


1
Liên kết đến một giải pháp được hoan nghênh, nhưng hãy đảm bảo rằng câu trả lời của bạn hữu ích nếu không có nó: thêm ngữ cảnh xung quanh liên kết để những người dùng đồng nghiệp của bạn sẽ biết nó là gì và tại sao nó ở đó, sau đó trích dẫn phần có liên quan nhất của trang bạn ' đang liên kết lại trong trường hợp trang đích không có sẵn. Các câu trả lời ít hơn một liên kết có thể bị xóa .
David Buck

1

fuzzyset - Một chuỗi mờ được đặt cho javascript. fuzzyset là một cấu trúc dữ liệu thực hiện một cái gì đó tương tự như tìm kiếm toàn văn bản dựa trên dữ liệu để xác định các lỗi chính tả có thể xảy ra và đối sánh chuỗi gần đúng. Lưu ý rằng đây là một cổng javascript của một thư viện python.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.