• Jump To … +
    abbreviations.js adjectives.js convertables.js dates.js demonyms.js firstnames.js honourifics.js irregular_nouns.js irregular_verbs.js misc.js multiples.js numbers.js organisations.js phrasal_verbs.js places.js uncountables.js verbs.js fns.js index.js lexicon.js negate.js passive_voice.js contractions.js fancy_lumping.js grammar_rules.js parts_of_speech.js phrasal_verbs.js tagger.js word_rules.js question.js sentence.js statement.js tense.js adjective.js to_adverb.js to_comparative.js to_noun.js to_superlative.js adverb.js to_adjective.js is_acronym.js article.js date.js date_rules.js is_date.js parse_date.js is_plural.js is_uncountable.js noun.js is_organisation.js organisation.js gender.js is_person.js parse_name.js person.js is_place.js place.js pluralize.js pronoun.js singularize.js is_value.js numbers.js to_number.js units.js value.js term.js conjugate.js from_infinitive.js predict_form.js suffix_rules.js to_actor.js to_infinitive.js negate.js verb.js sentence_parser.js text.js
  • singularize.js

  • ¶
    'use strict';
    const is_uncountable = require('./is_uncountable.js');
    const irregulars = require('../../data/irregular_nouns.js');
    const is_plural = require('./is_plural.js');
    const fns = require('../../fns.js');
    
    const singularize_rules = [
      [/([^v])ies$/i, '$1y'],
      [/ises$/i, 'isis'],
      [/ives$/i, 'ife'],
      [/(antenn|formul|nebul|vertebr|vit)ae$/i, '$1a'],
      [/(octop|vir|radi|nucle|fung|cact|stimul)(i)$/i, '$1us'],
      [/(buffal|tomat|tornad)(oes)$/i, '$1o'],
      [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '$1sis'],
      [/(vert|ind|cort)(ices)$/i, '$1ex'],
      [/(matr|append)(ices)$/i, '$1ix'],
      [/(x|ch|ss|sh|s|z|o)es$/i, '$1'],
      [/men$/i, 'man'],
      [/(n)ews$/i, '$1ews'],
      [/([ti])a$/i, '$1um'],
      [/([^f])ves$/i, '$1fe'],
      [/([lr])ves$/i, '$1f'],
      [/([^aeiouy]|qu)ies$/i, '$1y'],
      [/(s)eries$/i, '$1eries'],
      [/(m)ovies$/i, '$1ovie'],
      [/([m|l])ice$/i, '$1ouse'],
      [/(cris|ax|test)es$/i, '$1is'],
      [/(alias|status)es$/i, '$1'],
      [/(ss)$/i, '$1'],
      [/(ics)$/i, '$1'],
      [/s$/i, '']
    ].map(function(a) {
      return {
        reg: a[0],
        repl: a[1]
      };
    });
    
    const singularize = function(str) {
      const low = str.toLowerCase();
  • ¶

    uncountable

      if (is_uncountable(low)) {
        return str;
      }
  • ¶

    is it already singular?

      if (is_plural(low) === false) {
        return str;
      }
  • ¶

    irregular

      const found = irregulars.filter(function(r) {
        return r[1] === low;
      });
      if (found[0]) {
        if (fns.titlecase(low) === str) { //handle capitalisation properly
          return fns.titlecase(found[0][0]);
        }
        return found[0][0];
      }
  • ¶

    inflect first word of preposition-phrase

      if (str.match(/([a-z]*) (of|in|by|for) [a-z]/)) {
        const first = str.match(/^([a-z]*) (of|in|by|for) [a-z]/);
        if (first && first[1]) {
          const better_first = singularize(first[1]);
          return better_first + str.replace(first[1], '');
        }
      }
  • ¶

    regular

      for (let i = 0; i < singularize_rules.length; i++) {
        if (str.match(singularize_rules[i].reg)) {
          return str.replace(singularize_rules[i].reg, singularize_rules[i].repl);
        }
      }
      return str;
    };
  • ¶

    console.log(singularize(‘gases’) === “gas”) console.log(singularize(‘kisses’) === “kiss”) console.log(singularize(‘kiss’) === “kiss”) console.log(singularize(‘children’) === “child”) console.log(singularize(‘peace’) === “peace”) console.log(singularize(‘child’) === “child”) console.log(singularize(‘mayors of chicago’) === “mayor of chicago”)

    module.exports = singularize;