jonathan
jonathan

Reputation:

Is there a JavaScript strcmp()?

Can anyone verify this for me? JavaScript does not have a version of strcmp(), so you have to write out something like:

 ( str1 < str2 ) ? 
            -1 : 
             ( str1 > str2 ? 1 : 0 );

Upvotes: 147

Views: 142384

Answers (8)

NastyCarl
NastyCarl

Reputation: 446

In my tests, this is about 10% faster than using a pair of ternary statements on the same set of randomly selected words.

function strcmp( a, b ) {
    for( let i=0 ; i<Math.min( a.length, b.length ) ; i++ ) {
        const n = a.charCodeAt(i) - b.charCodeAt(i);
        if( n ) return  n && ( ( n>>31 ) || 1 );
    }
    const n = a.length - b.length;
    return  n && ( ( n>>31 ) || 1 );
}

Upvotes: 0

Exo Flame
Exo Flame

Reputation: 147

So I fell into this rabbit hole and wrote some tests to build an intuition, the result's are weird. tldr it looks like localeCompare Performs a to lowercase that the equality operators do not. This causes "ff" to be >= "ZZ" but locale Compare returns -1 becuse "ff" <= 'zz'

For best results view logs of code in browser console ctrl + shift + i

second snip it hides hand tests so you see some random ones.

Home this helps someone

function stringBench(n, bench, min = 10, len = 10, logDif = false) {
  function makeid(length) {
    var result = '';
    var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    var charactersLength = characters.length;
    for (var i = 0; i < length; i++) {
      result += characters.charAt(Math.floor(Math.random() *
        charactersLength));
    }
    return result;

  }

  var a = [];
  var b = [];
  var pool = {};
  let rle = [];
  let rlc = [];

  let now;
  for (let i = 0; i < n; i++) {
    pool[i] = (makeid(min + Math.floor(Math.random() *
      len))); //10-20ish

  }
  for (let i = 0; i < bench; i++) {
    a[i] = (Math.floor(Math.random() * n));
    b[i] = (Math.floor(Math.random() * n));
  }

  console.log("now testin le vs lc on a pool of", n, " with this many samples ", bench);
  now = Date.now();
  for (let i = 0; i < bench; i++) {
    rlc[i] = pool[a[i]].localeCompare(pool[b[i]]);
  }
  let lcDelta = Date.now() - now;
  console.log("Performed ", bench, "lc localeCompare in ", lcDelta);


  now = Date.now();
  for (let i = 0; i < bench; i++) {
    rle[i] = pool[a[i]] <= pool[b[i]];
  }
  let leDelta = Date.now() - now;
  console.log("Performed ", bench, "le (<=) compares in ", leDelta)

  for (let i = 0; i < n; i++) {
    pool[i] = (makeid(min + Math.floor(Math.random() *
      len))); //10-20ish

  }
  for (let i = 0; i < bench; i++) {
    a[i] = (Math.floor(Math.random() * n));
    b[i] = (Math.floor(Math.random() * n));
  }


  now = Date.now();
  for (let i = 0; i < bench; i++) {
    rle[i] = pool[a[i]] <= pool[b[i]];
  }
  let leDelta2 = Date.now() - now;
  console.log("Performed ", bench, "le (<=) compares in ", leDelta2)

  now = Date.now();
  for (let i = 0; i < bench; i++) {
    rlc[i] = pool[a[i]].localeCompare(pool[b[i]]);
  }
  let lcDelta2 = Date.now() - now;
  console.log("Performed ", bench, "lc localeCompare in ", lcDelta2);

  function testCmp(a, b, log = true) {
    let le = a <= b;
    let ge = a >= b;
    let lc = a.localeCompare(b);
    let l = a < b;
    let g = a > b;
    if (le && ge) console.assert(lc == 0, 'le && ge -> == -> lc == 0,')
    if (le) console.assert(lc <= 0, 'le-> lc <= 0')
    if (ge) console.assert(lc >= 0, 'ge-> lc >= 0')
    if (l) console.assert(lc < 0, 'l=>lc < 0')
    if (g) console.assert(lc > 0, 'g-> lc > 0')
    if (!log) return;
    console.log(`Compare:  ${a} le ${b} `, a <= b);
    console.log(`Compare:  ${a} ge ${b}`, a >= b);
    console.log(`Compare: ${a} lc ${b}`, a.localeCompare(b));
  }

  let c = 0
  for (let i = 0; i < bench; i++) {
    if (rle[i] != rlc[i] <= 0) {
      c++;
      testCmp(pool[a[i]], pool[b[i]], true);
      console.warn(pool[a[i]], ' le != lc <= 0 ', pool[b[i]]);

    }


    // rlc[i] = pool[a[i]].localeCompare(pool[b[i]]);
  }
  console.warn(' le != lc  out of bench, num diffs: ', c);


  testCmp('ff', 'ff')
  testCmp('ff', 'fa')
  testCmp('ff', 'fz')
  testCmp('ff', 'fff')
  testCmp('ff', 'ffa')
  testCmp('ff', 'ffz')
  testCmp('ff', 'a')
  testCmp('ff', 'z')
  testCmp('ff', 'f')
  testCmp('ff', 'zff')
  testCmp('ff', 'aff')
  testCmp('ff', 'ZZ')
  testCmp('ff', 'AA')
  testCmp('FF', 'ZZ')
  testCmp('FF', 'ff')
  testCmp('FF', 'AA')
  testCmp('ff', 'ZZZ')

  console.log("Dif le - lc = ", leDelta2 - lcDelta2);

  console.log("avg le ms/Mops = ", (leDelta + leDelta2) / (bench / 1000000));
  console.log("avg lc ms/Mops = ", (lcDelta + lcDelta2) / (bench / 1000000));


  console.log("Dif  - lc = ", leDelta2 - lcDelta2);

};
stringBench(1000, 5000, 1, 3, true);
// stringBench(1000000, 1000000);//nothing is equire
// stringBench(1000, 100000000);
// stringBench(1000000, 100000000, 3, 5);
// stringBench(1000000, 100000000, 15, 20);

