135 Stimmen

Alle akzentuierten Zeichen in einer Zeichenkette effizient ersetzen?

Für die Umsetzung eines armen Mannes von in der Nähe von -korrekte Sortierung auf der Client-Seite Ich brauche eine JavaScript-Funktion, die effizient Ersetzung einzelner Zeichen in einer Zeichenkette.

Hier ist, was ich meine (beachten Sie, dass dies für den deutschen Text gilt, andere Sprachen sortieren anders):

native sorting gets it wrong: a b c o u z ä ö ü
collation-correct would be:   a ä b c o ö u ü z

Im Grunde muss ich alle Vorkommen von "ä" in einer gegebenen Zeichenkette durch "a" (und so weiter) ersetzen. Auf diese Weise würde das Ergebnis der nativen Sortierung sehr nahe an dem liegen, was ein Benutzer erwarten würde (oder was eine Datenbank zurückgeben würde).

In anderen Sprachen ist dies möglich: Python-Lieferungen str.translate() , in Perl gibt es tr/…/…/ , XPath hat eine Funktion translate() , ColdFusion hat ReplaceList() . Aber was ist mit JavaScript?

Hier ist, was ich im Moment habe.

// 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]; 
  }) );
}

Zunächst einmal gefällt mir nicht, dass die Regex bei jedem Funktionsaufruf neu aufgebaut wird. Ich schätze, dass eine Schließung in dieser Hinsicht helfen kann, aber ich scheine aus irgendeinem Grund nicht den Dreh raus zu haben.

Fällt jemandem etwas Effizienteres ein?


Die folgenden Antworten fallen in zwei Kategorien:

  1. Funktionen zur Ersetzung von Zeichenketten mit unterschiedlichem Grad an Vollständigkeit und Effizienz (worum ich ursprünglich gebeten hatte)
  2. A späte Erwähnung von String#localeCompare die jetzt weitgehend unterstützt unter den JS-Engines (nicht so sehr zum Zeitpunkt der Frage) und könnte diese Art von Problem viel eleganter lösen.

12 Stimmen

Sie liegen falsch mit Ihrer Annahme, dass ein Benutzer erwartet, dass "ä" mit "a" sortiert wird. Das schwedische Alphabet hat 29 Buchstaben: abcdefghijklmnopqrstuvwxyzåäö und das dänische/norwegische auch: abcdefghijklmnopqrstuvwxyzæøå. Die erwartete Reihenfolge ist: "Apelsin", "Banan", "Äpple".

1 Stimmen

Ich weiß. Die Lösung war für die Sortierung deutscher Texte gedacht. Auch dort ist sie nicht richtig aber gut genug für den Anwendungsfall. Diese Frage war nie als Suche nach dem "löst alle Probleme"-Algorithmus gedacht.

1 Stimmen

Ich habe die Frage ein wenig umformuliert, um das von Anfang an klar zu machen.

6voto

Jan Hagge Punkte 77

Ich habe eine Prototyp-Version davon erstellt:

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];})
    );
};

Verwenden Sie wie:

var teststring = 'ä ö ü Ä Ö Ü ß';
teststring.strip();

Dadurch wird die Zeichenfolge in a_o_u_A_O_U_ss geändert.

0 Stimmen

Das nicht funktioniert. Wenn ich jedoch var newstr = teststring.strip(); et console.log() das, dann funktioniert es. jsfiddle . Danke Mann, das ist die prägnanteste und lesbarste Methode.

5voto

Adam Pietrasiak Punkte 11497

Auf der Grundlage bestehender Antworten und einiger Vorschläge habe ich diese erstellt:

