문자열 비교 자바 스크립트 반환 가능성 %


82

두 문자열을 비교하고 비슷할 가능성을 반환 할 수있는 JavaScript 함수를 찾고 있습니다. 나는 soundex를 보았지만 여러 단어로 된 문자열이나 이름이 아닌 경우에는별로 좋지 않습니다. 다음과 같은 기능을 찾고 있습니다.

function compare(strA,strB){

}

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

이 함수는 숫자, 여러 단어 값 및 이름을 포함하여 모든 유형의 문자열에서 작동합니다. 제가 사용할 수있는 간단한 알고리즘이 있을까요?

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


나는 이것을 테스트하고 있지만 여전히 완벽한 것을 찾는 데 어려움을 겪고 있습니다. 이것을 깨는 고전적인 예. "처음 두 대통령은 무엇입니까?"라고 질문합니다. 누군가 "아담스와 워싱턴"이라고 대답합니다. "george washington john adams"에 대한 문자열 비교는 대략 50 % 여야합니다.
Brad Ruderman

oof, jQuery에 의존합니까?
Shawn Whinnery

답변:


135

다음은 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);
}

편집 거리 계산 용

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

용법

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

0.8571428571428571 반환


아래에서 재생할 수 있습니다.


여러 단어에 대한 개선 : var similarity2 = 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 = similarity (split1 [i], split2 [j]); if (최대 <온도) 최대 = 온도; } console.log (max); 합계 + = 최대 / split1.length; } 반환 합계; };
infinito84

@ overlord1234 위의 방법은 다음과 같은 문자열에 대해 작동합니까?
hyperfkcb

의미 체계가 첨부되지 않은 문자열과 함께 작동합니다. 인라인 코드 조각을 실행 해보십시오 (David에게 감사드립니다). 앞서 언급 한 문자열을 입력하면 0.17857142857142858의 유사성을 얻습니다.
overlord1234

@hyperfkcb 그는 얼마나 많은 문자가 잘못된 위치에 있는지 (어느 정도) 계산하는 편집 거리 알고리즘을 구현하고 있으므로 백분율을 계산하기 위해 가능한 더 긴 편집 거리 값 (longerLength)을 사용하고 수행 (longerLength-editDistance) / longLength.
infinito84

그러나 긴 문자열에는 너무 느립니다.
upupming

16

다음은 비교를 수행하고 동등성을 기준으로 백분율을 반환하는 매우 간단한 함수입니다. 가능한 모든 시나리오에 대해 테스트되지는 않았지만 시작하는 데 도움이 될 수 있습니다.

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
문제는 "atest"와 "test"가 0 %를 반환한다는 것인데, 이는 사실이 아닙니다.
peresisUser

8

문자열 유사성을 위해이 라이브러리를 사용하는 것은 저에게 매력처럼 작용했습니다!

여기에 예가 있습니다-

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

6
stringSimilarity에는 프로젝트에 1,000 개 이상의 파일을 포함하는 lodash라는 종속성이 있다는 점을 제외하면 문자열 유사성을 얻을 수 있습니다.
GrumpyCrouton

2
예, 패키지를 로컬로 추가하는 동안 발생합니다. 그러나 대신 CDN 을 사용 하여 번들 크기를 줄일 수 있습니다 . - 여기에 CDN 링크입니다 jsdelivr.com/package/npm/lodash - jsdelivr.com/package/npm/string-similarity은
Tushar Walzade

2
그들은 lodash를 포함한 대부분의 종속성을 제거 한
뢰벤 크란츠

7

귀하의 목적에 충분할 수 있도록 내가 빠르게 썼던 하나만 :

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

이것은 동일하지만 대소 문자가 다른 문자의 무게를 완전히 다르거 나 누락 된 문자의 1/4만큼 무겁게합니다. 0과 1 사이의 숫자를 반환하며 1은 문자열이 동일 함을 의미합니다. 0은 유사점이 없음을 의미합니다. 예 :

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
정확하지 않음 : Compare ( "Apple", "zApple") = 0.07, while Compare ( "Apple", "Applez") = 0.84
Kousha

3
@Kousha, 위치입니다. "Apple"과 "zApple"에는 공통된 문자가 하나만 있습니다 (두 번째 p).
Paul

2
@Paulpro Apple과 zApple은 논리적으로 5 개의 공통 문자를 가지고 있습니다. 귀하의 구현 결함입니다. Apple, zApple, Applez는 비슷합니다.
Kousha

2
@Kousha, zApple은 위치 적이므로이 알고리즘에 따르면 유사하지 않습니다. 그렇다고 알고리즘이 잘못되는 것은 아닙니다.
Paul

8
@Paulpro : 그건 ... 당신의 알고리즘이 부정확하게하지만,이 질문에 대한 빈약 한 대답을하게하지 않습니다
마르코스

6

PHP.js 라이브러리의 기능 similar_text어떻 습니까?

같은 이름 의 PHP 함수를 기반으로합니다 .

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
일치하는 문자에 따라 유사성이 반환됩니까? 어떻게 유사성 평가합니까
hyperfkcb

2

두 문자열 사이의 유사도를 찾으려면 우리는 하나 또는 두 가지 이상의 방법을 사용할 수 있지만 주로 ' 주사위 계수 '를 사용하는 경향이 있습니다 . 어떤게 더 좋아! 내 지식으로는 ' Levenshtein distance '를 사용하는 것보다

npm 의이 ' string-similarity '패키지를 사용하면 위에서 말한 것을 작업 할 수 있습니다.

몇 가지 쉬운 사용 예는

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

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

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

자세한 내용은 위에 제공된 링크를 방문하십시오. 감사합니다.


1
솔루션에 대한 링크는 환영하지만 답변이없는 경우 유용한 지 확인하십시오 . 링크 주변에 컨텍스트를 추가 하여 동료 사용자가 그것이 무엇인지, 왜 거기에 있는지 알 수 있도록 한 다음 페이지에서 가장 관련성이 높은 부분을 인용하십시오. 대상 페이지를 사용할 수없는 경우 다시 연결합니다. 링크에 불과한 답변은 삭제 될 수 있습니다 .
David Buck

1

fuzzyset- 자바 스크립트에 대해 설정된 퍼지 문자열입니다. fuzzyset은 데이터에 대한 전체 텍스트 검색과 유사한 작업을 수행하여 오타 가능성과 대략적인 문자열 일치를 확인하는 데이터 구조입니다. 이것은 파이썬 라이브러리의 자바 스크립트 포트입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.