I found some approach to sort string in natural way
const rows = ['37-SK', '4-ML', '41-NP', '2-YZ', '21', '26-BF'];
console.log(rows.sort((a, b) => a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' })));
it outputs following:
0: "2-YZ"
1: "4-ML"
2: "21"
3: "26-BF"
4: "37-SK"
5: "41-NP"
length: 6
[[Prototype]]: Array(0)
but when i replace undefined with null
console.log(rows.sort((a, b) => a.localeCompare(b, null, { numeric: true, sensitivity: 'base' })));
it outputs:
Uncaught TypeError: Cannot convert undefined or null to object
at String.localeCompare (<anonymous>)
at natSort.js:2:35
at Array.sort (<anonymous>)
at natSort.js:2:18
Why?
CodePudding user response:
It's how the spec is written. If the second argument (the locales string) is undefined
, then it's assumed to be whatever is the system default. If it is anything other than undefined
, then it must be a string with proper locale information, or else that's an error.
edit — as noted in a comment, the locales argument can also be an already-initialized locale list object. However, it's still true that undefined
will work, while null
will not.
CodePudding user response:
String#localeCompare
takes three parameters:
Parameter name | Expected value |
---|---|
compareString |
The string against which the referenceStr is compared. |
locales and options |
These arguments customize the behavior of the function and let applications specify the language whose formatting conventions should be used. In implementations which ignore the locales and options arguments, the locale used and the form of the string returned are entirely implementation-dependent.See the Intl.Collator() constructor for details on these parameters and how to use them. |
When you pass a value for the locales
parameter, it's passed to the Intl.Collator
constructor listed above whose documentation is available here. The steps taken to construct an Intl.Collator
include:
- Return ? InitializeCollator(
collator
,locales
,options
).
The documentation for InitializeCollator
includes the step:
- Let
requestedLocales
be ? CanonicalizeLocaleList(locales
).
Here's the actual answer, then. The documentation for CanonicalizeLocaleList
includes the steps for handling the value you passed to locales
all the way back in String#localeCompare
:
- If
locales
isundefined
, then
a. Return a new emptyList
. - [...]
- If Type(
locales
) isString
or Type(locales
) isObject
andlocales
has an[[InitializedLocale]]
internal slot, then
a. LetO
be ! CreateArrayFromList(«locales
»). - Else
a. LetO
be ? ToObject(locales
). - [...]
- [...]
- [...]
- [...]
Looking at the documentation for ToObject(argument)
:
Argument Type | Result |
---|---|
Undefined | Throw a TypeError exception. |
Null | Throw a TypeError exception. |
Boolean | Return a new Boolean object whose [[BooleanData]] internal slot is set to argument . See 20.3 for a description of Boolean objects. |
Number | Return a new Number object whose [[NumberData]] internal slot is set to argument . See 21.1 for a description of Number objects. |
String | Return a new String object whose [[StringData]] internal slot is set to argument . See 22.1 for a description of String objects. |
Symbol | Return a new Symbol object whose [[SymbolData]] internal slot is set to argument . See 20.4 for a description of Symbol objects. |
BigInt | Return a new BigInt object whose [[BigIntData]] internal slot is set to argument . See 21.2 for a description of BigInt objects. |
Object | Return argument . |
To summarize, if you pass:
undefined
localeCompare
passes to Intl.Collator().constructor
which passes to InitializeCollator
which passes to CanonicalizeLocaleList
which has an explicit step that returns a new empty List
.
null
localeCompare
passes to Intl.Collator().constructor
which passes to InitializeCollator
which passes to CanonicalizeLocaleList
which calls ToObject
which throws a TypeError
if it's passed null
.
You asked about two other cases in the comments:
{}
(empty object)
localeCompare
passes to Intl.Collator().constructor
which passes to InitializeCollator
which passes to CanonicalizeLocaleList
which calls ToObject
which returns the object it is passed.
Note that CanonicalizeLocaleList
would have handled the object if it had an [[InitializedLocale]]
internal slot.
''
(empty string)
localeCompare
passes to Intl.Collator().constructor
which passes to InitializeCollator
which passes to CanonicalizeLocaleList
which calls ToObject
which returns a new String
object which a length of zero so the rest of CanonicalizeLocaleList
has no effect and an empty List
is returned.
I'll admit, I don't know why the empty string case doesn't work. There's probably something deeper in the spec that handles that case.