skip to Main Content

In javascript Infinity is always larger than any other number except itself, while -Infinity is always smaller. What is an analogue of that for characters, i.e. what are the value of a string c for which c.localCompare(anyString) would always return (+/-)1 respectively (obviously except when c === anyString)?

In practice this will be used to sort objects based on 2 flags and the groupName property, which will be keyed in by users into another piece of software under input validation constraints, so won’t be an empty string. (I would need to check to see if someone could ‘attack’ the script by pasting lots of 0xFFFF into name prompt.)

If something like Infinity existed, the callback for sort() would look like this:

(n1, n2) =>
  (n1.flag1 ? plusCharInfinity :
    n1.flag2 ? minusCharInfinity :
      n1.groupName).localeCompare(
        n2.flag1 ? plusCharInfinity :
          n2.flag2 ? minusCharInfinity :
            n2.groupName)

I.e. if flag1 === true then demote to the bottom; if flag2 === true – promote to the top; otherwise use the given groupName value.

2

Answers


  1. As others have pointed out in the comments, it’s not possible to create strings which exist outside the range of valid string code points in order to enforce the relationships described.

    However, if you simply want to implement deterministic sort cases using the String.prototype.localeCompare() API (which is implementation-defined by the way), you can implement it using custom objects. Here is an example:

    const smallest = {
      localeCompare(that) {
        return Object.is(this, that) ? 0 : -1;
      },
    };
    
    const largest = {
      localeCompare(that) {
        return Object.is(this, that) ? 0 : 1;
      },
    };
    
    console.log(smallest.localeCompare(smallest)); // 0
    console.log(largest.localeCompare(largest)); // 0
    
    console.log(smallest.localeCompare(largest)); // -1
    console.log(smallest.localeCompare("")); // -1
    console.log(smallest.localeCompare("zzzzzzzzzz")); // -1
    // etc.
    
    console.log(largest.localeCompare(smallest)); // 1
    console.log(largest.localeCompare("")); // 1
    console.log(largest.localeCompare("zzzzzzzzzz")); // 1
    // etc.

    Code in TS Playground

    Login or Signup to reply.
  2. There’s no directly analogous values for strings, or at least none that I could easily find in the spec.

    Lower Bound

    The empty string, "", works well as the lower bound, however as @pilchard points out, it’s also a value that you might commonly have, so it’s not as safe as e.g. -Infinity which is always even lower than any actual valid non-infinity number.

    stringToCompare.localeCompare("");
    

    Higher Bound

    The high bound is harder to construct. Theoretically, you’d need to have the maximum length string, with every character as the highest value, and to wit, here are some approaches you could try to get that value:

    • You could try to override the getter for a specific string’s length property, to pretend you have a really big string. However, the prototype is frozen in most engines, and you’re not allowed to override it.

    • The JavaScript spec defines a maximum size for strings as 253-1 elements, which weighs in as requiring 16,384TiB of storage, and thus is impossible to construct.

      However most browsers limit this to a much smaller number (one which can actually fit in the RAM that we have today): 32-bit Chrome: ~512Mib, 64-bit Chrome: ~1Gib, Firefox: ~2Gib and Safari: ~4Gib. There are ways to check that length and construct such a string, but it would be quite the waste of memory, and not particularly performant.

    But, there is a simpler solution. If you’re OK with not having a fixed highest value, you could always construct the "higher" string each time you need to do a comparison.

    JavaScript represents characters as 16-bit numbers, making the biggest possible character uffff. If we construct a string that is the same as the original string, but which has this character at the beginning, then this new string will always be larger than the original string:

    stringToCompare.localeCompare(`uffff${stringToCompare}`);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search