単数形を複数形に変換するJavaScript(複数形から単数形も)

追記:id:amachangさんからアドバイスをもらったので書き直してみました!
大きなデータはプロトタイプで持つとオブジェクトを作るたびに生成しなくて済むのでメモリに優しくなる、という理解ですがいいんだろうか・・・
ついでに複数形から単数形に変換するsingularize()メソッドも追加しました


JavaScriptの勉強を始めたので、何か書こう。
配列の扱いと正規表現的なとこを確認したので、単数形と複数形に変換するソースを書いてみる。
といっても、cakePHPのinflector.phpを参考というかJavaScriptで書き換えてみただけ!!

  • inflector.js
function Inflector() {}

Inflector.prototype.pluralRules = {
  pluralRules : {
    '/(s)tatus$/i':'RegExp.$1+"tatuses"',
    '/^(ox)$/i':'RegExp.$1+"en"',
    '/([m|l])ouse$/i':'RegExp.$1+"ice"',
    '/(matr|vert|ind)ix|ex$/i':'RegExp.$1+"ices"',
    '/(x|ch|ss|sh)$/i':'RegExp.$1+"es"',
    '/(r|t|c)y$/i':'RegExp.$1+"ies"',
    '/(hive)$/i':'RegExp.$1+"s"',
    '/(?:([^f])fe|([lr])f)$/i':'RegExp.$1+RegExp.$2+"ves"',
    '/(.*)sis$/i':'RegExp.$1+"ses"',
    '/([ti])um$/i':'RegExp.$1+"a"',
    '/(buffal|tomat)o$/i':'RegExp.$1+"oes"',
    '/(bu)s$/i':'RegExp.$1+"ses"',
    '/(alias)/i':'RegExp.$1+"es"',
    '/(octop|vir)us$/i':'RegExp.$1+"i"',
    '/(.*)s$/i':'RegExp.$1+"s"',
    '/(.*)/i':'RegExp.$1+"s"'
  },
  singularRules : {
    '/(s)tatuses$/i':'RegExp.$1+"tatus"',
    '/^(ox)en$/i':'RegExp.$1',
    '/([m|l])ice$/i':'RegExp.$1+"ouse"',
    '/(matr)ices$/i':'RegExp.$1+"ix"',
    '/(vert|ind)ices$/i':'RegExp.$1+"ex"',
    '/(cris|ax|test)es$/i':'RegExp.$1+"is"', 
    '/(x|ch|ss|sh)es$/i':'RegExp.$1',
    '/(r|t|c)ies$/i':'RegExp.$1+"y"',
    '/(movie)s$/i':'RegExp.$1',
    '/(hive)s$/i':'RegExp.$1',
    '/([^f])ves$/i':'RegExp.$1+"fe"',
    '/([lr])ves$/i':'RegExp.$1+"f"',
    '/(analy|ba|diagno|parenthe|synop|the)ses$/i':'RegExp.$1+"sis"',
    '/([ti])a$/i':'RegExp.$1+"um"',
    '/(buffal|tomat)oes$/i':'RegExp.$1+"o"',
    '/(bu)ses$/i':'RegExp.$1+"s"',
    '/(alias)es/i':'RegExp.$1',
    '/(octop|vir)i$/i':'RegExp.$1+"us"',
    '/(.*)s$/i':'RegExp.$1',
    '/(.*)/i':'RegExp.$1'
  },
  uninflected : [
    'deer', 'fish', 'measles', 'ois', 'pox', 'rice', 'sheep', 'Amoyese', 'bison', 'bream', 'buffalo', 'cantus', 'carp', 'cod', 'coitus', 'corps', 'diabetes', 'elk', 'equipment', 'flounder', 'gallows', 'Genevese', 'Gilbertese', 'graffiti', 'headquarters', 'herpes', 'information', 'innings', 'Lucchese', 'mackerel', 'mews', 'moose', 'mumps', 'news', 'nexus', 'Niasese', 'Pekingese', 'Portuguese', 'proceedings', 'rabies', 'salmon', 'scissors', 'series', 'shears', 'siemens', 'species', 'testes', 'trousers', 'trout', 'tuna', 'whiting', 'wildebeest', 'Yengeese'
  ],
  pluralIrregular : {
    'atlas':'atlases',  'child':'children',
    'corpus':'corpuses', 'ganglion':'ganglions',
    'genus':'genera', 'graffito':'graffiti',
    'leaf':'leaves', 'man':'men', 
    'money':'monies', 'mythos':'mythoi', 
    'numen':'numina', 'opus':'opuses',
    'penis':'penises', 'person':'people',
    'sex':'sexes', 'soliloquy':'soliloquies',
    'testis':'testes', 'woman':'women', 
    'move':'moves'
  },
  singularIrregular : {
    'atlases':'atlas', 'children':'child',
    'corpuses':'corpus', 'ganglions':'ganglion',
    'genera':'genus', 'graffiti':'graffito',
    'leaves':'leaf', 'men':'man', 
    'monies':'money', 'mythoi':'mythos',
    'numina':'numen', 'opuses':'opus',
    'penises':'penises', 'people':'person',
    'sexes':'sex', 'soliloquies':'soliloquy',
    'testes':'testis', 'women':'woman',
    'moves':'move'
  }
}