function stringBench(n, bench, min = 10, len = 10, logDif = false) {
  function makeid(length) {
    var result = '';
    var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    var charactersLength = characters.length;
    for (var i = 0; i < length; i++) {
      result += characters.charAt(Math.floor(Math.random() *
        charactersLength));
    }
    return result;


  }

  var a = [];
  var b = [];
  var pool = {};
  let rle = [];
  let rlc = [];

  let now;
  for (let i = 0; i < n; i++) {
    pool[i] = (makeid(min + Math.floor(Math.random() *
      len))); //10-20ish

  }
  for (let i = 0; i < bench; i++) {
    a[i] = (Math.floor(Math.random() * n));
    b[i] = (Math.floor(Math.random() * n));
  }

  console.log("now testin le vs lc on a pool of", n, " with this many samples ", bench);
  now = Date.now();
  for (let i = 0; i < bench; i++) {
    rlc[i] = pool[a[i]].localeCompare(pool[b[i]]);
  }
  let lcDelta = Date.now() - now;
  console.log("Performed ", bench, "lc localeCompare in ", lcDelta);


  now = Date.now();
  for (let i = 0; i < bench; i++) {
    rle[i] = pool[a[i]] <= pool[b[i]];
  }
  let leDelta = Date.now() - now;
  console.log("Performed ", bench, "le (<=) compares in ", leDelta)

  for (let i = 0; i < n; i++) {
    pool[i] = (makeid(min + Math.floor(Math.random() *
      len))); //10-20ish

  }
  for (let i = 0; i < bench; i++) {
    a[i] = (Math.floor(Math.random() * n));
    b[i] = (Math.floor(Math.random() * n));
  }


  now = Date.now();
  for (let i = 0; i < bench; i++) {
    rle[i] = pool[a[i]] <= pool[b[i]];
  }
  let leDelta2 = Date.now() - now;
  console.log("Performed ", bench, "le (<=) compares in ", leDelta2)

  now = Date.now();
  for (let i = 0; i < bench; i++) {
    rlc[i] = pool[a[i]].localeCompare(pool[b[i]]);
  }
  let lcDelta2 = Date.now() - now;
  console.log("Performed ", bench, "lc localeCompare in ", lcDelta2);

  function testCmp(a, b, log = true) {
    let le = a <= b;
    let ge = a >= b;
    let lc = a.localeCompare(b);
    let l = a < b;
    let g = a > b;
    if (le && ge) console.assert(lc == 0, 'le && ge -> == -> lc == 0,')
    if (le) console.assert(lc <= 0, 'le-> lc <= 0')
    if (ge) console.assert(lc >= 0, 'ge-> lc >= 0')
    if (l) console.assert(lc < 0, 'l=>lc < 0')
    if (g) console.assert(lc > 0, 'g-> lc > 0')
    if (!log) return;
    console.log(`Compare:  ${a} le ${b} `, a <= b);
    console.log(`Compare:  ${a} ge ${b}`, a >= b);
    console.log(`Compare: ${a} lc ${b}`, a.localeCompare(b));
  }

  let c = 0
  for (let i = 0; i < bench; i++) {
    if (rle[i] != rlc[i] <= 0) {
      c++;
      testCmp(pool[a[i]], pool[b[i]], true);
      console.warn(pool[a[i]], ' le != lc <= 0 ', pool[b[i]]);

    }


    // rlc[i] = pool[a[i]].localeCompare(pool[b[i]]);
  }
  console.warn(' le != lc  out of bench, num diffs: ', c);



  testCmp('ff', 'fa')
  testCmp('ff', 'fz')
  testCmp('ff', 'ZZ')



  console.log("Dif le - lc = ", leDelta2 - lcDelta2);

  console.log("avg le ms/Mops = ", (leDelta + leDelta2) / (bench / 1000000));
  console.log("avg lc ms/Mops = ", (lcDelta + lcDelta2) / (bench / 1000000));







  console.log("Dif  - lc = ", leDelta2 - lcDelta2);


  // for (let i = 0; i < bench; i++) {
  //     rlc[i] != rle[i]
  //     pool[a[i]].localeCompare(pool[b[i]]);
  // }
  //
  // console.log(makeid(5));
};
stringBench(1000, 5000, 1, 3, true);
// stringBench(1000000, 1000000);//nothing is equire
// stringBench(1000, 100000000);
// stringBench(1000000, 100000000, 3, 5);
// stringBench(1000000, 100000000, 15, 20);

