Skip to content

Instantly share code, notes, and snippets.

@alastairtree
Last active October 2, 2022 07:48
Show Gist options
  • Save alastairtree/7ea4f55edfcd49fe3f73ad60dfdbd5cd to your computer and use it in GitHub Desktop.
Save alastairtree/7ea4f55edfcd49fe3f73ad60dfdbd5cd to your computer and use it in GitHub Desktop.
Detecting plurals
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace LanguageUtils
{
public static class PluralisationHelper
{
private static readonly Dictionary<string, string> KnownCommonPluralsDictionary = new Dictionary<string, string>
{
{"children", "child"},
{"people", "person"}
};
/// <summary>
/// This is far from perfect but detects the majority of english language plurals
/// See https://en.wikipedia.org/wiki/English_plurals#Irregular_plurals and
/// and http://grammar.yourdictionary.com/grammar-rules-and-tips/irregular-plurals.html
/// for useful example cases.
/// </summary>
public static bool IsPlural(string plural, string singular)
{
// dogs => dog
if (plural.EndsWith("s") && singular == ReplaceEnd(plural, "s"))
return true;
// babies => baby
if (singular.EndsWith("y") && plural == ReplaceEnd(singular, "y", "ies"))
return true;
// catches => catch
if (plural.EndsWith("es") && singular == ReplaceEnd(plural, "es"))
return true;
// people => person and other "one off" edge cases
if (KnownCommonPluralsDictionary.ContainsKey(plural) && KnownCommonPluralsDictionary[plural] == singular)
return true;
//series => series
if (plural.EndsWith("ies") && plural == singular)
return true;
//statuses => status
if (plural.EndsWith("uses") && singular == ReplaceEnd(plural, "es"))
return true;
//synopses => synopsis
if (plural.EndsWith("ses") && singular == ReplaceEnd(plural, "es", "is"))
return true;
//wives => wife
if (singular.EndsWith("fe") && plural == ReplaceEnd(singular, "fe", "ves"))
return true;
//halves => half
if (singular.EndsWith("f") && plural == ReplaceEnd(singular, "f", "ves"))
return true;
//women => woman
if (singular.EndsWith("man") && plural == ReplaceEnd(singular, "man", "men"))
return true;
//vertices => vertex
if (singular.EndsWith("ex") && plural == ReplaceEnd(singular, "ex", "ices"))
return true;
//matricies => matrix
if (singular.EndsWith("ix") && plural == ReplaceEnd(singular, "ix", "ices"))
return true;
//indicies => index
if (singular.EndsWith("ex") && plural == ReplaceEnd(singular, "ex", "ices"))
return true;
//axes => axis
if (singular.EndsWith("is") && plural == ReplaceEnd(singular, "is", "es"))
return true;
//medium => media
if (singular.EndsWith("um") && plural == ReplaceEnd(singular, "um", "a"))
return true;
return false;
}
private static string ReplaceEnd(string word, string trimString, string replacement = "")
{
return Regex.Replace(word, $"{trimString}$", replacement);
}
}
}
using System.Collections.Generic;
using NUnit.Framework;
namespace LanguageUtils
{
[TestFixture]
public class PluralisationHelpertests
{
[TestCase("Synopses", "Synopsis", true)]
[TestCase("Men", "Man", true)]
[TestCase("Women", "Woman", true)]
[TestCase("People", "Person", true)]
[TestCase("Children", "Child", true)]
[TestCase("Synopses", "NotSynopses", false)]
[TestCase("Synopses", "SynopsesNot", false)]
[TestCase("Codes", "Code", true)]
[TestCase("Babies", "Baby", true)]
[TestCase("Boxes", "Box", true)]
[TestCase("Wives", "Wife", true)]
[TestCase("Tomatoes", "Tomato", true)]
[TestCase("Catches", "Catch", true)]
[TestCase("Statuses", "Status", true)]
[TestCase("Urls", "Url", true)]
[TestCase("Ids", "Id", true)]
[TestCase("Types", "Type", true)]
[TestCase("Types", "TypeNote", false)]
[TestCase("Series", "Series", true)]
[TestCase("indexes", "index", true)]
[TestCase("indices", "index", true)]
[TestCase("media", "medium", true)]
[TestCase("viruses", "virus", true)]
[TestCase("halves", "half", true)]
public void IdentifyCommonPluralsCorrectly(string plural, string singular, bool isPlural)
{
var actual = PluralisationHelper.IsPlural(plural,singular);
Assert.AreEqual(isPlural, actual, $"{plural} is{(isPlural ? "" : " not")} a plural of {singular}");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment