アクセント記号付きアルファベットのソート

(405d) 更新

公開メモ

概要

ホームページ上でアクセント記号付きアルファベット(Ë や ö など) を含む文字列を「それらしく」ソートしたい。

アクセント記号付アルファベットのソート順

ここでの「それらしく」の意味は、「見た目が ABC 順になるように」とのこと。

どうやら本来、アクセント記号付文字列のソート順はロケール依存に強く依存していて、 例えばスウェーデンでは "abcdefghijklmnopqrstuvwxyzåäö" の順に並べるのが正しいソート順だったり、 デンマークやノルウェーでは "abcdefghijklmnopqrstuvwxyzæøå" の順に並べるのが正しいソート順だったりするそうなのですが、 国際会議の参加者名などをソートする場合には公用語である英語ロケールでのソートにせざるを得ないので、 ここでは

「アクセント記号を取り去った上でソートする」

を「正しいソート順」としておきます。

html エンティティ

オンラインフォームから名前を入力してもらう場合、 文字コードを UTF-8 にしておけばアクセント記号付アルファベットをそのまま入力できるのですが、 人によってはわざわざ html エンティティの形で入力してくれる場合があります。

例えば、Ö を Ö とか Ö とか Ö とか書いても、ちゃんと Ö として認識しなければならない。

やりたいこと

なので、やりたいのは、

  • html エンティティを UTF-8 にデコードする
  • UTF-8 文字列からアクセント記号を取り去る
  • 大文字小文字を無視して並べる場合には、さらに大文字(あるいは小文字)に変換

html エンティティを UTF-8 にデコードする

https://stackoverflow.com/questions/7394748/whats-the-right-way-to-decode-a-string-that-has-special-html-entities-in-it

こちらを参考に、he というライブラリを使うのが簡単のようです。

https://github.com/mathiasbynens/he

LANG:html
<script src="https://cdn.rawgit.com/mathiasbynens/he/670991a4/he.js"></script>

としておき、

LANG:javascript
string_without_html_entity = he.decode(string_with_html_entity);

のようにしてデコードできます。

UTF-8 文字列からアクセント記号を取り去る

https://stackoverflow.com/questions/990904/remove-accents-diacritics-in-a-string-in-javascript

こちらを参考に、ES6 から使える String.prototype.normalize により、 アクセント記号をアルファベットから分離し、 さらにアクセント記号を消去するのが簡単なようです。

LANG:javascript
str.normalize('NFD').replace(/[\u0300-\u036f]/g, '')

ただ、String.prototype.normalize はまだブラウザ上で自由に使えるわけではないため、

https://github.com/walling/unorm/

のようなライブラリで polyfill しておく必要があります。

LANG:html
<script src="https://cdn.rawgit.com/walling/unorm/219b1c17/lib/unorm.js"></script>

大文字小文字を無視して並べる場合には、さらに大文字(あるいは小文字)に変換

String.prototype.toUpperCase を使います。

最終コード

LANG:html
<script src="https://cdn.rawgit.com/mathiasbynens/he/670991a4/he.js"></script>
<script src="https://cdn.rawgit.com/walling/unorm/219b1c17/lib/unorm.js"></script>

しておき、

LANG:javascript
function normalizeString(s) {
  // html エンティティを UTF-8 に直す
  // アクセント記号を除去する
  // 大文字に直す
  return he.decode(s).normalize('NFD').replace(/[\u0300-\u036f]/g, '').toUpperCase();
}

function compareString(a,b) {
  var na = normalizeString(a);
  var nb = normalizeString(b);
  if (na==nb) return 0;
  return na < nb ? -1 : 1;
}

arrayOfAccentedStrings.sort(compareString);

とします。

CoffeeScript なら、

LANG:coffee
   normalizeString = (s)->
     he.decode(s).normalize('NFD').replace(/[\u0300-\u036f]/g, '').toUpperCase()

   compareString = (a,b)->
     na = normalizeString(a)
     nb = normalizeString(b)
     return 0 if na == nb
     if na < nb then -1 else 1

   arrayOfAccentedStrings.sort(compareString)

ですね。

コメント・質問





Counter: 601 (from 2010/06/03), today: 1, yesterday: 4