Reverse the gender of a string

var male2female = <<'EOD'
  maleS femaleS, maleness femaleness, him her, himself herself, his her, his
  hers, he she, Mr Mrs, Mister Missus, Ms Mr, Master Miss, MasterS MistressES,
  uncleS auntS, nephewS nieceS, sonS daughterS, grandsonS granddaughterS,
  brotherS sisterS, man woman, men women, boyS girlS, paternal maternal,
  grandfatherS grandmotherS, GodfatherS GodmotherS, GodsonS GoddaughterS,
  fiancéS fiancéeS, husband wife, husbands wives, fatherS motherS, bachelorS
  spinsterS, bridegroomS brideS, widowerS widowS, KnightS DameS, Sir DameS,
  KingS QueenS, DukeS DuchessES, PrinceS PrincessES, Lord Lady, Lords Ladies,
  MarquessES MarchionessES, EarlS CountessES, ViscountS ViscountessES, ladS
  lassES, sir madam, gentleman lady, gentlemen ladies, BaronS BaronessES,
  stallionS mareS, ramS eweS, coltS fillieS, billy nanny, billies nannies,
  bullS cowS, godS goddessES, heroS heroineS, shirtS blouseS, undies nickers,
  sweat glow, jackarooS jillarooS, gigoloS hookerS, landlord landlady,
  landlords landladies, manservantS maidservantS, actorS actressES, CountS
  CountessES, EmperorS EmpressES, giantS giantessES, heirS heiressES, hostS
  hostessES, lionS lionessES, managerS manageressES, murdererS murderessES,
  priestS priestessES, poetS poetessES, shepherdS shepherdessES, stewardS
  stewardessES, tigerS tigressES, waiterS waitressES, cockS henS, dogS bitchES,
  drakeS henS, dogS vixenS, tomS tibS, boarS sowS, buckS roeS, peacockS
  peahenS, gander goose, ganders geese, friarS nunS, monkS nunS
EOD
 
var m2f = male2female.split(/,\s*/).map { |tok| tok.words}
 
var re_plural = /E?S\z/
var re_ES = /ES\z/
 
func gen_pluralize(m, f) {
    [
        [m - re_plural, f - re_plural],
        [m.sub(re_ES, 'es'), f.sub(re_ES, 'es')],
        [m.sub(re_plural, 's'), f.sub(re_plural, 's')],
    ]
}
 
var dict = Hash()
 
for m,f in m2f {
    for x,y in gen_pluralize(m, f).map{.map{.lc}} {
        if (x ~~ dict) {
            dict{y} = x
        } else {
            dict{x, y} = (y, x)
        }
    }
}
 
var gen_re = Regex('\b(' + dict.keys.join('|') + ')\b', 'i')
 
func copy_case(orig, repl) {
    var a = orig.chars
    var b = repl.chars
 
    var uc = 0
    var min = [a, b].map{.len}.min
    for i in ^min {
        if (a[i] ~~ /^[[:upper:]]/) {
            b[i].uc!
            ++uc
        }
    }
 
    uc == min ? repl.uc : b.join('')
}
 
func reverse_gender(text) {
    text.gsub(gen_re, { |a| copy_case(a, dict{a.lc}) })
}

Example:

say reverse_gender("She was a soul stripper. She took my heart!")

Output:

He was a soul stripper. He took my heart!

Last updated