Upvotes: 0

Runay Dhaygude
Runay Dhaygude

Reputation: 23

from this How to Check if Two Strings are Equal in JavaScript article:

  1. Generally, if your strings contain only ASCII characters, you use the === operator to check if they are equal.
  2. But when your strings contain characters that include combining characters(eg. e + ◌́ = é), you normalize them first before comparing for equality as follows- s1.normalize() === s2.normalize()

Upvotes: 1

Anonymous
Anonymous

Reputation: 91

var strcmp = new Intl.Collator(undefined, {numeric:true, sensitivity:'base'}).compare;

Usage: strcmp(string1, string2)

Result: 1 means string1 is bigger, 0 means equal, -1 means string2 is bigger.

This has higher performance than String.prototype.localeCompare

Also, numeric:true makes it do logical number comparison

Upvotes: 9

1&#39;&#39;
1&#39;&#39;

Reputation: 27125

localeCompare() is slow, so if you don't care about the "correct" ordering of non-English-character strings, try your original method or the cleaner-looking:

str1 < str2 ? -1 : +(str1 > str2)

This is an order of magnitude faster than localeCompare() on my machine.

The + ensures that the answer is always numeric rather than boolean.

Upvotes: 30

newacct
newacct

Reputation: 122519

What about

str1.localeCompare(str2)

Upvotes: 157

Esteban K&#252;ber
Esteban K&#252;ber

Reputation: 36862

Javascript doesn't have it, as you point out.

A quick search came up with:

function strcmp ( str1, str2 ) {
    // http://kevin.vanzonneveld.net
    // +   original by: Waldo Malqui Silva
    // +      input by: Steve Hilder
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +    revised by: gorthaur
    // *     example 1: strcmp( 'waldo', 'owald' );
    // *     returns 1: 1
    // *     example 2: strcmp( 'owald', 'waldo' );
    // *     returns 2: -1

    return ( ( str1 == str2 ) ? 0 : ( ( str1 > str2 ) ? 1 : -1 ) );
}

from http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_strcmp/

Of course, you could just add localeCompare if needed:

if (typeof(String.prototype.localeCompare) === 'undefined') {
    String.prototype.localeCompare = function(str, locale, options) {
        return ((this == str) ? 0 : ((this > str) ? 1 : -1));
    };
}

And use str1.localeCompare(str2) everywhere, without having to worry wether the local browser has shipped with it. The only problem is that you would have to add support for locales and options if you care about that.

Upvotes: 45

Panos Papadopoulos
Panos Papadopoulos

Reputation: 117

How about:

String.prototype.strcmp = function(s) {
    if (this < s) return -1;
    if (this > s) return 1;
    return 0;
}

Then, to compare s1 with 2:

s1.strcmp(s2)

Upvotes: -2

Related Questions