EN
JavaScript - reverse string that contains emoji, surrogate characters and Asian characters
5
points
In this short article, we would like to show you the correct way to reverse strings that may contain emoji, surrogate characters, and Asian characters using JavaScript.
Quick solution:
// ONLINE-RUNNER:browser;
const text = 'Hi there ! 💻🙂😀😋';
const reversion = '\u202e' + text + '\u202c'; // \u202c used to avoid affecting the following text
console.log(reversion);
Warning: the above solution only diplsays text as reversed - go to next seaction to see characters real reversion.
Real reversion examples
Example 1:
In this section, you can find source code that makes reversion on some characters to get reversed string.
// ONLINE-RUNNER:browser;
function isHighSurrogate(code) {
return code >= 0xD800 && code <= 0xDBFF;
}
function isLowSurrogate(code) {
return code >= 0xDC00 && code <= 0xDFFF;
}
function isVariationSelectors(code) {
return code >= 0xFE00 && code <= 0xFE0F;
}
function isCombiningMark(code) {
return code >= 0x0300 && code <= 0x036F ||
code >= 0x1AB0 && code <= 0x1AFF ||
code >= 0x1DC0 && code <= 0x1DFF ||
code >= 0x20D0 && code <= 0x20FF ||
code >= 0xFE20 && code <= 0xFE2F;
}
function reverseString(text) {
let result = '';
for (var i = 0; i < text.length; i += 1) {
var a = text.charCodeAt(i); // potential high surrogate
var character;
if (isHighSurrogate(a)) {
if (i + 1 < text.length) {
var b = text.charCodeAt(i + 1); // potential low surrogate
if (isLowSurrogate(b)) {
i += 1;
character = String.fromCharCode(a, b);
} else {
character = String.fromCharCode(a);
}
} else {
break;
}
} else {
character = String.fromCharCode(a);
while (i + 1 < text.length) {
var c = text.charCodeAt(i + 1); // potential combining mark or variation selector
if (isCombiningMark(c) || isVariationSelectors(c)) {
i += 1;
character += String.fromCharCode(c);
} else {
break;
}
}
}
result = character + result;
}
return result;
}
// Usage example:
// -- e.g. 1
var text1 = 'Example text ...';
var reversion1 = reverseString(text1);
console.log(text1); // Example text ...
console.log(reversion1); // ... txet elpmaxE
console.log();
// -- e.g. 2
var text2 = '❤️💻🙂😍😊😀🤑😁😋🤗';
var reversion2 = reverseString(text2);
console.log(text2); // ❤️💻🙂😍😊😀🤑😁😋🤗
console.log(reversion2); // 🤗😋😁🤑😀😊😍🙂💻❤️
console.log();
// -- e.g. 3
var data = [
{
// Nothing special
'input': 'ma\xF1ana',
'expected': 'ana\xF1am'
},
{
// Combining mark
'input': 'man\u0303ana',
'expected': 'anan\u0303am'
},
{
// Multiple combining marks
'input': 'foo\u0303\u035C\u035D\u035Ebar',
'expected': 'rabo\u0303\u035C\u035D\u035Eof'
},
{
// Astral symbol (surrogate pair)
'input': 'foo\uD834\uDF06bar',
'expected': 'rab\uD834\uDF06oof'
},
{
// Unpaired surrogates
'input': 'foo\uD834bar\uDF06baz',
'expected': 'zab\uDF06rab\uD834oof'
},
{
// Zalgo
'input': 'H\u0339\u0319\u0326\u032E\u0349\u0329\u0317\u0317\u0367\u0307\u030F\u030A\u033EE\u0368\u0346\u0352\u0306\u036E\u0303\u034F\u0337\u032E\u0323\u032B\u0324\u0323 \u0335\u031E\u0339\u033B\u0300\u0309\u0313\u036C\u0351\u0361\u0345C\u036F\u0302\u0350\u034F\u0328\u031B\u0354\u0326\u031F\u0348\u033BO\u031C\u034E\u034D\u0359\u035A\u032C\u031D\u0323\u033D\u036E\u0350\u0357\u0300\u0364\u030D\u0300\u0362M\u0334\u0321\u0332\u032D\u034D\u0347\u033C\u031F\u032F\u0326\u0309\u0312\u0360\u1E1A\u031B\u0319\u031E\u032A\u0317\u0365\u0364\u0369\u033E\u0351\u0314\u0350\u0345\u1E6E\u0334\u0337\u0337\u0317\u033C\u034D\u033F\u033F\u0313\u033D\u0350H\u0319\u0319\u0314\u0304\u035C',
'expected': 'H\u0319\u0319\u0314\u0304\u035C\u1E6E\u0334\u0337\u0337\u0317\u033C\u034D\u033F\u033F\u0313\u033D\u0350\u1E1A\u031B\u0319\u031E\u032A\u0317\u0365\u0364\u0369\u033E\u0351\u0314\u0350\u0345M\u0334\u0321\u0332\u032D\u034D\u0347\u033C\u031F\u032F\u0326\u0309\u0312\u0360O\u031C\u034E\u034D\u0359\u035A\u032C\u031D\u0323\u033D\u036E\u0350\u0357\u0300\u0364\u030D\u0300\u0362C\u036F\u0302\u0350\u034F\u0328\u031B\u0354\u0326\u031F\u0348\u033B \u0335\u031E\u0339\u033B\u0300\u0309\u0313\u036C\u0351\u0361\u0345E\u0368\u0346\u0352\u0306\u036E\u0303\u034F\u0337\u032E\u0323\u032B\u0324\u0323H\u0339\u0319\u0326\u032E\u0349\u0329\u0317\u0317\u0367\u0307\u030F\u030A\u033E'
}
];
for (var i = 0; i < data.length; ++i) {
var item = data[i];
var reversion = reverseString(item.input);
console.log(item.input + ' ---> ' + reversion + '\n\n');
}
Example 2:
In this section, you can find source code that uses Intl.Segmenter
class to get reversed string.
Note:
The
Intl.Segmenter
was added in ECMAScript 2023.
// ONLINE-RUNNER:browser;
const createReversor = (locales) => {
const segmenter = new Intl.Segmenter(locales, {
granularity: "grapheme"
});
return (text) => {
const iterator = segmenter.segment(text)
let result = '';
for (let {segment} of iterator) {
result = segment + result;
}
return result;
}
};
// Usage example:
const reverseString = createReversor('en');
// -- e.g. 1
var text1 = 'Example text ...';
var reversion1 = reverseString(text1);
console.log(text1); // Example text ...
console.log(reversion1); // ... txet elpmaxE
console.log();
// -- e.g. 2
var text2 = '❤️💻🙂😍😊😀🤑😁😋🤗';
var reversion2 = reverseString(text2);
console.log(text2); // ❤️💻🙂😍😊😀🤑😁😋🤗
console.log(reversion2); // 🤗😋😁🤑😀😊😍🙂💻❤️
console.log();
// -- e.g. 3
var data = [
{
// Nothing special
'input': 'ma\xF1ana',
'expected': 'ana\xF1am'
},
{
// Combining mark
'input': 'man\u0303ana',
'expected': 'anan\u0303am'
},
{
// Multiple combining marks
'input': 'foo\u0303\u035C\u035D\u035Ebar',
'expected': 'rabo\u0303\u035C\u035D\u035Eof'
},
{
// Astral symbol (surrogate pair)
'input': 'foo\uD834\uDF06bar',
'expected': 'rab\uD834\uDF06oof'
},
{
// Unpaired surrogates
'input': 'foo\uD834bar\uDF06baz',
'expected': 'zab\uDF06rab\uD834oof'
},
{
// Zalgo
'input': 'H\u0339\u0319\u0326\u032E\u0349\u0329\u0317\u0317\u0367\u0307\u030F\u030A\u033EE\u0368\u0346\u0352\u0306\u036E\u0303\u034F\u0337\u032E\u0323\u032B\u0324\u0323 \u0335\u031E\u0339\u033B\u0300\u0309\u0313\u036C\u0351\u0361\u0345C\u036F\u0302\u0350\u034F\u0328\u031B\u0354\u0326\u031F\u0348\u033BO\u031C\u034E\u034D\u0359\u035A\u032C\u031D\u0323\u033D\u036E\u0350\u0357\u0300\u0364\u030D\u0300\u0362M\u0334\u0321\u0332\u032D\u034D\u0347\u033C\u031F\u032F\u0326\u0309\u0312\u0360\u1E1A\u031B\u0319\u031E\u032A\u0317\u0365\u0364\u0369\u033E\u0351\u0314\u0350\u0345\u1E6E\u0334\u0337\u0337\u0317\u033C\u034D\u033F\u033F\u0313\u033D\u0350H\u0319\u0319\u0314\u0304\u035C',
'expected': 'H\u0319\u0319\u0314\u0304\u035C\u1E6E\u0334\u0337\u0337\u0317\u033C\u034D\u033F\u033F\u0313\u033D\u0350\u1E1A\u031B\u0319\u031E\u032A\u0317\u0365\u0364\u0369\u033E\u0351\u0314\u0350\u0345M\u0334\u0321\u0332\u032D\u034D\u0347\u033C\u031F\u032F\u0326\u0309\u0312\u0360O\u031C\u034E\u034D\u0359\u035A\u032C\u031D\u0323\u033D\u036E\u0350\u0357\u0300\u0364\u030D\u0300\u0362C\u036F\u0302\u0350\u034F\u0328\u031B\u0354\u0326\u031F\u0348\u033B \u0335\u031E\u0339\u033B\u0300\u0309\u0313\u036C\u0351\u0361\u0345E\u0368\u0346\u0352\u0306\u036E\u0303\u034F\u0337\u032E\u0323\u032B\u0324\u0323H\u0339\u0319\u0326\u032E\u0349\u0329\u0317\u0317\u0367\u0307\u030F\u030A\u033E'
}
];
for (var i = 0; i < data.length; ++i) {
var item = data[i];
var reversion = reverseString(item.input);
console.log(item.input + ' ---> ' + reversion + '\n\n');
}