Node.js, MeCabを使用した日本語の辞書順ソート用compare関数を実装しました。
漢字、ひらがな、カタカナに対応しています。
String#localeCompare()
は概ね辞書順に動きますが、濁音半濁音を無視します。
これを補完し、漢字の読みにも対応しました。
コード全体はこちらにおいてあります。
必須環境
- Node.js
- MeCab
$ npm install is-windows encoding-japanese --save
使い方
const Comparator = require('./japanese-comparator')
const list = ['b', 'a', '2', '1', '+', '-', '-2', 'あがき', '悪', '赤城']
// get関数は引数なしでも動作しますが、リストを渡してキャッシュを作ったほうが早くなります
const compare = new Comparator().get(list)
console.log(list.sort(compare))
/*
[
'-', '-2', '+',
'1', '2', 'a',
'b', '赤城', 'あがき',
'悪'
]
*/
内部の実装
function hiraToKana(str) {
return str.replace(/[\u3041-\u3096]/g, function(match) {
var chr = match.charCodeAt(0) + 0x60;
return String.fromCharCode(chr);
});
}
ひらがなをカタカナに変換します。
const dakuMap = hiraToKana('かが きぎ くぐ けげ こご さざ しじ すず せぜ そぞ ただ ちぢ つづ てで とど はばぱ ひびぴ ふぶぷ へべぺ ほぼぽ')
.split(/\s/g)
.reduce((prev, current) => {
[...current].map(c => {
prev[c] = current
})
return prev
}, {})
濁音半濁音の文字の順序を定義しています。
function compareKana(a, b) {
if (a[0] === undefined && b[0] === undefined) {
return 0
} else if (a[0] === undefined) {
return -1
} else if (b[0] === undefined) {
return 1
} else if (a[0] === b[0]) {
return compareKana(a.substring(1), b.substring(1))
} else if (
dakuMap[a[0]] && dakuMap[b[0]] && dakuMap[a[0]] === dakuMap[b[0]]
) {
return dakuMap[a[0]].indexOf(a[0]) - dakuMap[a[0]].indexOf(b[0])
} else {
return a[0].localeCompare(b[0], 'ja')
}
}
カタカナの文字列同士を比較します。
if (isWindows()) {
this.detected = Encoding.detect(exec(`echo テキストエンコーディングのテスト | mecab -Oyomi`))
}
...
Encoding.convert(exec(command), {
from: this.detected,
type: 'string'
})
Windowsでは何も考えずにmecab
コマンドを叩くと出力がShift-JIS
になるので変換しています。
createCache(list) {
list = this.preprocess(list)
const yomiAll = this.kanjiToKana(list.join(this.splitter), true).split(this.splitter)
console.log(yomiAll)
list.forEach((str, i) => {
this.cache[str] = yomiAll[i]
})
}
compare
関数が呼ばれるたびにmecab
コマンドを叩いていると日が暮れてしまうので、
ソートする文字列リストを全結合し、一回だけmecab
を叩き、再度分割、結果をキャッシュする仕組みを入れました。
以上です。