Для плохой реализации сортировки с правильным сопоставлением около на стороне клиента мне нужна функция JavaScript, которая выполняет замену одного символа эффективный в строке.
Вот что я имею в виду (обратите внимание, что это относится к немецкому тексту, другие языки сортируются по-другому):
native sorting gets it wrong: a b c o u z ä ö ü collation-correct would be: a ä b c o ö u ü z
По сути, мне нужно, чтобы все вхождения «ä» в данной строке заменялись на «а» (и так далее). Таким образом, результат собственной сортировки будет очень близок к тому, что ожидает пользователь (или к тому, что вернет база данных).
В других языках есть средства для этого: Python поставляет str.translate(), в Perl есть tr/…/…/, XPath имеет функцию translate(), ColdFusion имеет ReplaceList(). Но как насчет JavaScript?
Вот что у меня есть прямо сейчас.
// s would be a rather short string (something like
// 200 characters at max, most of the time much less)
function makeSortString(s) {
var translate = {
"ä": "a", "ö": "o", "ü": "u",
"Ä": "A", "Ö": "O", "Ü": "U" // probably more to come
};
var translate_re = /[öäüÖÄÜ]/g;
return ( s.replace(translate_re, function(match) {
return translate[match];
}) );
}
Во-первых, мне не нравится, что регулярное выражение перестраивается каждый раз, когда я вызываю функцию. Я думаю, что закрытие может помочь в этом отношении, но я, кажется, по какой-то причине не разбираюсь в этом.
Кто-нибудь может придумать что-нибудь более эффективное?
String#localeCompare, который теперь является широко поддерживаемый среди JS-движков (не так много во время вопроса) и мог бы решить эту категорию проблем гораздо более элегантно.Я знаю. Решение предназначалось для сортировки немецкого текста. Даже там это не правильный, но достаточно хорошее для варианта использования. Этот вопрос никогда не задумывался как поиск алгоритма «решает все проблемы».
Я немного перефразировал вопрос, чтобы прояснить это с самого начала.
@Tomalak: Я нашел ваш вопрос, когда переходил по ссылке из другого вопроса о «u» и «ü», и мне пришлось возразить. Но поскольку вы сейчас пояснили, что это было для немецкого языка, мне больше нечего возражать.
@some: Я предпочитаю короткое обсуждение в комментариях, а не голосование "против" в любое время. К сожалению, здесь есть люди, которые сначала голосуют против, а потом задают вопросы (если вообще). Следствие: ваш комментарий был оценен по достоинству. :)
@Tomalak: К сожалению, есть люди, которые голосуют против без всякой видимой причины, даже правильные ответы и кристально ясные вопросы ... Интересно, заметили ли они, что сами теряют 1 балл? Я предпочитаю оставить комментарий, чтобы автор мог внести пояснения / исправления. ИМХО, что лучше.
Просто чтобы вы знали, у меня есть вилка tableorter, в которой я модифицировал оригиналsortLocaleCompare вариант, чтобы автоматически заменять эти строки с диакритическими знаками; пожалуйста, смотрите эта демонстрация для более подробной информации. Если это все еще не работает для вас, попробуйте эта демонстрация, который заменяет сортировщик текста по умолчанию на sugar.js.
Спасибо @Mottie. (Я считаю, что sortLocaleCompare даже не существовало, когда я впервые написал этот вопрос.)
На самом деле он существовал в v2.0.5 (недокументированный), и все, что он делал, это return a.localeCompare(b); внутри сортировки.
см. более свежее решение stackoverflow.com/a/18391901/759452
Есть пакет NPM, который делает именно это, github.com/andrewrk/node-diacritics.
Отвечает ли это на ваш вопрос? Удаление диакритических знаков и диакритических знаков в строке в JavaScript



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я не могу говорить о том, что вы пытаетесь сделать конкретно с самой функцией, но если вам не нравится, что регулярное выражение создается каждый раз, вот два решения и некоторые предостережения по каждому из них.
Вот один из способов сделать это:
function makeSortString(s) {
if (!makeSortString.translate_re) makeSortString.translate_re = /[öäüÖÄÜ]/g;
var translate = {
"ä": "a", "ö": "o", "ü": "u",
"Ä": "A", "Ö": "O", "Ü": "U" // probably more to come
};
return ( s.replace(makeSortString.translate_re, function(match) {
return translate[match];
}) );
}
Это, очевидно, сделает регулярное выражение свойством самой функции. Единственное, что вам может не понравиться в этом (или вы можете, я думаю, это зависит), это то, что регулярное выражение теперь может быть изменено за пределами тела функции. Итак, кто-то может сделать это, чтобы изменить внутреннее используемое регулярное выражение:
makeSortString.translate_re = /[a-z]/g;
Так что вариант есть.
Один из способов получить закрытие и, таким образом, предотвратить изменение регулярного выражения кем-либо, - это определить это как присвоение анонимной функции следующим образом:
var makeSortString = (function() {
var translate_re = /[öäüÖÄÜ]/g;
return function(s) {
var translate = {
"ä": "a", "ö": "o", "ü": "u",
"Ä": "A", "Ö": "O", "Ü": "U" // probably more to come
};
return ( s.replace(translate_re, function(match) {
return translate[match];
}) );
}
})();
Надеюсь, это будет вам полезно.
ОБНОВЛЕНИЕ: это рано, и я не знаю, почему я не видел очевидного раньше, но также может быть полезно поместить объект translate в закрытие:
var makeSortString = (function() {
var translate_re = /[öäüÖÄÜ]/g;
var translate = {
"ä": "a", "ö": "o", "ü": "u",
"Ä": "A", "Ö": "O", "Ü": "U" // probably more to come
};
return function(s) {
return ( s.replace(translate_re, function(match) {
return translate[match];
}) );
}
})();
Я пытаюсь сделать так, чтобы сортировка плагина jQuery tablesorter работала правильно для табличных данных на немецком языке. Плагин может использовать определяемую пользователем функцию для извлечения строки для сортировки, что мне и нужно сделать, иначе полученный порядок сортировки будет неправильным.
Неужели эта функция настолько неэффективна? Что вы сделали с тестированием?
Я не хотел сказать, что моя реализация была неэффективной. Это почти самый эффективный способ сделать это, о чем я могу думать. Но я не могу придумать все, поэтому я надеялся, что существует какой-то действительно умный способ манипулирования строками, о котором я не подозревал.
Понятно - думаю, вашего решения достаточно; поскольку я мог увидеть применение этой функции в долгосрочной перспективе, я провел базовое тестирование. Я сделал 5000 итераций для строки из 200 символов, содержащей хотя бы один из этих символов, каждые 8 символов, и это заняло около 500 мс.
Кстати, это тестирование проводилось в FF. В Chrome это работало примерно так же; поскольку JS-движок Chrome (V8) быстрее, в общем, стоит отметить этот факт, FWIW.
Спасибо за ваше время, очень признателен. Я проведу собственное тестирование (но не сегодня, здесь 18:00) и опубликую здесь свои выводы.
На самом деле мне никогда не приходилось писать тестовый пример для сравнения результатов. Я оставил это открытым как напоминание, что когда-нибудь это сделаю, но было бы несправедливо не принять ответ, который я использовал, поэтому я делаю это сейчас. Извините за столь долгую задержку.
Не беспокойтесь - надеюсь, я был вам полезен!
Ты был. ;-) Я выложил то, что у меня сейчас работает, может кому-то это пригодится.
Я воспользовался ответом этого человека на аналогичную проблему: stackoverflow.com/a/5912746/81633
Кодировка для венгерского регулярного выражения: var translate_re = /[éáűőúöüóíÉÁŰPŐÚÖÜÓÍ]/g; var translate = { "é": "e", "á": "a", "ű": "u", "ő": "o", "ú": "u", "ö": "o", "ü": "u", "ó": "o", "í": "i", "É": "E", "Á": "A", "Ű": "U", "Ő": "O", "Ú": "U", "Ö": "O", "Ü": "U", "Ó": "O", "Í": "I" };
Кодировка для регулярного выражения румынского языка: var translate_re = /[șțăîâȘȚĂÎÂ]/g; var translate = { "ș": "s", "ț": "t", "ă": "a", "î": "i", "â": "a", "Ș": "S", "Ț": "T", "Ă": "A", "Î": "I", "Â": "A" };
Вот что я использую на основе решения Джейсона Бантинга.
Все это для подключаемый модуль jQuery tablesorter: для (почти правильной) сортировки неанглийских таблиц с помощью плагина tableorter необходимо использовать настраиваемый textExtraction функция.
Этот:
'dd.mm.yyyy') на распознанный формат ('yyyy-mm-dd')Будьте осторожны, сохраняя файл JavaScript в кодировке UTF-8, иначе он не сработает.
// file encoding must be UTF-8!
function getTextExtractor()
{
return (function() {
var patternLetters = /[öäüÖÄÜáàâéèêúùûóòôÁÀÂÉÈÊÚÙÛÓÒÔß]/g;
var patternDateDmy = /^(?:\D+)?(\d{1,2})\.(\d{1,2})\.(\d{2,4})$/;
var lookupLetters = {
"ä": "a", "ö": "o", "ü": "u",
"Ä": "A", "Ö": "O", "Ü": "U",
"á": "a", "à": "a", "â": "a",
"é": "e", "è": "e", "ê": "e",
"ú": "u", "ù": "u", "û": "u",
"ó": "o", "ò": "o", "ô": "o",
"Á": "A", "À": "A", "Â": "A",
"É": "E", "È": "E", "Ê": "E",
"Ú": "U", "Ù": "U", "Û": "U",
"Ó": "O", "Ò": "O", "Ô": "O",
"ß": "s"
};
var letterTranslator = function(match) {
return lookupLetters[match] || match;
}
return function(node) {
var text = $.trim($(node).text());
var date = text.match(patternDateDmy);
if (date)
return [date[3], date[2], date[1]].join("-");
else
return text.replace(patternLetters, letterTranslator);
}
})();
}
Вы можете использовать это так:
$("table.sortable").tablesorter({
textExtraction: getTextExtractor()
});
Не знаю, увидит ли кто-нибудь мой комментарий, но мне нужна та же функция для некоторых акцентированных букв на португальском языке, и мне не удается заставить ее работать. Должны ли соответствующие буквы в моем php-файле вызываться «html-кодом»: & Iacute; или просто набрав букву «Í»? Пробовал оба, ничего не работает. И да, я изменил функцию js в соответствии со своими потребностями с помощью букв Í и í, а мой js закодирован в utf-8.
@kevin: Конечно, кто-то заметил комментарий. ;-) Символ в вашем HTML (который, как я полагаю, создается этим файлом PHP) может быть Í или фактическим Í. Это не имеет значения, если настройки кодировки верны (фактическая кодировка файла PHP, кодировка файла, воспринимаемая сервером PHP, заголовок HTTP Content-Type, метатеги HTML). Использование объекта HTML может быть самым безопасным. Если файл .js имеет кодировку UTF-8, он должен обслуживаться как таковой (text/javascript; Charset=UTF-8), тогда все должно быть в порядке.
Спасибо, что заметили ;-), я проверял и пробовал разными способами то, что вы сказали, просто не получается. Может ли это быть из-за того, что другие файлы js вызываются на той же странице php? Если вы хотите взглянуть, это здесь: schulz-al.tempsite.ws/br/?page_id=51. Спасибо за помощь, оценил.
@kevin: Кстати, проверьте свои ссылки на sitemap-up.gif и sitemap-down.gif, я получаю для них 401 доступ запрещен.
@kevin: Следующее: ваши скрипты обслуживаются как Content-Type: text/html без параметра Charset. Они должны быть как минимум Content-Type: text/javascript;. Кроме того, ваш метод GetTextExtractor() (тот, что есть в jquery.tablesorter.min.js) довольно сильно отличается от моей функции, и вы не знаете, почему вы думаете, что ваш метод может работать. ;-) Совет: поместите средство извлечения текста в scripts.js, а не в код плагина tableorter. Не трогайте код плагина, чтобы избежать головной боли в будущем.
Да уж видел изображения п.б., решил. Что касается <code> Content-Type: text / javascript </code>, все мои скрипты вызываются таким образом <code> <script type = "text / javascript" src = "<? Php bloginfo ('template_url'); ?> / js / jquery.tablesorter.min.js "charset = " utf-8 "> </script> </code>, поэтому я не понимаю, что вы имеете в виду. Я просто сделал то, что вы предлагали, скопировал обратно ваш js-код в новый файл scripts.js и добавил мои буквы «Í» и «í», по-прежнему ничего, схожу с ума и чувствую себя тупым. В любом случае большое спасибо за помощь.
@kevin: Мне жаль говорить, что есть причина чувствовать себя глупо. ;-) Вы скопировали мой код $("table.sortable").tablesorter(…);, но на самом деле ваш стол - это $("table.tablesorter"). Также нет необходимости повторно звонить в tablesorter(). Как только вы внесете это изменение, оно будет работать - я только что протестировал с помощью FireBug.
Омг, это действительно было довольно глупо ... Большое спасибо за помощь, Томалак, очень признателен, теперь он отлично работает. Мне также пришлось вызвать извлечение символов <code> {textExtraction: GetTextExtractor ()} </code> после вызова виджета зебры <code> $ .tablesorter.defaults.widgets = ['zebra']; </code>, чтобы получить все это вместе. Еще раз спасибо!
@kevin: Рад слышать, что это все-таки сработало. :) P.S .: Я был бы признателен за ответное голосование. ;)
Конечно, как я могу это сделать? (это щелчок по значку флажка "это отличный комментарий"?)
@kevin: Нет, нажимая кнопки голосования в верхнем левом углу ответа. ;) За комментарии тоже можно проголосовать, но только голоса за вопросы или ответы создают репутацию, которая является основной валютой этого сайта. ;)
Отличный код! Может быть, вы могли бы сделать из этого GitHub Gist?
У меня все еще была проблема с сортировкой, например: Šalat, Sup. Это неправильный порядок, поэтому я сделал что-то вроде этого - «Š»: «Szz», «š»: «szz», это должно быть почти на 100% эффективным.
Я сделал прототипную версию этого:
String.prototype.strip = function() {
var translate_re = /[öäüÖÄÜß ]/g;
var translate = {
"ä":"a", "ö":"o", "ü":"u",
"Ä":"A", "Ö":"O", "Ü":"U",
" ":"_", "ß":"ss" // probably more to come
};
return (this.replace(translate_re, function(match){
return translate[match];})
);
};
Используйте как:
var teststring = 'ä ö ü Ä Ö Ü ß';
teststring.strip();
Это изменит строку на a_o_u_A_O_U_ss
это не работает. Однако, если я использую var newstr = teststring.strip(); и console.info(), тогда он работает - jsfiddle. Спасибо, мужик, это самый лаконичный и читаемый метод.
Я думаю, что это могло бы работать немного чище / лучше (хотя я не тестировал его производительность):
String.prototype.stripAccents = function() {
var translate_re = /[àáâãäçèéêëìíîïñòóôõöùúûüýÿÀÁÂÃÄÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝ]/g;
var translate = 'aaaaaceeeeiiiinooooouuuuyyAAAAACEEEEIIIINOOOOOUUUUY';
return (this.replace(translate_re, function(match){
return translate.substr(translate_re.source.indexOf(match)-1, 1); })
);
};
Или, если вы все еще слишком беспокоитесь о производительности, давайте возьмем лучшее из обоих миров:
String.prototype.stripAccents = function() {
var in_chrs = 'àáâãäçèéêëìíîïñòóôõöùúûüýÿÀÁÂÃÄÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝ',
out_chrs = 'aaaaaceeeeiiiinooooouuuuyyAAAAACEEEEIIIINOOOOOUUUUY',
transl = {};
eval('var chars_rgx = /['+in_chrs+']/g');
for(var i = 0; i < in_chrs.length; i++){ transl[in_chrs.charAt(i)] = out_chrs.charAt(i); }
return this.replace(chars_rgx, function(match){
return transl[match]; });
};
РЕДАКТИРОВАТЬ (автор @Tomalak)
Я ценю идею. Однако есть несколько ошибок в реализации, как указано в комментарии ниже.
Вот как я бы это реализовал.
var stripAccents = (function () {
var in_chrs = 'àáâãäçèéêëìíîïñòóôõöùúûüýÿÀÁÂÃÄÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝ',
out_chrs = 'aaaaaceeeeiiiinooooouuuuyyAAAAACEEEEIIIINOOOOOUUUUY',
chars_rgx = new RegExp('[' + in_chrs + ']', 'g'),
transl = {}, i,
lookup = function (m) { return transl[m] || m; };
for (i=0; i<in_chrs.length; i++) {
transl[ in_chrs[i] ] = out_chrs[i];
}
return function (s) { return s.replace(chars_rgx, lookup); }
})();
Почему вы думаете, что это работает лучше? Я предполагаю, что поиск объекта на много быстрее, чем String.indexOf().
Tomalak, я добавил еще один способ сделать это, который объединяет лучшее из обоих миров (читаемость и производительность), я мог бы в конечном итоге сделать еще один шаг и кэшировать объект char_rgx, но я не думаю, что это имеет большой смысл, если только при работе с точность в реальном времени ...
Извините, но в этом коде есть несколько ошибок. Во-первых, ненадлежащее использование eval(). Для этого есть new RegExp(). Во-вторых, он изменяет прототип String. Модификация встроенных типов данных очень не одобряется. В-третьих, функция запускает цикл для каждого символа при каждом вызове. Это то, чего я пытался избежать в первую очередь. Это означает, что он исправляет удобочитаемость за счет производительности, что я считаю плохим компромиссом. Я ценю идею, но исполнение не оптимально. :)
@Tomalak, это отличный способ сделать это! Мне просто интересно, почему вы возвращаете функцию вместо того, чтобы передавать "s" в первую очередь var stripAccents = function(s){ var in_chrs = ... }? jsfiddle
Поскольку возвращение функции закрывает переменные и функцию во внешней области видимости, поэтому их не нужно переопределять каждый раз при вызове stripAccents(). Смотрите закрытия.
Вот более полная версия, основанная на стандарте Unicode, взятая отсюда: http://semplicewebsites.com/removing-accents-javascript
var Latinise = {};Latinise.latin_map = {"Á":"A",
"Ă":"A",
"Ắ":"A",
"Ặ":"A",
"Ằ":"A",
"Ẳ":"A",
"Ẵ":"A",
"Ǎ":"A",
"Â":"A",
"Ấ":"A",
"Ậ":"A",
"Ầ":"A",
"Ẩ":"A",
"Ẫ":"A",
"Ä":"A",
"Ǟ":"A",
"Ȧ":"A",
"Ǡ":"A",
"Ạ":"A",
"Ȁ":"A",
"À":"A",
"Ả":"A",
"Ȃ":"A",
"Ā":"A",
"Ą":"A",
"Å":"A",
"Ǻ":"A",
"Ḁ":"A",
"Ⱥ":"A",
"Ã":"A",
"Ꜳ":"AA",
"Æ":"AE",
"Ǽ":"AE",
"Ǣ":"AE",
"Ꜵ":"AO",
"Ꜷ":"AU",
"Ꜹ":"AV",
"Ꜻ":"AV",
"Ꜽ":"AY",
"Ḃ":"B",
"Ḅ":"B",
"Ɓ":"B",
"Ḇ":"B",
"Ƀ":"B",
"Ƃ":"B",
"Ć":"C",
"Č":"C",
"Ç":"C",
"Ḉ":"C",
"Ĉ":"C",
"Ċ":"C",
"Ƈ":"C",
"Ȼ":"C",
"Ď":"D",
"Ḑ":"D",
"Ḓ":"D",
"Ḋ":"D",
"Ḍ":"D",
"Ɗ":"D",
"Ḏ":"D",
"Dz":"D",
"Dž":"D",
"Đ":"D",
"Ƌ":"D",
"DZ":"DZ",
"DŽ":"DZ",
"É":"E",
"Ĕ":"E",
"Ě":"E",
"Ȩ":"E",
"Ḝ":"E",
"Ê":"E",
"Ế":"E",
"Ệ":"E",
"Ề":"E",
"Ể":"E",
"Ễ":"E",
"Ḙ":"E",
"Ë":"E",
"Ė":"E",
"Ẹ":"E",
"Ȅ":"E",
"È":"E",
"Ẻ":"E",
"Ȇ":"E",
"Ē":"E",
"Ḗ":"E",
"Ḕ":"E",
"Ę":"E",
"Ɇ":"E",
"Ẽ":"E",
"Ḛ":"E",
"Ꝫ":"ET",
"Ḟ":"F",
"Ƒ":"F",
"Ǵ":"G",
"Ğ":"G",
"Ǧ":"G",
"Ģ":"G",
"Ĝ":"G",
"Ġ":"G",
"Ɠ":"G",
"Ḡ":"G",
"Ǥ":"G",
"Ḫ":"H",
"Ȟ":"H",
"Ḩ":"H",
"Ĥ":"H",
"Ⱨ":"H",
"Ḧ":"H",
"Ḣ":"H",
"Ḥ":"H",
"Ħ":"H",
"Í":"I",
"Ĭ":"I",
"Ǐ":"I",
"Î":"I",
"Ï":"I",
"Ḯ":"I",
"İ":"I",
"Ị":"I",
"Ȉ":"I",
"Ì":"I",
"Ỉ":"I",
"Ȋ":"I",
"Ī":"I",
"Į":"I",
"Ɨ":"I",
"Ĩ":"I",
"Ḭ":"I",
"Ꝺ":"D",
"Ꝼ":"F",
"Ᵹ":"G",
"Ꞃ":"R",
"Ꞅ":"S",
"Ꞇ":"T",
"Ꝭ":"IS",
"Ĵ":"J",
"Ɉ":"J",
"Ḱ":"K",
"Ǩ":"K",
"Ķ":"K",
"Ⱪ":"K",
"Ꝃ":"K",
"Ḳ":"K",
"Ƙ":"K",
"Ḵ":"K",
"Ꝁ":"K",
"Ꝅ":"K",
"Ĺ":"L",
"Ƚ":"L",
"Ľ":"L",
"Ļ":"L",
"Ḽ":"L",
"Ḷ":"L",
"Ḹ":"L",
"Ⱡ":"L",
"Ꝉ":"L",
"Ḻ":"L",
"Ŀ":"L",
"Ɫ":"L",
"Lj":"L",
"Ł":"L",
"LJ":"LJ",
"Ḿ":"M",
"Ṁ":"M",
"Ṃ":"M",
"Ɱ":"M",
"Ń":"N",
"Ň":"N",
"Ņ":"N",
"Ṋ":"N",
"Ṅ":"N",
"Ṇ":"N",
"Ǹ":"N",
"Ɲ":"N",
"Ṉ":"N",
"Ƞ":"N",
"Nj":"N",
"Ñ":"N",
"NJ":"NJ",
"Ó":"O",
"Ŏ":"O",
"Ǒ":"O",
"Ô":"O",
"Ố":"O",
"Ộ":"O",
"Ồ":"O",
"Ổ":"O",
"Ỗ":"O",
"Ö":"O",
"Ȫ":"O",
"Ȯ":"O",
"Ȱ":"O",
"Ọ":"O",
"Ő":"O",
"Ȍ":"O",
"Ò":"O",
"Ỏ":"O",
"Ơ":"O",
"Ớ":"O",
"Ợ":"O",
"Ờ":"O",
"Ở":"O",
"Ỡ":"O",
"Ȏ":"O",
"Ꝋ":"O",
"Ꝍ":"O",
"Ō":"O",
"Ṓ":"O",
"Ṑ":"O",
"Ɵ":"O",
"Ǫ":"O",
"Ǭ":"O",
"Ø":"O",
"Ǿ":"O",
"Õ":"O",
"Ṍ":"O",
"Ṏ":"O",
"Ȭ":"O",
"Ƣ":"OI",
"Ꝏ":"OO",
"Ɛ":"E",
"Ɔ":"O",
"Ȣ":"OU",
"Ṕ":"P",
"Ṗ":"P",
"Ꝓ":"P",
"Ƥ":"P",
"Ꝕ":"P",
"Ᵽ":"P",
"Ꝑ":"P",
"Ꝙ":"Q",
"Ꝗ":"Q",
"Ŕ":"R",
"Ř":"R",
"Ŗ":"R",
"Ṙ":"R",
"Ṛ":"R",
"Ṝ":"R",
"Ȑ":"R",
"Ȓ":"R",
"Ṟ":"R",
"Ɍ":"R",
"Ɽ":"R",
"Ꜿ":"C",
"Ǝ":"E",
"Ś":"S",
"Ṥ":"S",
"Š":"S",
"Ṧ":"S",
"Ş":"S",
"Ŝ":"S",
"Ș":"S",
"Ṡ":"S",
"Ṣ":"S",
"Ṩ":"S",
"Ť":"T",
"Ţ":"T",
"Ṱ":"T",
"Ț":"T",
"Ⱦ":"T",
"Ṫ":"T",
"Ṭ":"T",
"Ƭ":"T",
"Ṯ":"T",
"Ʈ":"T",
"Ŧ":"T",
"Ɐ":"A",
"Ꞁ":"L",
"Ɯ":"M",
"Ʌ":"V",
"Ꜩ":"TZ",
"Ú":"U",
"Ŭ":"U",
"Ǔ":"U",
"Û":"U",
"Ṷ":"U",
"Ü":"U",
"Ǘ":"U",
"Ǚ":"U",
"Ǜ":"U",
"Ǖ":"U",
"Ṳ":"U",
"Ụ":"U",
"Ű":"U",
"Ȕ":"U",
"Ù":"U",
"Ủ":"U",
"Ư":"U",
"Ứ":"U",
"Ự":"U",
"Ừ":"U",
"Ử":"U",
"Ữ":"U",
"Ȗ":"U",
"Ū":"U",
"Ṻ":"U",
"Ų":"U",
"Ů":"U",
"Ũ":"U",
"Ṹ":"U",
"Ṵ":"U",
"Ꝟ":"V",
"Ṿ":"V",
"Ʋ":"V",
"Ṽ":"V",
"Ꝡ":"VY",
"Ẃ":"W",
"Ŵ":"W",
"Ẅ":"W",
"Ẇ":"W",
"Ẉ":"W",
"Ẁ":"W",
"Ⱳ":"W",
"Ẍ":"X",
"Ẋ":"X",
"Ý":"Y",
"Ŷ":"Y",
"Ÿ":"Y",
"Ẏ":"Y",
"Ỵ":"Y",
"Ỳ":"Y",
"Ƴ":"Y",
"Ỷ":"Y",
"Ỿ":"Y",
"Ȳ":"Y",
"Ɏ":"Y",
"Ỹ":"Y",
"Ź":"Z",
"Ž":"Z",
"Ẑ":"Z",
"Ⱬ":"Z",
"Ż":"Z",
"Ẓ":"Z",
"Ȥ":"Z",
"Ẕ":"Z",
"Ƶ":"Z",
"IJ":"IJ",
"Œ":"OE",
"ᴀ":"A",
"ᴁ":"AE",
"ʙ":"B",
"ᴃ":"B",
"ᴄ":"C",
"ᴅ":"D",
"ᴇ":"E",
"ꜰ":"F",
"ɢ":"G",
"ʛ":"G",
"ʜ":"H",
"ɪ":"I",
"ʁ":"R",
"ᴊ":"J",
"ᴋ":"K",
"ʟ":"L",
"ᴌ":"L",
"ᴍ":"M",
"ɴ":"N",
"ᴏ":"O",
"ɶ":"OE",
"ᴐ":"O",
"ᴕ":"OU",
"ᴘ":"P",
"ʀ":"R",
"ᴎ":"N",
"ᴙ":"R",
"ꜱ":"S",
"ᴛ":"T",
"ⱻ":"E",
"ᴚ":"R",
"ᴜ":"U",
"ᴠ":"V",
"ᴡ":"W",
"ʏ":"Y",
"ᴢ":"Z",
"á":"a",
"ă":"a",
"ắ":"a",
"ặ":"a",
"ằ":"a",
"ẳ":"a",
"ẵ":"a",
"ǎ":"a",
"â":"a",
"ấ":"a",
"ậ":"a",
"ầ":"a",
"ẩ":"a",
"ẫ":"a",
"ä":"a",
"ǟ":"a",
"ȧ":"a",
"ǡ":"a",
"ạ":"a",
"ȁ":"a",
"à":"a",
"ả":"a",
"ȃ":"a",
"ā":"a",
"ą":"a",
"ᶏ":"a",
"ẚ":"a",
"å":"a",
"ǻ":"a",
"ḁ":"a",
"ⱥ":"a",
"ã":"a",
"ꜳ":"aa",
"æ":"ae",
"ǽ":"ae",
"ǣ":"ae",
"ꜵ":"ao",
"ꜷ":"au",
"ꜹ":"av",
"ꜻ":"av",
"ꜽ":"ay",
"ḃ":"b",
"ḅ":"b",
"ɓ":"b",
"ḇ":"b",
"ᵬ":"b",
"ᶀ":"b",
"ƀ":"b",
"ƃ":"b",
"ɵ":"o",
"ć":"c",
"č":"c",
"ç":"c",
"ḉ":"c",
"ĉ":"c",
"ɕ":"c",
"ċ":"c",
"ƈ":"c",
"ȼ":"c",
"ď":"d",
"ḑ":"d",
"ḓ":"d",
"ȡ":"d",
"ḋ":"d",
"ḍ":"d",
"ɗ":"d",
"ᶑ":"d",
"ḏ":"d",
"ᵭ":"d",
"ᶁ":"d",
"đ":"d",
"ɖ":"d",
"ƌ":"d",
"ı":"i",
"ȷ":"j",
"ɟ":"j",
"ʄ":"j",
"dz":"dz",
"dž":"dz",
"é":"e",
"ĕ":"e",
"ě":"e",
"ȩ":"e",
"ḝ":"e",
"ê":"e",
"ế":"e",
"ệ":"e",
"ề":"e",
"ể":"e",
"ễ":"e",
"ḙ":"e",
"ë":"e",
"ė":"e",
"ẹ":"e",
"ȅ":"e",
"è":"e",
"ẻ":"e",
"ȇ":"e",
"ē":"e",
"ḗ":"e",
"ḕ":"e",
"ⱸ":"e",
"ę":"e",
"ᶒ":"e",
"ɇ":"e",
"ẽ":"e",
"ḛ":"e",
"ꝫ":"et",
"ḟ":"f",
"ƒ":"f",
"ᵮ":"f",
"ᶂ":"f",
"ǵ":"g",
"ğ":"g",
"ǧ":"g",
"ģ":"g",
"ĝ":"g",
"ġ":"g",
"ɠ":"g",
"ḡ":"g",
"ᶃ":"g",
"ǥ":"g",
"ḫ":"h",
"ȟ":"h",
"ḩ":"h",
"ĥ":"h",
"ⱨ":"h",
"ḧ":"h",
"ḣ":"h",
"ḥ":"h",
"ɦ":"h",
"ẖ":"h",
"ħ":"h",
"ƕ":"hv",
"í":"i",
"ĭ":"i",
"ǐ":"i",
"î":"i",
"ï":"i",
"ḯ":"i",
"ị":"i",
"ȉ":"i",
"ì":"i",
"ỉ":"i",
"ȋ":"i",
"ī":"i",
"į":"i",
"ᶖ":"i",
"ɨ":"i",
"ĩ":"i",
"ḭ":"i",
"ꝺ":"d",
"ꝼ":"f",
"ᵹ":"g",
"ꞃ":"r",
"ꞅ":"s",
"ꞇ":"t",
"ꝭ":"is",
"ǰ":"j",
"ĵ":"j",
"ʝ":"j",
"ɉ":"j",
"ḱ":"k",
"ǩ":"k",
"ķ":"k",
"ⱪ":"k",
"ꝃ":"k",
"ḳ":"k",
"ƙ":"k",
"ḵ":"k",
"ᶄ":"k",
"ꝁ":"k",
"ꝅ":"k",
"ĺ":"l",
"ƚ":"l",
"ɬ":"l",
"ľ":"l",
"ļ":"l",
"ḽ":"l",
"ȴ":"l",
"ḷ":"l",
"ḹ":"l",
"ⱡ":"l",
"ꝉ":"l",
"ḻ":"l",
"ŀ":"l",
"ɫ":"l",
"ᶅ":"l",
"ɭ":"l",
"ł":"l",
"lj":"lj",
"ſ":"s",
"ẜ":"s",
"ẛ":"s",
"ẝ":"s",
"ḿ":"m",
"ṁ":"m",
"ṃ":"m",
"ɱ":"m",
"ᵯ":"m",
"ᶆ":"m",
"ń":"n",
"ň":"n",
"ņ":"n",
"ṋ":"n",
"ȵ":"n",
"ṅ":"n",
"ṇ":"n",
"ǹ":"n",
"ɲ":"n",
"ṉ":"n",
"ƞ":"n",
"ᵰ":"n",
"ᶇ":"n",
"ɳ":"n",
"ñ":"n",
"nj":"nj",
"ó":"o",
"ŏ":"o",
"ǒ":"o",
"ô":"o",
"ố":"o",
"ộ":"o",
"ồ":"o",
"ổ":"o",
"ỗ":"o",
"ö":"o",
"ȫ":"o",
"ȯ":"o",
"ȱ":"o",
"ọ":"o",
"ő":"o",
"ȍ":"o",
"ò":"o",
"ỏ":"o",
"ơ":"o",
"ớ":"o",
"ợ":"o",
"ờ":"o",
"ở":"o",
"ỡ":"o",
"ȏ":"o",
"ꝋ":"o",
"ꝍ":"o",
"ⱺ":"o",
"ō":"o",
"ṓ":"o",
"ṑ":"o",
"ǫ":"o",
"ǭ":"o",
"ø":"o",
"ǿ":"o",
"õ":"o",
"ṍ":"o",
"ṏ":"o",
"ȭ":"o",
"ƣ":"oi",
"ꝏ":"oo",
"ɛ":"e",
"ᶓ":"e",
"ɔ":"o",
"ᶗ":"o",
"ȣ":"ou",
"ṕ":"p",
"ṗ":"p",
"ꝓ":"p",
"ƥ":"p",
"ᵱ":"p",
"ᶈ":"p",
"ꝕ":"p",
"ᵽ":"p",
"ꝑ":"p",
"ꝙ":"q",
"ʠ":"q",
"ɋ":"q",
"ꝗ":"q",
"ŕ":"r",
"ř":"r",
"ŗ":"r",
"ṙ":"r",
"ṛ":"r",
"ṝ":"r",
"ȑ":"r",
"ɾ":"r",
"ᵳ":"r",
"ȓ":"r",
"ṟ":"r",
"ɼ":"r",
"ᵲ":"r",
"ᶉ":"r",
"ɍ":"r",
"ɽ":"r",
"ↄ":"c",
"ꜿ":"c",
"ɘ":"e",
"ɿ":"r",
"ś":"s",
"ṥ":"s",
"š":"s",
"ṧ":"s",
"ş":"s",
"ŝ":"s",
"ș":"s",
"ṡ":"s",
"ṣ":"s",
"ṩ":"s",
"ʂ":"s",
"ᵴ":"s",
"ᶊ":"s",
"ȿ":"s",
"ɡ":"g",
"ᴑ":"o",
"ᴓ":"o",
"ᴝ":"u",
"ť":"t",
"ţ":"t",
"ṱ":"t",
"ț":"t",
"ȶ":"t",
"ẗ":"t",
"ⱦ":"t",
"ṫ":"t",
"ṭ":"t",
"ƭ":"t",
"ṯ":"t",
"ᵵ":"t",
"ƫ":"t",
"ʈ":"t",
"ŧ":"t",
"ᵺ":"th",
"ɐ":"a",
"ᴂ":"ae",
"ǝ":"e",
"ᵷ":"g",
"ɥ":"h",
"ʮ":"h",
"ʯ":"h",
"ᴉ":"i",
"ʞ":"k",
"ꞁ":"l",
"ɯ":"m",
"ɰ":"m",
"ᴔ":"oe",
"ɹ":"r",
"ɻ":"r",
"ɺ":"r",
"ⱹ":"r",
"ʇ":"t",
"ʌ":"v",
"ʍ":"w",
"ʎ":"y",
"ꜩ":"tz",
"ú":"u",
"ŭ":"u",
"ǔ":"u",
"û":"u",
"ṷ":"u",
"ü":"u",
"ǘ":"u",
"ǚ":"u",
"ǜ":"u",
"ǖ":"u",
"ṳ":"u",
"ụ":"u",
"ű":"u",
"ȕ":"u",
"ù":"u",
"ủ":"u",
"ư":"u",
"ứ":"u",
"ự":"u",
"ừ":"u",
"ử":"u",
"ữ":"u",
"ȗ":"u",
"ū":"u",
"ṻ":"u",
"ų":"u",
"ᶙ":"u",
"ů":"u",
"ũ":"u",
"ṹ":"u",
"ṵ":"u",
"ᵫ":"ue",
"ꝸ":"um",
"ⱴ":"v",
"ꝟ":"v",
"ṿ":"v",
"ʋ":"v",
"ᶌ":"v",
"ⱱ":"v",
"ṽ":"v",
"ꝡ":"vy",
"ẃ":"w",
"ŵ":"w",
"ẅ":"w",
"ẇ":"w",
"ẉ":"w",
"ẁ":"w",
"ⱳ":"w",
"ẘ":"w",
"ẍ":"x",
"ẋ":"x",
"ᶍ":"x",
"ý":"y",
"ŷ":"y",
"ÿ":"y",
"ẏ":"y",
"ỵ":"y",
"ỳ":"y",
"ƴ":"y",
"ỷ":"y",
"ỿ":"y",
"ȳ":"y",
"ẙ":"y",
"ɏ":"y",
"ỹ":"y",
"ź":"z",
"ž":"z",
"ẑ":"z",
"ʑ":"z",
"ⱬ":"z",
"ż":"z",
"ẓ":"z",
"ȥ":"z",
"ẕ":"z",
"ᵶ":"z",
"ᶎ":"z",
"ʐ":"z",
"ƶ":"z",
"ɀ":"z",
"ff":"ff",
"ffi":"ffi",
"ffl":"ffl",
"fi":"fi",
"fl":"fl",
"ij":"ij",
"œ":"oe",
"st":"st",
"ₐ":"a",
"ₑ":"e",
"ᵢ":"i",
"ⱼ":"j",
"ₒ":"o",
"ᵣ":"r",
"ᵤ":"u",
"ᵥ":"v",
"ₓ":"x"};
String.prototype.latinise=function(){return this.replace(/[^A-Za-z0-9\[\] ]/g,function(a){return Latinise.latin_map[a]||a})};
String.prototype.latinize=String.prototype.latinise;
String.prototype.isLatin=function(){return this==this.latinise()}
Некоторые примеры:
> "Piqué".latinize();
"Pique"
> "Piqué".isLatin();
false
> "Pique".isLatin();
true
> "Piqué".latinise().isLatin();
true
Спасибо, это, безусловно, полезно. Есть место для оптимизации, но это хорошее начало. +1
В чем смысл этой строки: String.prototype.latinize=String.prototype.latinise;?
@zsitro Эта строка позволяет вызывать функцию через "äöü".latinize() и "äöü".latinise(). Не очень хорошая практика !!
Измените прототип строки, это основная плохая практика. Я должен его обновить. Спасибо, в любом случае
Да - вы определенно можете избежать модификации прототипа - зависит от размера вашего проекта. Вы также можете выбрать предпочитаемое написание вместо того, чтобы раскрывать оба.
Если вы хотите добиться сортировки, где «ä» стоит после «a» и не рассматривается как одно и то же, вы можете использовать такую функцию, как моя.
Вы всегда можете изменить алфавит, чтобы получить другую или даже странную сортировку. Однако, если вы хотите, чтобы некоторые буквы были эквивалентными, вам нужно манипулировать строками, например a = a.replace(/ä/, 'a') или аналогичными, как многие уже ответили выше. Я добавил прописные буквы, если кто-то хочет, чтобы все слова в верхнем регистре были перед всеми словами в нижнем регистре (тогда вы должны опустить .toLowerCase()).
function sortbyalphabet(a,b) {
alphabet = "0123456789AaÀàÁáÂâÃãÄäBbCcÇçDdÈèÉéÊêËëFfGgHhÌìÍíÎîÏïJjKkLlMmNnÑñOoÒòÓóÔôÕõÖöPpQqRrSsTtÙùÚúÛûÜüVvWwXxÝýŸÿZz";
a = a.toLowerCase();
b = b.toLowerCase();
shorterone = (a.length > b.length ? a : b);
for (i=0; i<shorterone.length; i++){
diff = alphabet.indexOf(a.charAt(i)) - alphabet.indexOf(b.charAt(i));
if (diff!=0){
return diff;
}
}
// sort the shorter first
return a.length - b.length;
}
var n = ["ast", "Äste", "apfel", "äpfel", "à"];
console.info(n.sort(sortbyalphabet));
// should return ["apfel", "ast", "à", "äpfel", "äste"]
Идея хорошая, реализацию можно улучшить. 1) Вы не использовали ключевое слово var. Это означает, что каждая объявленная вами переменная является глобальной. Это определенно не то, что вы имели в виду (в JS нет автоматической области видимости функций). Забывание var порождает неприятные ошибки. 2) Вы должны использовать замыкание вместо переопределения алфавита при каждом вызове функции. 3) Вы не выполняете ни проверку типов, ни строгие сравнения. - Я создал оптимизированную версию вашей функции здесь: jsperf.com/collation-string-sorting. И в Chrome, и в IE это примерно в 4 раза быстрее, чем ваш подход.
Прямой перенос на javascript решения Кирона: https://github.com/rwarasaurus/nano/blob/master/system/helpers.php#L61-73:
/**
* Normalise a string replacing foreign characters
*
* @param {String} str
* @return {String} str
*/
var normalize = (function () {
var a = ['À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 'Ð', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'ß', 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'ÿ', 'Ā', 'ā', 'Ă', 'ă', 'Ą', 'ą', 'Ć', 'ć', 'Ĉ', 'ĉ', 'Ċ', 'ċ', 'Č', 'č', 'Ď', 'ď', 'Đ', 'đ', 'Ē', 'ē', 'Ĕ', 'ĕ', 'Ė', 'ė', 'Ę', 'ę', 'Ě', 'ě', 'Ĝ', 'ĝ', 'Ğ', 'ğ', 'Ġ', 'ġ', 'Ģ', 'ģ', 'Ĥ', 'ĥ', 'Ħ', 'ħ', 'Ĩ', 'ĩ', 'Ī', 'ī', 'Ĭ', 'ĭ', 'Į', 'į', 'İ', 'ı', 'IJ', 'ij', 'Ĵ', 'ĵ', 'Ķ', 'ķ', 'Ĺ', 'ĺ', 'Ļ', 'ļ', 'Ľ', 'ľ', 'Ŀ', 'ŀ', 'Ł', 'ł', 'Ń', 'ń', 'Ņ', 'ņ', 'Ň', 'ň', 'ʼn', 'Ō', 'ō', 'Ŏ', 'ŏ', 'Ő', 'ő', 'Œ', 'œ', 'Ŕ', 'ŕ', 'Ŗ', 'ŗ', 'Ř', 'ř', 'Ś', 'ś', 'Ŝ', 'ŝ', 'Ş', 'ş', 'Š', 'š', 'Ţ', 'ţ', 'Ť', 'ť', 'Ŧ', 'ŧ', 'Ũ', 'ũ', 'Ū', 'ū', 'Ŭ', 'ŭ', 'Ů', 'ů', 'Ű', 'ű', 'Ų', 'ų', 'Ŵ', 'ŵ', 'Ŷ', 'ŷ', 'Ÿ', 'Ź', 'ź', 'Ż', 'ż', 'Ž', 'ž', 'ſ', 'ƒ', 'Ơ', 'ơ', 'Ư', 'ư', 'Ǎ', 'ǎ', 'Ǐ', 'ǐ', 'Ǒ', 'ǒ', 'Ǔ', 'ǔ', 'Ǖ', 'ǖ', 'Ǘ', 'ǘ', 'Ǚ', 'ǚ', 'Ǜ', 'ǜ', 'Ǻ', 'ǻ', 'Ǽ', 'ǽ', 'Ǿ', 'ǿ'];
var b = ['A', 'A', 'A', 'A', 'A', 'A', 'AE', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'Y', 's', 'a', 'a', 'a', 'a', 'a', 'a', 'ae', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'n', 'o', 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 'u', 'y', 'y', 'A', 'a', 'A', 'a', 'A', 'a', 'C', 'c', 'C', 'c', 'C', 'c', 'C', 'c', 'D', 'd', 'D', 'd', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'G', 'g', 'G', 'g', 'G', 'g', 'G', 'g', 'H', 'h', 'H', 'h', 'I', 'i', 'I', 'i', 'I', 'i', 'I', 'i', 'I', 'i', 'IJ', 'ij', 'J', 'j', 'K', 'k', 'L', 'l', 'L', 'l', 'L', 'l', 'L', 'l', 'l', 'l', 'N', 'n', 'N', 'n', 'N', 'n', 'n', 'O', 'o', 'O', 'o', 'O', 'o', 'OE', 'oe', 'R', 'r', 'R', 'r', 'R', 'r', 'S', 's', 'S', 's', 'S', 's', 'S', 's', 'T', 't', 'T', 't', 'T', 't', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'W', 'w', 'Y', 'y', 'Y', 'Z', 'z', 'Z', 'z', 'Z', 'z', 's', 'f', 'O', 'o', 'U', 'u', 'A', 'a', 'I', 'i', 'O', 'o', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'A', 'a', 'AE', 'ae', 'O', 'o'];
return function (str) {
var i = a.length;
while (i--) str = str.replace(a[i], b[i]);
return str;
};
}());
И немного измененная версия, использующая char-map вместо двух массивов:
Чтобы сравнить эти два метода, я сделал простой тест: http://jsperf.com/replace-foreign-characters
/**
* Normalise a string replacing foreign characters
*
* @param {String} str
* @return {String}
*/
var normalize = (function () {
var map = {
"À": "A",
"Á": "A",
"Â": "A",
"Ã": "A",
"Ä": "A",
"Å": "A",
"Æ": "AE",
"Ç": "C",
"È": "E",
"É": "E",
"Ê": "E",
"Ë": "E",
"Ì": "I",
"Í": "I",
"Î": "I",
"Ï": "I",
"Ð": "D",
"Ñ": "N",
"Ò": "O",
"Ó": "O",
"Ô": "O",
"Õ": "O",
"Ö": "O",
"Ø": "O",
"Ù": "U",
"Ú": "U",
"Û": "U",
"Ü": "U",
"Ý": "Y",
"ß": "s",
"à": "a",
"á": "a",
"â": "a",
"ã": "a",
"ä": "a",
"å": "a",
"æ": "ae",
"ç": "c",
"è": "e",
"é": "e",
"ê": "e",
"ë": "e",
"ì": "i",
"í": "i",
"î": "i",
"ï": "i",
"ñ": "n",
"ò": "o",
"ó": "o",
"ô": "o",
"õ": "o",
"ö": "o",
"ø": "o",
"ù": "u",
"ú": "u",
"û": "u",
"ü": "u",
"ý": "y",
"ÿ": "y",
"Ā": "A",
"ā": "a",
"Ă": "A",
"ă": "a",
"Ą": "A",
"ą": "a",
"Ć": "C",
"ć": "c",
"Ĉ": "C",
"ĉ": "c",
"Ċ": "C",
"ċ": "c",
"Č": "C",
"č": "c",
"Ď": "D",
"ď": "d",
"Đ": "D",
"đ": "d",
"Ē": "E",
"ē": "e",
"Ĕ": "E",
"ĕ": "e",
"Ė": "E",
"ė": "e",
"Ę": "E",
"ę": "e",
"Ě": "E",
"ě": "e",
"Ĝ": "G",
"ĝ": "g",
"Ğ": "G",
"ğ": "g",
"Ġ": "G",
"ġ": "g",
"Ģ": "G",
"ģ": "g",
"Ĥ": "H",
"ĥ": "h",
"Ħ": "H",
"ħ": "h",
"Ĩ": "I",
"ĩ": "i",
"Ī": "I",
"ī": "i",
"Ĭ": "I",
"ĭ": "i",
"Į": "I",
"į": "i",
"İ": "I",
"ı": "i",
"IJ": "IJ",
"ij": "ij",
"Ĵ": "J",
"ĵ": "j",
"Ķ": "K",
"ķ": "k",
"Ĺ": "L",
"ĺ": "l",
"Ļ": "L",
"ļ": "l",
"Ľ": "L",
"ľ": "l",
"Ŀ": "L",
"ŀ": "l",
"Ł": "l",
"ł": "l",
"Ń": "N",
"ń": "n",
"Ņ": "N",
"ņ": "n",
"Ň": "N",
"ň": "n",
"ʼn": "n",
"Ō": "O",
"ō": "o",
"Ŏ": "O",
"ŏ": "o",
"Ő": "O",
"ő": "o",
"Œ": "OE",
"œ": "oe",
"Ŕ": "R",
"ŕ": "r",
"Ŗ": "R",
"ŗ": "r",
"Ř": "R",
"ř": "r",
"Ś": "S",
"ś": "s",
"Ŝ": "S",
"ŝ": "s",
"Ş": "S",
"ş": "s",
"Š": "S",
"š": "s",
"Ţ": "T",
"ţ": "t",
"Ť": "T",
"ť": "t",
"Ŧ": "T",
"ŧ": "t",
"Ũ": "U",
"ũ": "u",
"Ū": "U",
"ū": "u",
"Ŭ": "U",
"ŭ": "u",
"Ů": "U",
"ů": "u",
"Ű": "U",
"ű": "u",
"Ų": "U",
"ų": "u",
"Ŵ": "W",
"ŵ": "w",
"Ŷ": "Y",
"ŷ": "y",
"Ÿ": "Y",
"Ź": "Z",
"ź": "z",
"Ż": "Z",
"ż": "z",
"Ž": "Z",
"ž": "z",
"ſ": "s",
"ƒ": "f",
"Ơ": "O",
"ơ": "o",
"Ư": "U",
"ư": "u",
"Ǎ": "A",
"ǎ": "a",
"Ǐ": "I",
"ǐ": "i",
"Ǒ": "O",
"ǒ": "o",
"Ǔ": "U",
"ǔ": "u",
"Ǖ": "U",
"ǖ": "u",
"Ǘ": "U",
"ǘ": "u",
"Ǚ": "U",
"ǚ": "u",
"Ǜ": "U",
"ǜ": "u",
"Ǻ": "A",
"ǻ": "a",
"Ǽ": "AE",
"ǽ": "ae",
"Ǿ": "O",
"ǿ": "o"
},
nonWord = /\W/g,
mapping = function (c) {
return map[c] || c;
};
return function (str) {
return str.replace(nonWord, mapping);
};
}());
Это строит карту персонажей с каждым вызовом replace(), именно этого я пытался избежать. Использование /\W/ - приятный штрих, даже если он попытается заменить все пробелы, цифры и знаки препинания.
Первый вопрос легко решить, добавив карту и функцию замены к внешнему замыканию, что я только что сделал.
... Я не понимаю вашу последнюю правку. Почему вы убрали функцию замены с крышки?
@Tomalak Мне показалось, что функциональные выражения немного медленнее, чем прямые вызовы. Однако после небольшого исследования я пришел к выводу, что в данном случае это не имеет смысла. Теперь лучше?
Да, теперь все в порядке. Сохраняете ли вы выражение функции в переменной и используете его (например, mapping) или передаете выражение функции в качестве аргумента (например, foo(function () {...}), как вы это делали в предыдущей версии), семантически не имеет значения. Последнее не является прямым вызовом, это просто выражение функции, которое никогда не сохраняется.
Полное решение вашего запроса:
function convert_accented_characters(str){
var conversions = new Object();
conversions['ae'] = 'ä|æ|ǽ';
conversions['oe'] = 'ö|œ';
conversions['ue'] = 'ü';
conversions['Ae'] = 'Ä';
conversions['Ue'] = 'Ü';
conversions['Oe'] = 'Ö';
conversions['A'] = 'À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ';
conversions['a'] = 'à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª';
conversions['C'] = 'Ç|Ć|Ĉ|Ċ|Č';
conversions['c'] = 'ç|ć|ĉ|ċ|č';
conversions['D'] = 'Ð|Ď|Đ';
conversions['d'] = 'ð|ď|đ';
conversions['E'] = 'È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě';
conversions['e'] = 'è|é|ê|ë|ē|ĕ|ė|ę|ě';
conversions['G'] = 'Ĝ|Ğ|Ġ|Ģ';
conversions['g'] = 'ĝ|ğ|ġ|ģ';
conversions['H'] = 'Ĥ|Ħ';
conversions['h'] = 'ĥ|ħ';
conversions['I'] = 'Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ';
conversions['i'] = 'ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı';
conversions['J'] = 'Ĵ';
conversions['j'] = 'ĵ';
conversions['K'] = 'Ķ';
conversions['k'] = 'ķ';
conversions['L'] = 'Ĺ|Ļ|Ľ|Ŀ|Ł';
conversions['l'] = 'ĺ|ļ|ľ|ŀ|ł';
conversions['N'] = 'Ñ|Ń|Ņ|Ň';
conversions['n'] = 'ñ|ń|ņ|ň|ʼn';
conversions['O'] = 'Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ';
conversions['o'] = 'ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º';
conversions['R'] = 'Ŕ|Ŗ|Ř';
conversions['r'] = 'ŕ|ŗ|ř';
conversions['S'] = 'Ś|Ŝ|Ş|Š';
conversions['s'] = 'ś|ŝ|ş|š|ſ';
conversions['T'] = 'Ţ|Ť|Ŧ';
conversions['t'] = 'ţ|ť|ŧ';
conversions['U'] = 'Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ';
conversions['u'] = 'ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ';
conversions['Y'] = 'Ý|Ÿ|Ŷ';
conversions['y'] = 'ý|ÿ|ŷ';
conversions['W'] = 'Ŵ';
conversions['w'] = 'ŵ';
conversions['Z'] = 'Ź|Ż|Ž';
conversions['z'] = 'ź|ż|ž';
conversions['AE'] = 'Æ|Ǽ';
conversions['ss'] = 'ß';
conversions['IJ'] = 'IJ';
conversions['ij'] = 'ij';
conversions['OE'] = 'Œ';
conversions['f'] = 'ƒ';
for(var i in conversions){
var re = new RegExp(conversions[i],"g");
str = str.replace(re,i);
}
return str;
}
Правильная терминология для таких акцентов - Диакритики. После поиска этого термина в Google я нашел эта функция, который является частью backbone.paginator. Он имеет очень полную коллекцию диакритических знаков и заменяет их наиболее интуитивно понятным символом ascii. Я обнаружил, что это наиболее полное решение Javascript, доступное на сегодняшний день.
Полная функция для использования в будущем:
function removeDiacritics (str) {
var defaultDiacriticsRemovalMap = [
{'base':'A', 'letters':/[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g},
{'base':'AA','letters':/[\uA732]/g},
{'base':'AE','letters':/[\u00C6\u01FC\u01E2]/g},
{'base':'AO','letters':/[\uA734]/g},
{'base':'AU','letters':/[\uA736]/g},
{'base':'AV','letters':/[\uA738\uA73A]/g},
{'base':'AY','letters':/[\uA73C]/g},
{'base':'B', 'letters':/[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g},
{'base':'C', 'letters':/[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g},
{'base':'D', 'letters':/[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g},
{'base':'DZ','letters':/[\u01F1\u01C4]/g},
{'base':'Dz','letters':/[\u01F2\u01C5]/g},
{'base':'E', 'letters':/[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g},
{'base':'F', 'letters':/[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g},
{'base':'G', 'letters':/[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g},
{'base':'H', 'letters':/[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g},
{'base':'I', 'letters':/[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g},
{'base':'J', 'letters':/[\u004A\u24BF\uFF2A\u0134\u0248]/g},
{'base':'K', 'letters':/[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g},
{'base':'L', 'letters':/[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g},
{'base':'LJ','letters':/[\u01C7]/g},
{'base':'Lj','letters':/[\u01C8]/g},
{'base':'M', 'letters':/[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g},
{'base':'N', 'letters':/[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g},
{'base':'NJ','letters':/[\u01CA]/g},
{'base':'Nj','letters':/[\u01CB]/g},
{'base':'O', 'letters':/[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g},
{'base':'OI','letters':/[\u01A2]/g},
{'base':'OO','letters':/[\uA74E]/g},
{'base':'OU','letters':/[\u0222]/g},
{'base':'P', 'letters':/[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g},
{'base':'Q', 'letters':/[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g},
{'base':'R', 'letters':/[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g},
{'base':'S', 'letters':/[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g},
{'base':'T', 'letters':/[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g},
{'base':'TZ','letters':/[\uA728]/g},
{'base':'U', 'letters':/[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g},
{'base':'V', 'letters':/[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g},
{'base':'VY','letters':/[\uA760]/g},
{'base':'W', 'letters':/[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g},
{'base':'X', 'letters':/[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g},
{'base':'Y', 'letters':/[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g},
{'base':'Z', 'letters':/[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g},
{'base':'a', 'letters':/[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g},
{'base':'aa','letters':/[\uA733]/g},
{'base':'ae','letters':/[\u00E6\u01FD\u01E3]/g},
{'base':'ao','letters':/[\uA735]/g},
{'base':'au','letters':/[\uA737]/g},
{'base':'av','letters':/[\uA739\uA73B]/g},
{'base':'ay','letters':/[\uA73D]/g},
{'base':'b', 'letters':/[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g},
{'base':'c', 'letters':/[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g},
{'base':'d', 'letters':/[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g},
{'base':'dz','letters':/[\u01F3\u01C6]/g},
{'base':'e', 'letters':/[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g},
{'base':'f', 'letters':/[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g},
{'base':'g', 'letters':/[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g},
{'base':'h', 'letters':/[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g},
{'base':'hv','letters':/[\u0195]/g},
{'base':'i', 'letters':/[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g},
{'base':'j', 'letters':/[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g},
{'base':'k', 'letters':/[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g},
{'base':'l', 'letters':/[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g},
{'base':'lj','letters':/[\u01C9]/g},
{'base':'m', 'letters':/[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g},
{'base':'n', 'letters':/[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g},
{'base':'nj','letters':/[\u01CC]/g},
{'base':'o', 'letters':/[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g},
{'base':'oi','letters':/[\u01A3]/g},
{'base':'ou','letters':/[\u0223]/g},
{'base':'oo','letters':/[\uA74F]/g},
{'base':'p','letters':/[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g},
{'base':'q','letters':/[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g},
{'base':'r','letters':/[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g},
{'base':'s','letters':/[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g},
{'base':'t','letters':/[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g},
{'base':'tz','letters':/[\uA729]/g},
{'base':'u','letters':/[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g},
{'base':'v','letters':/[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g},
{'base':'vy','letters':/[\uA761]/g},
{'base':'w','letters':/[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g},
{'base':'x','letters':/[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g},
{'base':'y','letters':/[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g},
{'base':'z','letters':/[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g}
];
for(var i=0; i<defaultDiacriticsRemovalMap.length; i++) {
str = str.replace(defaultDiacriticsRemovalMap[i].letters, defaultDiacriticsRemovalMap[i].base);
}
return str;
}
Выглядит очень хорошо. - К сожалению, на момент написания этого вопроса backbone.js не существовало. :)
больше обсуждения этого решения на stackoverflow.com/a/18391901/759452
Просто следует нормализовать цепочку и запустить замену кодами:
var str = "Letras Á É Í Ó Ú Ñ - á é í ó ú ñ...";
console.info (str.normalize ("NFKD").replace (/[\u0300-\u036F]/g, ""));
// Letras A E I O U N - a e i o u n...
См. нормализовать
Тогда вы можете использовать эту функцию:
function noTilde (s) {
if (s.normalize != undefined) {
s = s.normalize ("NFKD");
}
return s.replace (/[\u0300-\u036F]/g, "");
}
Это очень мило! Но с другой стороны, на момент написания он был передовым и вряд ли портативным.
может быть инкапсулирован в функцию
Всякая инкапсуляция будет бесполезна, если движок JS в браузере не поддерживает эту функцию.
На дворе 2016 год, а Safari все еще не поддерживает его ... Жаль, что было бы очень удобно иметь этот инструмент под рукой.
Это не поддерживается в IE :(
@beginning Вот библиотека, которую вы можете использовать в качестве полифилла, чтобы получить функцию нормализации в браузерах, у которых ее нет: github.com/walling/unorm Я знаю, что это 4 года спустя, ну да ладно.
Просто попробуйте это со шведскими буквами åäö, и вы ошибочно получите aao. Не очень приятно ...
Давным-давно я сделал это на Java и нашел другое решение, основанное на одной строке, которая захватывает часть таблицы Unicode, которая была важна для преобразования - остальное было преобразовано в? или любой другой заменяющий символ. Поэтому я попытался преобразовать его в JavaScript. Имейте в виду, что я не эксперт по JS. :-)
TAB_00C0 = "AAAAAAACEEEEIIII" +
"DNOOOOO*OUUUUYIs" +
"aaaaaaaceeeeiiii" +
"?nooooo/ouuuuy?y" +
"AaAaAaCcCcCcCcDd" +
"DdEeEeEeEeEeGgGg" +
"GgGgHhHhIiIiIiIi" +
"IiJjJjKkkLlLlLlL" +
"lLlNnNnNnnNnOoOo" +
"OoOoRrRrRrSsSsSs" +
"SsTtTtTtUuUuUuUu" +
"UuUuWwYyYZzZzZzF";
function stripDiacritics(source) {
var result = source.split('');
for (var i = 0; i < result.length; i++) {
var c = source.charCodeAt(i);
if (c >= 0x00c0 && c <= 0x017f) {
result[i] = String.fromCharCode(TAB_00C0.charCodeAt(c - 0x00c0));
} else if (c > 127) {
result[i] = '?';
}
}
return result.join('');
}
stripDiacritics("Šupa, čo? ľšťčžýæøåℌð")
Это преобразует большинство символов Юникода latin1 + 2. Невозможно перевести один символ в несколько. Я не знаю его производительности на JS, в Java это, безусловно, самое быстрое из распространенных решений (6-50x), нет карты, нет регулярного выражения, ничего. Он производит строгий вывод в формате ASCII, потенциально с потерей информации, но размер вывода совпадает с размером ввода.
Я протестировал фрагмент с помощью http://www.webtoolkitonline.com/javascript-tester.html, и он произвел Supa, co? lstczyaoa??, как и ожидалось.
Это довольно здорово. Спасибо, что поделился!
Недавно я сравнил это с "Šupa, čo? ľšťčžýæøåℌð".normalize ("NFKD").replace (/[\u0300-\u036F]/g, "") и был удивлен, что normalize + replace (регулярное выражение, заметьте) примерно в два раза быстрее! Я обвиняю эти вещи быть встроенные модули и массово оптимизированы, но это бесспорно. Другое дело, что это не совсем то же самое для некоторых персонажей. Результат: Supa, co? lstczyæøaHð - значит, æøð не разрешены, но, с другой стороны, он охватывает ℌð, который был вне диапазона моей таблицы. Зная это, честно говоря, я предпочитаю normalize+replace.
Основываясь на существующих ответах и некоторых предложениях, я создал этот:
String.prototype.removeAccents = function() {
var removalMap = {
'A' : /[AⒶAÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄ]/g,
'AA' : /[Ꜳ]/g,
'AE' : /[ÆǼǢ]/g,
'AO' : /[Ꜵ]/g,
'AU' : /[Ꜷ]/g,
'AV' : /[ꜸꜺ]/g,
'AY' : /[Ꜽ]/g,
'B' : /[BⒷBḂḄḆɃƂƁ]/g,
'C' : /[CⒸCĆĈĊČÇḈƇȻꜾ]/g,
'D' : /[DⒹDḊĎḌḐḒḎĐƋƊƉꝹ]/g,
'DZ' : /[DZDŽ]/g,
'Dz' : /[DzDž]/g,
'E' : /[EⒺEÈÉÊỀẾỄỂẼĒḔḖĔĖËẺĚȄȆẸỆȨḜĘḘḚƐƎ]/g,
'F' : /[FⒻFḞƑꝻ]/g,
'G' : /[GⒼGǴĜḠĞĠǦĢǤƓꞠꝽꝾ]/g,
'H' : /[HⒽHĤḢḦȞḤḨḪĦⱧⱵꞍ]/g,
'I' : /[IⒾIÌÍÎĨĪĬİÏḮỈǏȈȊỊĮḬƗ]/g,
'J' : /[JⒿJĴɈ]/g,
'K' : /[KⓀKḰǨḲĶḴƘⱩꝀꝂꝄꞢ]/g,
'L' : /[LⓁLĿĹĽḶḸĻḼḺŁȽⱢⱠꝈꝆꞀ]/g,
'LJ' : /[LJ]/g,
'Lj' : /[Lj]/g,
'M' : /[MⓂMḾṀṂⱮƜ]/g,
'N' : /[NⓃNǸŃÑṄŇṆŅṊṈȠƝꞐꞤ]/g,
'NJ' : /[NJ]/g,
'Nj' : /[Nj]/g,
'O' : /[OⓄOÒÓÔỒỐỖỔÕṌȬṎŌṐṒŎȮȰÖȪỎŐǑȌȎƠỜỚỠỞỢỌỘǪǬØǾƆƟꝊꝌ]/g,
'OI' : /[Ƣ]/g,
'OO' : /[Ꝏ]/g,
'OU' : /[Ȣ]/g,
'P' : /[PⓅPṔṖƤⱣꝐꝒꝔ]/g,
'Q' : /[QⓆQꝖꝘɊ]/g,
'R' : /[RⓇRŔṘŘȐȒṚṜŖṞɌⱤꝚꞦꞂ]/g,
'S' : /[SⓈSẞŚṤŜṠŠṦṢṨȘŞⱾꞨꞄ]/g,
'T' : /[TⓉTṪŤṬȚŢṰṮŦƬƮȾꞆ]/g,
'TZ' : /[Ꜩ]/g,
'U' : /[UⓊUÙÚÛŨṸŪṺŬÜǛǗǕǙỦŮŰǓȔȖƯỪỨỮỬỰỤṲŲṶṴɄ]/g,
'V' : /[VⓋVṼṾƲꝞɅ]/g,
'VY' : /[Ꝡ]/g,
'W' : /[WⓌWẀẂŴẆẄẈⱲ]/g,
'X' : /[XⓍXẊẌ]/g,
'Y' : /[YⓎYỲÝŶỸȲẎŸỶỴƳɎỾ]/g,
'Z' : /[ZⓏZŹẐŻŽẒẔƵȤⱿⱫꝢ]/g,
'a' : /[aⓐaẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁąⱥɐ]/g,
'aa' : /[ꜳ]/g,
'ae' : /[æǽǣ]/g,
'ao' : /[ꜵ]/g,
'au' : /[ꜷ]/g,
'av' : /[ꜹꜻ]/g,
'ay' : /[ꜽ]/g,
'b' : /[bⓑbḃḅḇƀƃɓ]/g,
'c' : /[cⓒcćĉċčçḉƈȼꜿↄ]/g,
'd' : /[dⓓdḋďḍḑḓḏđƌɖɗꝺ]/g,
'dz' : /[dzdž]/g,
'e' : /[eⓔeèéêềếễểẽēḕḗĕėëẻěȅȇẹệȩḝęḙḛɇɛǝ]/g,
'f' : /[fⓕfḟƒꝼ]/g,
'g' : /[gⓖgǵĝḡğġǧģǥɠꞡᵹꝿ]/g,
'h' : /[hⓗhĥḣḧȟḥḩḫẖħⱨⱶɥ]/g,
'hv' : /[ƕ]/g,
'i' : /[iⓘiìíîĩīĭïḯỉǐȉȋịįḭɨı]/g,
'j' : /[jⓙjĵǰɉ]/g,
'k' : /[kⓚkḱǩḳķḵƙⱪꝁꝃꝅꞣ]/g,
'l' : /[lⓛlŀĺľḷḹļḽḻſłƚɫⱡꝉꞁꝇ]/g,
'lj' : /[lj]/g,
'm' : /[mⓜmḿṁṃɱɯ]/g,
'n' : /[nⓝnǹńñṅňṇņṋṉƞɲʼnꞑꞥ]/g,
'nj' : /[nj]/g,
'o' : /[oⓞoòóôồốỗổõṍȭṏōṑṓŏȯȱöȫỏőǒȍȏơờớỡởợọộǫǭøǿɔꝋꝍɵ]/g,
'oi' : /[ƣ]/g,
'ou' : /[ȣ]/g,
'oo' : /[ꝏ]/g,
'p' : /[pⓟpṕṗƥᵽꝑꝓꝕ]/g,
'q' : /[qⓠqɋꝗꝙ]/g,
'r' : /[rⓡrŕṙřȑȓṛṝŗṟɍɽꝛꞧꞃ]/g,
's' : /[sⓢsßśṥŝṡšṧṣṩșşȿꞩꞅẛ]/g,
't' : /[tⓣtṫẗťṭțţṱṯŧƭʈⱦꞇ]/g,
'tz' : /[ꜩ]/g,
'u' : /[uⓤuùúûũṹūṻŭüǜǘǖǚủůűǔȕȗưừứữửựụṳųṷṵʉ]/g,
'v' : /[vⓥvṽṿʋꝟʌ]/g,
'vy' : /[ꝡ]/g,
'w' : /[wⓦwẁẃŵẇẅẘẉⱳ]/g,
'x' : /[xⓧxẋẍ]/g,
'y' : /[yⓨyỳýŷỹȳẏÿỷẙỵƴɏỿ]/g,
'z' : /[zⓩzźẑżžẓẕƶȥɀⱬꝣ]/g,
};
var str = this;
for(var latin in removalMap) {
var nonLatin = removalMap[latin];
str = str.replace(nonLatin , latin);
}
return str;
}
Он использует настоящие символы вместо списка Unicode и хорошо работает.
Вы можете использовать это как
"ąąą".removeAccents(); // returns "aaa"
Вы можете легко преобразовать эту функцию, чтобы она не была прототипом строки. Однако, поскольку я фанат использования прототипа строки в таких случаях, вам придется сделать это самостоятельно.
К сожалению, это сравнительно неэффективно с таким количеством регулярных выражений в цикле.
Я решил по-другому, если хотите.
Здесь я использовал два массива, где searchChars, содержащий, который будет заменен, и replaceChars, содержащий желаемые символы.
var text = "your input string";
var searchChars = ['Å','Ä','å','Ö','ö']; // add more charecter.
var replaceChars = ['A','A','a','O','o']; // exact same index to searchChars.
var index;
for (var i = 0; i < text.length; i++) {
if ( $.inArray(text[i], searchChars) >-1 ){ // $.inArray() is from jquery.
index = searchChars.indexOf(text[i]);
text = text.slice(0, i) + replaceChars[index] + text.slice(i+1,text.length);
}
}Это крайне неэффективно. Вам будет полезно выбрать одно из других решений.
Простой и легкий способ:
function remove-accents(p){
c='áàãâäéèêëíìîïóòõôöúùûüçÁÀÃÂÄÉÈÊËÍÌÎÏÓÒÕÖÔÚÙÛÜÇ';s='aaaaaeeeeiiiiooooouuuucAAAAAEEEEIIIIOOOOOUUUUC';n='';for(i=0;i<p.length;i++){if (c.search(p.substr(i,1))>=0){n+=s.substr(c.search(p.substr(i,1)),1);} else{n+=p.substr(i,1);}} return n;
}
Итак, сделайте это:
remove-accents("Thís ís ân accêntéd phráse");
Выход:
"This is an accented phrase"
Ни в одном ответе не упоминается String.localeCompare, который делает именно то, что вы изначально хотели, но не то, о чем вы просите.
var list = ['a', 'b', 'c', 'o', 'u', 'z', 'ä', 'ö', 'ü'];
list.sort((a, b) => a.localeCompare(b));
console.info(list);
//Outputs ['a', 'ä', 'b', 'c', 'o', 'ö', 'u', 'ü', 'z']
Однако второй и третий параметры не поддерживаются старыми браузерами. Тем не менее, этот вариант стоит рассмотреть.
Хорошее дополнение! В этом конкретном случае я не влиял на как, строки сравнивались, так как это делается внутри TableSorter. Я мог влиять только на строки какие, которые хочу использовать. Поэтому заменить их на тот момент было единственным вариантом. Возможно, в более современных версиях TableSorter лучше внутренняя обработка этих вещей.
Я добавил особое упоминание об этом ответе на вопрос.
Если вы ищете специально способ преобразования символов с диакритическими знаками в символы без диакритических знаков, а не способ сортировки символов с диакритическими знаками, с небольшими изысками, можно управлять функцией String.localeCompare, чтобы найти основные латинские символы, которые соответствуют расширенные. Например, вы можете захотеть создать удобный для человека URL-адрес из заголовка страницы. Если да, вы можете сделать что-то вроде этого:
var baseChars = [];
for (var i = 97; i < 97 + 26; i++) {
baseChars.push(String.fromCharCode(i));
}
//if needed, handle fancy compound characters
baseChars = baseChars.concat('ss,aa,ae,ao,au,av,ay,dz,hv,lj,nj,oi,ou,oo,tz,vy'.split(','));
function isUpperCase(c) { return c !== c.toLocaleLowerCase() }
function toBaseChar(c, opts) {
opts = opts || {};
//if (!('nonAlphaChar' in opts)) opts.nonAlphaChar = '';
//if (!('noMatchChar' in opts)) opts.noMatchChar = '';
if (!('locale' in opts)) opts.locale = 'en';
var cOpts = {sensitivity: 'base'};
//exit early for any non-alphabetical character
if (c.localeCompare('9', opts.locale, cOpts) <= 0) return opts.nonAlphaChar === undefined ? c : opts.nonAlphaChar;
for (var i = 0; i < baseChars.length; i++) {
var baseChar = baseChars[i];
var comp = c.localeCompare(baseChar, opts.locale, cOpts);
if (comp == 0) return (isUpperCase(c)) ? baseChar.toUpperCase() : baseChar;
}
return opts.noMatchChar === undefined ? c : opts.noMatchChar;
}
function latinify(str, opts) {
return str.replace(/[^\w\s\d]/g, function(c) {
return toBaseChar(c, opts);
})
}
// Example:
console.info(latinify('Čeština Tsėhesenėstsestotse Tshivenḓa Emigliàn–Rumagnòl Slovenščina Português Tiếng Việt Straße'))
// "Cestina Tsehesenestsestotse Tshivenda Emiglian–Rumagnol Slovenscina Portugues Tieng Viet Strasse"Это должно работать достаточно хорошо, но если потребуется дальнейшая оптимизация, можно использовать двоичный поиск с localeCompare в качестве компаратора для поиска базового символа. Обратите внимание, что регистр сохраняется, а параметры позволяют сохранить, заменить или удалить символы, которые не являются алфавитными или не имеют соответствующих латинских символов, которыми они могут быть заменены. Эта реализация более быстрая и гибкая и должна работать с новыми символами по мере их добавления. Недостатком является то, что составные символы, такие как '', должны обрабатываться специально, если они нуждаются в поддержке.
Это очень мило. Жаль, что поздним ответам на старые темы уделяется так мало внимания.
Самый лучший ответ здесь. Должен получить больше голосов (получил мое!)
Я просто хотел опубликовать свое решение, используя Строка # localeCompare
const base_chars = [
'1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'-', '_', ' '
];
const fix = str => str.normalize('NFKD').split('')
.map(c => base_chars.find(bc => bc.localeCompare(c, 'en', { sensitivity: 'base' })==0))
.join('');
const str = 'OÒ óëå-123';
console.info(`fix(${str}) = ${fix(str)}`);https://stackoverflow.com/a/37511463
With ES2015/ES6 String.Prototype.Normalize(),
const str = "Crème Brulée" str.normalize('NFD').replace(/[\u0300-\u036f]/g, "") > 'Creme Brulee'Two things are happening here:
normalize()ing toNFDUnicode normal form decomposes combined graphemes into the combination of simple ones. TheèofCrèmeends up expressed ase+̀.- Using a regex character class to match the U+0300 → U+036F range, it is now trivial to
globally get rid of the diacritics, which the Unicode standard conveniently groups as the Combining Diacritical Marks Unicode block.See comment for performance testing.
Alternatively, if you just want sorting
Intl.Collator has sufficient support ~85% right now, a polyfill is also available here but I haven't tested it.
const c = new Intl.Collator(); ['creme brulee', 'crème brulée', 'crame brulai', 'crome brouillé', 'creme brulay', 'creme brulfé', 'creme bruléa'].sort(c.compare) [ 'crame brulai','creme brulay','creme bruléa','creme brulee', 'crème brulée','creme brulfé','crome brouillé' ] ['creme brulee', 'crème brulée', 'crame brulai', 'crome brouillé'].sort((a,b) => a>b) ["crame brulai", "creme brulee", "crome brouillé", "crème brulée"]
Считаю этот ответ лучшим. Основан на стандарте Unicode и использует встроенные функции. Спасибо.
Intl.Collator(undefined , {sensitivity: 'base'})Я использовал это для создания заголовка, поэтому, прежде чем заменять пробелы на конечную косую черту и все на строчные буквы. Ваша функция работает отлично !!!
Уже обсуждался в другом ответе в этой ветке за 14 год. stackoverflow.com/a/23767389/18771
Это не правильно. Настоящий, ошибочно заменены недиакритические шведские / немецкие буквы. См. JSFiddle.
Ответ os Crisalin почти идеален. Просто улучшили производительность, чтобы избежать создания нового RegExp при каждом запуске.
var normalizeConversions = [
{ regex: new RegExp('ä|æ|ǽ', 'g'), clean: 'ae' },
{ regex: new RegExp('ö|œ', 'g'), clean: 'oe' },
{ regex: new RegExp('ü', 'g'), clean: 'ue' },
{ regex: new RegExp('Ä', 'g'), clean: 'Ae' },
{ regex: new RegExp('Ü', 'g'), clean: 'Ue' },
{ regex: new RegExp('Ö', 'g'), clean: 'Oe' },
{ regex: new RegExp('À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ', 'g'), clean: 'A' },
{ regex: new RegExp('à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª', 'g'), clean: 'a' },
{ regex: new RegExp('Ç|Ć|Ĉ|Ċ|Č', 'g'), clean: 'C' },
{ regex: new RegExp('ç|ć|ĉ|ċ|č', 'g'), clean: 'c' },
{ regex: new RegExp('Ð|Ď|Đ', 'g'), clean: 'D' },
{ regex: new RegExp('ð|ď|đ', 'g'), clean: 'd' },
{ regex: new RegExp('È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě', 'g'), clean: 'E' },
{ regex: new RegExp('è|é|ê|ë|ē|ĕ|ė|ę|ě', 'g'), clean: 'e' },
{ regex: new RegExp('Ĝ|Ğ|Ġ|Ģ', 'g'), clean: 'G' },
{ regex: new RegExp('ĝ|ğ|ġ|ģ', 'g'), clean: 'g' },
{ regex: new RegExp('Ĥ|Ħ', 'g'), clean: 'H' },
{ regex: new RegExp('ĥ|ħ', 'g'), clean: 'h' },
{ regex: new RegExp('Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ', 'g'), clean: 'I' },
{ regex: new RegExp('ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı', 'g'), clean: 'i' },
{ regex: new RegExp('Ĵ', 'g'), clean: 'J' },
{ regex: new RegExp('ĵ', 'g'), clean: 'j' },
{ regex: new RegExp('Ķ', 'g'), clean: 'K' },
{ regex: new RegExp('ķ', 'g'), clean: 'k' },
{ regex: new RegExp('Ĺ|Ļ|Ľ|Ŀ|Ł', 'g'), clean: 'L' },
{ regex: new RegExp('ĺ|ļ|ľ|ŀ|ł', 'g'), clean: 'l' },
{ regex: new RegExp('Ñ|Ń|Ņ|Ň', 'g'), clean: 'N' },
{ regex: new RegExp('ñ|ń|ņ|ň|ʼn', 'g'), clean: 'n' },
{ regex: new RegExp('Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ', 'g'), clean: 'O' },
{ regex: new RegExp('ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º', 'g'), clean: 'o' },
{ regex: new RegExp('Ŕ|Ŗ|Ř', 'g'), clean: 'R' },
{ regex: new RegExp('ŕ|ŗ|ř', 'g'), clean: 'r' },
{ regex: new RegExp('Ś|Ŝ|Ş|Š', 'g'), clean: 'S' },
{ regex: new RegExp('ś|ŝ|ş|š|ſ', 'g'), clean: 's' },
{ regex: new RegExp('Ţ|Ť|Ŧ', 'g'), clean: 'T' },
{ regex: new RegExp('ţ|ť|ŧ', 'g'), clean: 't' },
{ regex: new RegExp('Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ', 'g'), clean: 'U' },
{ regex: new RegExp('ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ', 'g'), clean: 'u' },
{ regex: new RegExp('Ý|Ÿ|Ŷ', 'g'), clean: 'Y' },
{ regex: new RegExp('ý|ÿ|ŷ', 'g'), clean: 'y' },
{ regex: new RegExp('Ŵ', 'g'), clean: 'W' },
{ regex: new RegExp('ŵ', 'g'), clean: 'w' },
{ regex: new RegExp('Ź|Ż|Ž', 'g'), clean: 'Z' },
{ regex: new RegExp('ź|ż|ž', 'g'), clean: 'z' },
{ regex: new RegExp('Æ|Ǽ', 'g'), clean: 'AE' },
{ regex: new RegExp('ß', 'g'), clean: 'ss' },
{ regex: new RegExp('IJ', 'g'), clean: 'IJ' },
{ regex: new RegExp('ij', 'g'), clean: 'ij' },
{ regex: new RegExp('Œ', 'g'), clean: 'OE' },
{ regex: new RegExp('ƒ', 'g'), clean: 'f' }
];
Использование:
function(str){
normalizeConversions.forEach(function(normalizeEntry){
str = str.replace(normalizeEntry.regex, normalizeEntry.clean);
});
return str;
};
Я думаю, вы можете сэкономить место, используя литералы регулярных выражений, а классы символов более эффективны, чем чередования. Однако реальным ударом по производительности будет выполнение такого количества регулярных выражений в одной строке. Regex работает медленно. 100 регулярных выражений медленные * 100. Гораздо эффективнее выполнить одно регулярное выражение, которое соответствует 100 символам, и искать замены, как это делает принятый ответ, чем выполнять 100 регулярных выражений в цикле. Вдобавок к этому, строки JS неизменяемы, поэтому вы выделяете (количество регулярных выражений - 1) выбрасываемые строки с помощью этого подхода, что тоже довольно расточительно.
Здесь есть 2 вещи: память и производительность обработки. Что касается использования памяти, вы правы, этот подход выделяет больше памяти, но сегодня все устройства имеют много памяти, и ее не так много памяти для выделения. Насчет производительности обработки, думаю, вы ошибаетесь. Я не подбираю 100 символов и ищу замену. Я делаю точно то же самое, что и ответ Crisalin, но вместо того, чтобы создавать RegExp при каждом приращении цикла, я создаю их один раз, повторно использую их при каждом вызове. Используйте немного больше памяти, но гораздо быстрее.
Вы применяете 100 (хорошо, сейчас 50) регулярных выражений в цикле, постоянно создавая новые строки в процессах. Это неэффективно. Попробуйте сами. Попробуйте также с длинными струнами.
Я просто не понимаю, где у моего ответа худшее исполнение, чем у ответа Крисалина Петровского. Я делаю то же самое, но быстрее. Не говорю, что это лучшее, но это улучшение решения Crisalin, и это была единственная цель моего ответа.
Это могло быть. Я не сравниваю ваши подходы. Все, что я указываю в вашем подходе, применимо и к его. (Существует глобальный кеш для регулярных выражений, постоянное создание одних и тех же не влияет на производительность так сильно, как вы думаете.)
Где спецификация этого глобального кеша регулярных выражений? Никогда ничего об этом не слышал. И, кстати, регулярные выражения не медленные, как вы могли подумать, на самом деле они очень быстрые, все зависит от того, как вы их пишете. Но в любом случае я просто улучшал ответ Crisalin, а не сравнивал его с принятым ответом.
Я не собираюсь с тобой спорить. Кэширование зависит от реализации, для этого в языке нет «спецификации». Не стесняйтесь взглянуть на то, что делает node.js (источник). Разумно предположить, что дорогостоящие операции, такие как создание объекта регулярного выражения, также кэшируются в большинстве других реализаций JS.
Я не спорю с вами, я был искренне заинтересован и собирался удалить свой ответ, потому что, если то, что вы говорите, правда, ответ Crisalin имеет такую же эффективность, что и мой. Но мы не можем запрограммировать предположения, поэтому я сохраню свой ответ.
Совершенно нормально. Я не пытался заставить вас удалить свой ответ.
Я не могу придумать более простого способа эффективно удалить диакритические знаки все из строки, чем использование этого удивительное решение.
Посмотрите это в действии:
var string = "öäüÖÄÜ";
var string_norm = string.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
console.info(string_norm);Уже обсуждался в другом ответе в этой теме. stackoverflow.com/a/23767389/18771
@Tomalak Верно, я этого не заметил. В любом случае, я не удаляю свой ответ, потому что считаю, что лучше использовать «NFD» вместо «NFKD». Плюс у меня есть отрывок. :п
Для парней, использующих Машинопись, и тех, кто не хочет иметь дело с прототипами строк, вот машинописная версия Эд. Ответ:
// Usage example:
"Some string".replace(/[^a-zA-Z0-9-_]/g, char => ToLatinMap.get(char) || '')
// Map:
export let ToLatinMap: Map<string, string> = new Map<string, string>([
["Á", "A"],
["Ă", "A"],
["Ắ", "A"],
["Ặ", "A"],
["Ằ", "A"],
["Ẳ", "A"],
["Ẵ", "A"],
["Ǎ", "A"],
["Â", "A"],
["Ấ", "A"],
["Ậ", "A"],
["Ầ", "A"],
["Ẩ", "A"],
["Ẫ", "A"],
["Ä", "A"],
["Ǟ", "A"],
["Ȧ", "A"],
["Ǡ", "A"],
["Ạ", "A"],
["Ȁ", "A"],
["À", "A"],
["Ả", "A"],
["Ȃ", "A"],
["Ā", "A"],
["Ą", "A"],
["Å", "A"],
["Ǻ", "A"],
["Ḁ", "A"],
["Ⱥ", "A"],
["Ã", "A"],
["Ꜳ", "AA"],
["Æ", "AE"],
["Ǽ", "AE"],
["Ǣ", "AE"],
["Ꜵ", "AO"],
["Ꜷ", "AU"],
["Ꜹ", "AV"],
["Ꜻ", "AV"],
["Ꜽ", "AY"],
["Ḃ", "B"],
["Ḅ", "B"],
["Ɓ", "B"],
["Ḇ", "B"],
["Ƀ", "B"],
["Ƃ", "B"],
["Ć", "C"],
["Č", "C"],
["Ç", "C"],
["Ḉ", "C"],
["Ĉ", "C"],
["Ċ", "C"],
["Ƈ", "C"],
["Ȼ", "C"],
["Ď", "D"],
["Ḑ", "D"],
["Ḓ", "D"],
["Ḋ", "D"],
["Ḍ", "D"],
["Ɗ", "D"],
["Ḏ", "D"],
["Dz", "D"],
["Dž", "D"],
["Đ", "D"],
["Ƌ", "D"],
["DZ", "DZ"],
["DŽ", "DZ"],
["É", "E"],
["Ĕ", "E"],
["Ě", "E"],
["Ȩ", "E"],
["Ḝ", "E"],
["Ê", "E"],
["Ế", "E"],
["Ệ", "E"],
["Ề", "E"],
["Ể", "E"],
["Ễ", "E"],
["Ḙ", "E"],
["Ë", "E"],
["Ė", "E"],
["Ẹ", "E"],
["Ȅ", "E"],
["È", "E"],
["Ẻ", "E"],
["Ȇ", "E"],
["Ē", "E"],
["Ḗ", "E"],
["Ḕ", "E"],
["Ę", "E"],
["Ɇ", "E"],
["Ẽ", "E"],
["Ḛ", "E"],
["Ꝫ", "ET"],
["Ḟ", "F"],
["Ƒ", "F"],
["Ǵ", "G"],
["Ğ", "G"],
["Ǧ", "G"],
["Ģ", "G"],
["Ĝ", "G"],
["Ġ", "G"],
["Ɠ", "G"],
["Ḡ", "G"],
["Ǥ", "G"],
["Ḫ", "H"],
["Ȟ", "H"],
["Ḩ", "H"],
["Ĥ", "H"],
["Ⱨ", "H"],
["Ḧ", "H"],
["Ḣ", "H"],
["Ḥ", "H"],
["Ħ", "H"],
["Í", "I"],
["Ĭ", "I"],
["Ǐ", "I"],
["Î", "I"],
["Ï", "I"],
["Ḯ", "I"],
["İ", "I"],
["Ị", "I"],
["Ȉ", "I"],
["Ì", "I"],
["Ỉ", "I"],
["Ȋ", "I"],
["Ī", "I"],
["Į", "I"],
["Ɨ", "I"],
["Ĩ", "I"],
["Ḭ", "I"],
["Ꝺ", "D"],
["Ꝼ", "F"],
["Ᵹ", "G"],
["Ꞃ", "R"],
["Ꞅ", "S"],
["Ꞇ", "T"],
["Ꝭ", "IS"],
["Ĵ", "J"],
["Ɉ", "J"],
["Ḱ", "K"],
["Ǩ", "K"],
["Ķ", "K"],
["Ⱪ", "K"],
["Ꝃ", "K"],
["Ḳ", "K"],
["Ƙ", "K"],
["Ḵ", "K"],
["Ꝁ", "K"],
["Ꝅ", "K"],
["Ĺ", "L"],
["Ƚ", "L"],
["Ľ", "L"],
["Ļ", "L"],
["Ḽ", "L"],
["Ḷ", "L"],
["Ḹ", "L"],
["Ⱡ", "L"],
["Ꝉ", "L"],
["Ḻ", "L"],
["Ŀ", "L"],
["Ɫ", "L"],
["Lj", "L"],
["Ł", "L"],
["LJ", "LJ"],
["Ḿ", "M"],
["Ṁ", "M"],
["Ṃ", "M"],
["Ɱ", "M"],
["Ń", "N"],
["Ň", "N"],
["Ņ", "N"],
["Ṋ", "N"],
["Ṅ", "N"],
["Ṇ", "N"],
["Ǹ", "N"],
["Ɲ", "N"],
["Ṉ", "N"],
["Ƞ", "N"],
["Nj", "N"],
["Ñ", "N"],
["NJ", "NJ"],
["Ó", "O"],
["Ŏ", "O"],
["Ǒ", "O"],
["Ô", "O"],
["Ố", "O"],
["Ộ", "O"],
["Ồ", "O"],
["Ổ", "O"],
["Ỗ", "O"],
["Ö", "O"],
["Ȫ", "O"],
["Ȯ", "O"],
["Ȱ", "O"],
["Ọ", "O"],
["Ő", "O"],
["Ȍ", "O"],
["Ò", "O"],
["Ỏ", "O"],
["Ơ", "O"],
["Ớ", "O"],
["Ợ", "O"],
["Ờ", "O"],
["Ở", "O"],
["Ỡ", "O"],
["Ȏ", "O"],
["Ꝋ", "O"],
["Ꝍ", "O"],
["Ō", "O"],
["Ṓ", "O"],
["Ṑ", "O"],
["Ɵ", "O"],
["Ǫ", "O"],
["Ǭ", "O"],
["Ø", "O"],
["Ǿ", "O"],
["Õ", "O"],
["Ṍ", "O"],
["Ṏ", "O"],
["Ȭ", "O"],
["Ƣ", "OI"],
["Ꝏ", "OO"],
["Ɛ", "E"],
["Ɔ", "O"],
["Ȣ", "OU"],
["Ṕ", "P"],
["Ṗ", "P"],
["Ꝓ", "P"],
["Ƥ", "P"],
["Ꝕ", "P"],
["Ᵽ", "P"],
["Ꝑ", "P"],
["Ꝙ", "Q"],
["Ꝗ", "Q"],
["Ŕ", "R"],
["Ř", "R"],
["Ŗ", "R"],
["Ṙ", "R"],
["Ṛ", "R"],
["Ṝ", "R"],
["Ȑ", "R"],
["Ȓ", "R"],
["Ṟ", "R"],
["Ɍ", "R"],
["Ɽ", "R"],
["Ꜿ", "C"],
["Ǝ", "E"],
["Ś", "S"],
["Ṥ", "S"],
["Š", "S"],
["Ṧ", "S"],
["Ş", "S"],
["Ŝ", "S"],
["Ș", "S"],
["Ṡ", "S"],
["Ṣ", "S"],
["Ṩ", "S"],
["Ť", "T"],
["Ţ", "T"],
["Ṱ", "T"],
["Ț", "T"],
["Ⱦ", "T"],
["Ṫ", "T"],
["Ṭ", "T"],
["Ƭ", "T"],
["Ṯ", "T"],
["Ʈ", "T"],
["Ŧ", "T"],
["Ɐ", "A"],
["Ꞁ", "L"],
["Ɯ", "M"],
["Ʌ", "V"],
["Ꜩ", "TZ"],
["Ú", "U"],
["Ŭ", "U"],
["Ǔ", "U"],
["Û", "U"],
["Ṷ", "U"],
["Ü", "U"],
["Ǘ", "U"],
["Ǚ", "U"],
["Ǜ", "U"],
["Ǖ", "U"],
["Ṳ", "U"],
["Ụ", "U"],
["Ű", "U"],
["Ȕ", "U"],
["Ù", "U"],
["Ủ", "U"],
["Ư", "U"],
["Ứ", "U"],
["Ự", "U"],
["Ừ", "U"],
["Ử", "U"],
["Ữ", "U"],
["Ȗ", "U"],
["Ū", "U"],
["Ṻ", "U"],
["Ų", "U"],
["Ů", "U"],
["Ũ", "U"],
["Ṹ", "U"],
["Ṵ", "U"],
["Ꝟ", "V"],
["Ṿ", "V"],
["Ʋ", "V"],
["Ṽ", "V"],
["Ꝡ", "VY"],
["Ẃ", "W"],
["Ŵ", "W"],
["Ẅ", "W"],
["Ẇ", "W"],
["Ẉ", "W"],
["Ẁ", "W"],
["Ⱳ", "W"],
["Ẍ", "X"],
["Ẋ", "X"],
["Ý", "Y"],
["Ŷ", "Y"],
["Ÿ", "Y"],
["Ẏ", "Y"],
["Ỵ", "Y"],
["Ỳ", "Y"],
["Ƴ", "Y"],
["Ỷ", "Y"],
["Ỿ", "Y"],
["Ȳ", "Y"],
["Ɏ", "Y"],
["Ỹ", "Y"],
["Ź", "Z"],
["Ž", "Z"],
["Ẑ", "Z"],
["Ⱬ", "Z"],
["Ż", "Z"],
["Ẓ", "Z"],
["Ȥ", "Z"],
["Ẕ", "Z"],
["Ƶ", "Z"],
["IJ", "IJ"],
["Œ", "OE"],
["ᴀ", "A"],
["ᴁ", "AE"],
["ʙ", "B"],
["ᴃ", "B"],
["ᴄ", "C"],
["ᴅ", "D"],
["ᴇ", "E"],
["ꜰ", "F"],
["ɢ", "G"],
["ʛ", "G"],
["ʜ", "H"],
["ɪ", "I"],
["ʁ", "R"],
["ᴊ", "J"],
["ᴋ", "K"],
["ʟ", "L"],
["ᴌ", "L"],
["ᴍ", "M"],
["ɴ", "N"],
["ᴏ", "O"],
["ɶ", "OE"],
["ᴐ", "O"],
["ᴕ", "OU"],
["ᴘ", "P"],
["ʀ", "R"],
["ᴎ", "N"],
["ᴙ", "R"],
["ꜱ", "S"],
["ᴛ", "T"],
["ⱻ", "E"],
["ᴚ", "R"],
["ᴜ", "U"],
["ᴠ", "V"],
["ᴡ", "W"],
["ʏ", "Y"],
["ᴢ", "Z"],
["á", "a"],
["ă", "a"],
["ắ", "a"],
["ặ", "a"],
["ằ", "a"],
["ẳ", "a"],
["ẵ", "a"],
["ǎ", "a"],
["â", "a"],
["ấ", "a"],
["ậ", "a"],
["ầ", "a"],
["ẩ", "a"],
["ẫ", "a"],
["ä", "a"],
["ǟ", "a"],
["ȧ", "a"],
["ǡ", "a"],
["ạ", "a"],
["ȁ", "a"],
["à", "a"],
["ả", "a"],
["ȃ", "a"],
["ā", "a"],
["ą", "a"],
["ᶏ", "a"],
["ẚ", "a"],
["å", "a"],
["ǻ", "a"],
["ḁ", "a"],
["ⱥ", "a"],
["ã", "a"],
["ꜳ", "aa"],
["æ", "ae"],
["ǽ", "ae"],
["ǣ", "ae"],
["ꜵ", "ao"],
["ꜷ", "au"],
["ꜹ", "av"],
["ꜻ", "av"],
["ꜽ", "ay"],
["ḃ", "b"],
["ḅ", "b"],
["ɓ", "b"],
["ḇ", "b"],
["ᵬ", "b"],
["ᶀ", "b"],
["ƀ", "b"],
["ƃ", "b"],
["ɵ", "o"],
["ć", "c"],
["č", "c"],
["ç", "c"],
["ḉ", "c"],
["ĉ", "c"],
["ɕ", "c"],
["ċ", "c"],
["ƈ", "c"],
["ȼ", "c"],
["ď", "d"],
["ḑ", "d"],
["ḓ", "d"],
["ȡ", "d"],
["ḋ", "d"],
["ḍ", "d"],
["ɗ", "d"],
["ᶑ", "d"],
["ḏ", "d"],
["ᵭ", "d"],
["ᶁ", "d"],
["đ", "d"],
["ɖ", "d"],
["ƌ", "d"],
["ı", "i"],
["ȷ", "j"],
["ɟ", "j"],
["ʄ", "j"],
["dz", "dz"],
["dž", "dz"],
["é", "e"],
["ĕ", "e"],
["ě", "e"],
["ȩ", "e"],
["ḝ", "e"],
["ê", "e"],
["ế", "e"],
["ệ", "e"],
["ề", "e"],
["ể", "e"],
["ễ", "e"],
["ḙ", "e"],
["ë", "e"],
["ė", "e"],
["ẹ", "e"],
["ȅ", "e"],
["è", "e"],
["ẻ", "e"],
["ȇ", "e"],
["ē", "e"],
["ḗ", "e"],
["ḕ", "e"],
["ⱸ", "e"],
["ę", "e"],
["ᶒ", "e"],
["ɇ", "e"],
["ẽ", "e"],
["ḛ", "e"],
["ꝫ", "et"],
["ḟ", "f"],
["ƒ", "f"],
["ᵮ", "f"],
["ᶂ", "f"],
["ǵ", "g"],
["ğ", "g"],
["ǧ", "g"],
["ģ", "g"],
["ĝ", "g"],
["ġ", "g"],
["ɠ", "g"],
["ḡ", "g"],
["ᶃ", "g"],
["ǥ", "g"],
["ḫ", "h"],
["ȟ", "h"],
["ḩ", "h"],
["ĥ", "h"],
["ⱨ", "h"],
["ḧ", "h"],
["ḣ", "h"],
["ḥ", "h"],
["ɦ", "h"],
["ẖ", "h"],
["ħ", "h"],
["ƕ", "hv"],
["í", "i"],
["ĭ", "i"],
["ǐ", "i"],
["î", "i"],
["ï", "i"],
["ḯ", "i"],
["ị", "i"],
["ȉ", "i"],
["ì", "i"],
["ỉ", "i"],
["ȋ", "i"],
["ī", "i"],
["į", "i"],
["ᶖ", "i"],
["ɨ", "i"],
["ĩ", "i"],
["ḭ", "i"],
["ꝺ", "d"],
["ꝼ", "f"],
["ᵹ", "g"],
["ꞃ", "r"],
["ꞅ", "s"],
["ꞇ", "t"],
["ꝭ", "is"],
["ǰ", "j"],
["ĵ", "j"],
["ʝ", "j"],
["ɉ", "j"],
["ḱ", "k"],
["ǩ", "k"],
["ķ", "k"],
["ⱪ", "k"],
["ꝃ", "k"],
["ḳ", "k"],
["ƙ", "k"],
["ḵ", "k"],
["ᶄ", "k"],
["ꝁ", "k"],
["ꝅ", "k"],
["ĺ", "l"],
["ƚ", "l"],
["ɬ", "l"],
["ľ", "l"],
["ļ", "l"],
["ḽ", "l"],
["ȴ", "l"],
["ḷ", "l"],
["ḹ", "l"],
["ⱡ", "l"],
["ꝉ", "l"],
["ḻ", "l"],
["ŀ", "l"],
["ɫ", "l"],
["ᶅ", "l"],
["ɭ", "l"],
["ł", "l"],
["lj", "lj"],
["ſ", "s"],
["ẜ", "s"],
["ẛ", "s"],
["ẝ", "s"],
["ḿ", "m"],
["ṁ", "m"],
["ṃ", "m"],
["ɱ", "m"],
["ᵯ", "m"],
["ᶆ", "m"],
["ń", "n"],
["ň", "n"],
["ņ", "n"],
["ṋ", "n"],
["ȵ", "n"],
["ṅ", "n"],
["ṇ", "n"],
["ǹ", "n"],
["ɲ", "n"],
["ṉ", "n"],
["ƞ", "n"],
["ᵰ", "n"],
["ᶇ", "n"],
["ɳ", "n"],
["ñ", "n"],
["nj", "nj"],
["ó", "o"],
["ŏ", "o"],
["ǒ", "o"],
["ô", "o"],
["ố", "o"],
["ộ", "o"],
["ồ", "o"],
["ổ", "o"],
["ỗ", "o"],
["ö", "o"],
["ȫ", "o"],
["ȯ", "o"],
["ȱ", "o"],
["ọ", "o"],
["ő", "o"],
["ȍ", "o"],
["ò", "o"],
["ỏ", "o"],
["ơ", "o"],
["ớ", "o"],
["ợ", "o"],
["ờ", "o"],
["ở", "o"],
["ỡ", "o"],
["ȏ", "o"],
["ꝋ", "o"],
["ꝍ", "o"],
["ⱺ", "o"],
["ō", "o"],
["ṓ", "o"],
["ṑ", "o"],
["ǫ", "o"],
["ǭ", "o"],
["ø", "o"],
["ǿ", "o"],
["õ", "o"],
["ṍ", "o"],
["ṏ", "o"],
["ȭ", "o"],
["ƣ", "oi"],
["ꝏ", "oo"],
["ɛ", "e"],
["ᶓ", "e"],
["ɔ", "o"],
["ᶗ", "o"],
["ȣ", "ou"],
["ṕ", "p"],
["ṗ", "p"],
["ꝓ", "p"],
["ƥ", "p"],
["ᵱ", "p"],
["ᶈ", "p"],
["ꝕ", "p"],
["ᵽ", "p"],
["ꝑ", "p"],
["ꝙ", "q"],
["ʠ", "q"],
["ɋ", "q"],
["ꝗ", "q"],
["ŕ", "r"],
["ř", "r"],
["ŗ", "r"],
["ṙ", "r"],
["ṛ", "r"],
["ṝ", "r"],
["ȑ", "r"],
["ɾ", "r"],
["ᵳ", "r"],
["ȓ", "r"],
["ṟ", "r"],
["ɼ", "r"],
["ᵲ", "r"],
["ᶉ", "r"],
["ɍ", "r"],
["ɽ", "r"],
["ↄ", "c"],
["ꜿ", "c"],
["ɘ", "e"],
["ɿ", "r"],
["ś", "s"],
["ṥ", "s"],
["š", "s"],
["ṧ", "s"],
["ş", "s"],
["ŝ", "s"],
["ș", "s"],
["ṡ", "s"],
["ṣ", "s"],
["ṩ", "s"],
["ʂ", "s"],
["ᵴ", "s"],
["ᶊ", "s"],
["ȿ", "s"],
["ɡ", "g"],
["ᴑ", "o"],
["ᴓ", "o"],
["ᴝ", "u"],
["ť", "t"],
["ţ", "t"],
["ṱ", "t"],
["ț", "t"],
["ȶ", "t"],
["ẗ", "t"],
["ⱦ", "t"],
["ṫ", "t"],
["ṭ", "t"],
["ƭ", "t"],
["ṯ", "t"],
["ᵵ", "t"],
["ƫ", "t"],
["ʈ", "t"],
["ŧ", "t"],
["ᵺ", "th"],
["ɐ", "a"],
["ᴂ", "ae"],
["ǝ", "e"],
["ᵷ", "g"],
["ɥ", "h"],
["ʮ", "h"],
["ʯ", "h"],
["ᴉ", "i"],
["ʞ", "k"],
["ꞁ", "l"],
["ɯ", "m"],
["ɰ", "m"],
["ᴔ", "oe"],
["ɹ", "r"],
["ɻ", "r"],
["ɺ", "r"],
["ⱹ", "r"],
["ʇ", "t"],
["ʌ", "v"],
["ʍ", "w"],
["ʎ", "y"],
["ꜩ", "tz"],
["ú", "u"],
["ŭ", "u"],
["ǔ", "u"],
["û", "u"],
["ṷ", "u"],
["ü", "u"],
["ǘ", "u"],
["ǚ", "u"],
["ǜ", "u"],
["ǖ", "u"],
["ṳ", "u"],
["ụ", "u"],
["ű", "u"],
["ȕ", "u"],
["ù", "u"],
["ủ", "u"],
["ư", "u"],
["ứ", "u"],
["ự", "u"],
["ừ", "u"],
["ử", "u"],
["ữ", "u"],
["ȗ", "u"],
["ū", "u"],
["ṻ", "u"],
["ų", "u"],
["ᶙ", "u"],
["ů", "u"],
["ũ", "u"],
["ṹ", "u"],
["ṵ", "u"],
["ᵫ", "ue"],
["ꝸ", "um"],
["ⱴ", "v"],
["ꝟ", "v"],
["ṿ", "v"],
["ʋ", "v"],
["ᶌ", "v"],
["ⱱ", "v"],
["ṽ", "v"],
["ꝡ", "vy"],
["ẃ", "w"],
["ŵ", "w"],
["ẅ", "w"],
["ẇ", "w"],
["ẉ", "w"],
["ẁ", "w"],
["ⱳ", "w"],
["ẘ", "w"],
["ẍ", "x"],
["ẋ", "x"],
["ᶍ", "x"],
["ý", "y"],
["ŷ", "y"],
["ÿ", "y"],
["ẏ", "y"],
["ỵ", "y"],
["ỳ", "y"],
["ƴ", "y"],
["ỷ", "y"],
["ỿ", "y"],
["ȳ", "y"],
["ẙ", "y"],
["ɏ", "y"],
["ỹ", "y"],
["ź", "z"],
["ž", "z"],
["ẑ", "z"],
["ʑ", "z"],
["ⱬ", "z"],
["ż", "z"],
["ẓ", "z"],
["ȥ", "z"],
["ẕ", "z"],
["ᵶ", "z"],
["ᶎ", "z"],
["ʐ", "z"],
["ƶ", "z"],
["ɀ", "z"],
["ff", "ff"],
["ffi", "ffi"],
["ffl", "ffl"],
["fi", "fi"],
["fl", "fl"],
["ij", "ij"],
["œ", "oe"],
["st", "st"],
["ₐ", "a"],
["ₑ", "e"],
["ᵢ", "i"],
["ⱼ", "j"],
["ₒ", "o"],
["ᵣ", "r"],
["ᵤ", "u"],
["ᵥ", "v"],
["ₓ", "x"],
]);
Intl.Collator для этой задачи - во время вопроса и в среде, которая мне была нужна, для этого не было возможности.
Как бы вы использовали Intl.Collator для преобразования нелатинских букв в латинские «эквиваленты»?
Я бы не стал. Первоначальный вопрос касался сортировка списка строк, правильно относящихся к определенному языку. В разных языках строки сортируются по-разному, но простые строки JS не обладают необходимыми знаниями, чтобы делать это правильно. Сопоставление символов с диакритическими знаками и форм без диакритических знаков является обходным решением. При наличии встроенной поддержки сопоставления сопоставление символов становится относительно бесполезной операцией, поскольку оно никогда не может обеспечить правильность и скорость сортировки на основе сопоставления.
Вы ошибаетесь в своем предположении, что пользователь ожидает, что "ä" будет отсортировано с "a". В шведском алфавите 29 букв: abcdefghijklmnopqrstuvwxyzåäö, а также в датском / норвежском: abcdefghijklmnopqrstuvwxyzæøå. Ожидаемый порядок: «Апельсин», «Банан», «Аппле».