String.prototype.removeAccents = function() {

    var removalMap = {
        'A'  : /[AÀÁÂÃÄÅ]/g,
        'AA' : /[]/g,
        'AE' : /[Æ]/g,
        'AO' : /[]/g,
        'AU' : /[]/g,
        'AV' : /[]/g,
        'AY' : /[]/g,
        'B'  : /[B]/g,
        'C'  : /[CÇ]/g,
        'D'  : /[D]/g,
        'DZ' : /[]/g,
        'Dz' : /[]/g,
        'E'  : /[EÈÉÊË]/g,
        'F'  : /[F]/g,
        'G'  : /[G]/g,
        'H'  : /[H]/g,
        'I'  : /[IÌÍÎÏ]/g,
        'J'  : /[J]/g,
        'K'  : /[K]/g,
        'L'  : /[L]/g,
        'LJ' : /[]/g,
        'Lj' : /[]/g,
        'M'  : /[M]/g,
        'N'  : /[NÑ]/g,
        'NJ' : /[]/g,
        'Nj' : /[]/g,
        'O'  : /[OÒÓÔÕÖØ]/g,
        'OI' : /[]/g,
        'OO' : /[]/g,
        'OU' : /[]/g,
        'P'  : /[P]/g,
        'Q'  : /[Q]/g,
        'R'  : /[R]/g,
        'S'  : /[SŠ]/g,
        'T'  : /[T]/g,
        'TZ' : /[]/g,
        'U'  : /[UÙÚÛÜ]/g,
        'V'  : /[V]/g,
        'VY' : /[]/g,
        'W'  : /[W]/g,
        'X'  : /[X]/g,
        'Y'  : /[Y]/g,
        'Z'  : /[ZŽ]/g,
        'a'  : /[aàáâãäå]/g,
        'aa' : /[]/g,
        'ae' : /[æ]/g,
        'ao' : /[]/g,
        'au' : /[]/g,
        'av' : /[]/g,
        'ay' : /[]/g,
        'b'  : /[b]/g,
        'c'  : /[cç]/g,
        'd'  : /[d]/g,
        'dz' : /[]/g,
        'e'  : /[eèéêë]/g,
        'f'  : /[fƒ]/g,
        'g'  : /[g]/g,
        'h'  : /[h]/g,
        'hv' : /[]/g,
        'i'  : /[iìíîï]/g,
        'j'  : /[j]/g,
        'k'  : /[k]/g,
        'l'  : /[l]/g,
        'lj' : /[]/g,
        'm'  : /[m]/g,
        'n'  : /[nñ]/g,
        'nj' : /[]/g,
        'o'  : /[oòóôõöø]/g,
        'oi' : /[]/g,
        'ou' : /[]/g,
        'oo' : /[]/g,
        'p'  : /[p]/g,
        'q'  : /[q]/g,
        'r'  : /[r]/g,
        's'  : /[s]/g,
        't'  : /[t]/g,
        'tz' : /[]/g,
        'u'  : /[uùúûü]/g,
        'v'  : /[v]/g,
        'vy' : /[]/g,
        'w'  : /[w]/g,
        'x'  : /[x]/g,
        'y'  : /[yýÿ]/g,
        'z'  : /[zž]/g,
    };

    var str = this;

    for(var latin in removalMap) {
      var nonLatin = removalMap[latin];
      str = str.replace(nonLatin , latin);
    }

    return str;
}

Es verwendet echte Zeichen anstelle einer Unicode-Liste und funktioniert gut.

Sie können es verwenden wie

"".removeAccents(); // returns "aaa"

Sie können diese Funktion leicht so umwandeln, dass sie kein String-Prototyp ist. Da ich jedoch ein Fan der Verwendung von String-Prototypen in solchen Fällen bin, müssen Sie es selbst tun.

1 Stimmen

Leider ist dies vergleichsweise ineffizient, wenn man so viele Regexe in einer Schleife hat.

4voto

yckart Punkte 29968

Eine direkte Portierung von Kierons Lösung nach 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 = ['À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 'Ð', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'ß', 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'ÿ', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'Œ', 'œ', '', '', '', '', '', '', '', '', '', '', '', '', 'Š', 'š', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'Ÿ', '', '', '', '', 'Ž', 'ž', '', 'ƒ', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''];
    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;
    };
}());

Und eine leicht geänderte Version, die eine Char-Map anstelle von zwei Arrays verwendet:

Um diese beiden Methoden zu vergleichen, habe ich ein einfaches Benchmarking durchgeführt: 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",
            "": "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"
        },
        nonWord = /\W/g,
        mapping = function (c) {
            return map[c] || c; 
        };

    return function (str) {
        return str.replace(nonWord, mapping);
    };
}());

0 Stimmen

Dadurch wird die Zeichentabelle mit jedem Aufruf von replace() genau das, was ich zu vermeiden versucht habe. Die Verwendung von /\W/ ist eine nette Idee, auch wenn es versucht, alle Leerzeichen, Ziffern und Satzzeichen zu ersetzen.

0 Stimmen

Der erste Punkt lässt sich leicht lösen, indem man die Karte und die Ersetzungsfunktion zu einer äußeren Schließung hinzufügt, wie ich es gerade getan habe.

0 Stimmen

...ich verstehe Ihren letzten Edit nicht. Warum haben Sie die Ersetzungsfunktion aus dem Verschluss entfernt?

4voto

dinigo Punkte 5732

Ich wollte nur meine Lösung veröffentlichen, die String#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.log(`fix(${str}) = ${fix(str)}`);

4voto

Prinzhorn Punkte 21479

In keiner einzigen Antwort wird erwähnt String.localeCompare die zufällig genau das tut, was Sie ursprünglich wollten, aber nicht das, worum Sie bitten.

var list = ['a', 'b', 'c', 'o', 'u', 'z', 'ä', 'ö', 'ü'];

list.sort((a, b) => a.localeCompare(b));

console.log(list);

//Outputs ['a', 'ä', 'b', 'c', 'o', 'ö', 'u', 'ü', 'z']

Der zweite und dritte Parameter werden jedoch von älteren Browsern nicht unterstützt. Dennoch ist diese Option eine Überlegung wert.

0 Stimmen

Schöner Zusatz! In diesem speziellen Fall hatte ich keinen Einfluss auf wie die Zeichenketten verglichen wurden, da dies von TableSorter intern durchgeführt wird. Ich konnte nur beeinflussen was Strings, die ich verwenden möchte. Daher war der Austausch der Saiten die einzige Möglichkeit. Vielleicht haben modernere Versionen von TableSorter eine bessere interne Handhabung dieser Dinge.

0 Stimmen

Ich habe diese Antwort auf die Frage besonders hervorgehoben.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X