Inflector.prototype.pluralize = function(word) {
  var word = word;
  for(i in this.pluralRules['uninflected']) {
    if(word.toLowerCase() == this.pluralRules['uninflected'][i]){
      return word;
    }
  }
  for(i in this.pluralRules['pluralIrregular']) {
    if(word.toLowerCase() == i) {
      return word = this.pluralRules['pluralIrregular'][i];
    }
  }
  for(i in this.pluralRules['pluralRules']) {
    try{
      var rObj = eval("new RegExp(" +i+ ");");
      if(word.match(rObj)) {
        word = word.replace(rObj, eval(this.pluralRules['pluralRules'][i]));
        return word;
      }
    }catch(e){
      alert(e.description);
    }
  }
  return word;
}

Inflector.prototype.singularize = function(word) {
  var word = word;
  for(i in this.pluralRules['uninflected']) {
    if(word.toLowerCase() == this.pluralRules['uninflected'][i]){
      return word;
    }
  }
  for(i in this.pluralRules['singularIrregular']) {
    if(word.toLowerCase() == i) {
      return word = this.pluralRules['singularIrregular'][i];
    }
  }
  for(i in this.pluralRules['singularRules']) {
    try{
      var rObj = eval("new RegExp(" +i+ ");");
      if(word.match(rObj)) {
        word = word.replace(rObj, eval(this.pluralRules['singularRules'][i]));
        return word;
      }
    }catch(e){
      alert(e.description);
    }
  }
  return word;
}

Inflector.prototype.addPluralRule = function(pattern, replace) {
  this.pluralRules['pluralRules']["'"+pattern+"'"] = "'"+replace+"'";
}
Inflector.prototype.addSingularRule = function(pattern, replace) {
  this.pluralRules['singularRules']["'"+pattern+"'"] = "'"+replace+"'";
}
Inflector.prototype.addUninflectedRule = function(pattern) {
  this.pluralRules['uninflected'].push("'"+pattern+"'");
}
Inflector.prototype.addPluralIrregularRule = function(pattern, replace) {
  this.pluralRules['pluralIrregular']["'"+pattern+"'"] = "'"+replace+"'";
}
Inflector.prototype.addSingularIrregularRule = function(pattern, replace) {
  this.pluralRules['sinularIrregular']["'"+pattern+"'"] = "'"+replace+"'";
}
  • test.js
function exchange(){
  var inflector = new Inflector();
  var word = "dependency";
  var reWord = inflector.pluralize(word);
  alert("word : " + reWord);
  var reWord = inflector.singularize(reWord);
  alert("word : " + reWord);
}
  • 結果
word : dependencies
word : dependency

おー複数形に変換された。
こうやってeval使っていいんだろか。。。
配列に正規表現のオブジェクトをそのまま入れるとエラーが出る。文字列以外入れちゃダメなのかな。