# Replacing words with their abbreviations - Follow up

This is the follow up for a question you can find here: replacing words with their abbreviations

The goal here is to compare two petential way of answering said question that was:

This particular function's goal is to replace words with their abbreviations by compairing each word with all of those in the config file and replacing whenever a match is found, the goal is to keep a general idea of what the string meant so its not a problem that a word once replaced could ressemble an other as long as they refer to roughly the same thing.
String is always upper case and free of special characters.

The two functions are:

From @Maarten Fabré:

def shorten_words(abbreviations, line, max_length=38):
replacements = set()
while len(line) > max_length:
for word in line.split(" "):
if (
word[-1] == "S"
and word not in abbreviations
and word[:-1] in abbreviations
):
word = word[:-1]
if word not in replacements and word in abbreviations:
line = line.replace(word, abbreviations[word])
if word == abbreviations[word]:
break
return line


and:

def shorten_words(abbreviations_file, sentence):
"""Shorten string sentence using the dictionnary_like object abbreviations."""
abbreviations = set(abbreviations_file)
word_list = sentence.split(' ')
size = len(sentence)
resultat = []
for word in word_list:
if word[-1] == "S" and word not in abbreviations and word[:-1] in abbreviations:
word = word[:-1]
if word.lower() not in abbreviations or size <= POSTAL_LINE_LENGTH:
resultat.append(word)
else:
resultat.append(abbreviations_file[word])
size -= len(word) - len(abbreviations_file[word])
return ' '.join(resultat)


The two main point of comparison are performance and readablility, those answer may still be reviewed as usual.

here is a sample of the configfile (some lines have key and value equal this is meant to remove plural):

[abbreviation]
AVANCEE = AVANC
COMPOSANT = COMPO
VERT = VERT
AGRAIRE = AGRAIR
MECANIQUE = MECA
CARROSSERIE = CARROS
SIGNALISATION = SIGNAL
FOURNITURE = FOURNI
LAITIERE = LAIT
INTERPROFESSIONNEL = INTRPRO
ATLANTIQUE = ATLAN
REALISATION = REAL
INCENDIE = INCEND
MARBRERIE = MARB
FUNEBRE = FUNEBR
POMPE = POMPE
ANTICIPATION = ANTICIP
OBJET = OBJET
ANTIQUITE = ANTIQ
MOBILITE = MOBIL
ASSOCIATIF = ASSO
ANCIENNE = ANC
TELECOMMUNICATION = TELECOM
RESEAUX = RESEAU
LOCALE = LOCAL
RESPIRE = RESPI
QUAND = QND
CHRETIENNE = CHRET
OUVRIERE = OUVRI
JEUNESSE = JEUNE
INTERCULTUREL = INTRCULT
VALORISATION = VALOR
ALIMENTAIRE = ALIMEN
COMMUNALE = COMMUNE
LAIQUE = LAIQ
CASSATION = CASS
TRAVAUX = TRAVAU
ONCOLOGIE = ONCO
RELIGION = RELIG
PLURALISME = PLURAL
FLOTTANTE = FLOT
EOLIENNE = EOLIEN
HUMAINE = HUMAIN
POTENTIEL = POTENT
AMELIORATION = AMELIO
MUSIQUE = MUSIQ
MUNICIPALE = MUNI
EVANGELIQUE = EVANG
BIOLOGISTE = BIOLOG
REPUBLICAIN = REPU
SYMPATHISANT = SYMPAT
ELU = ELU
INTERCONNEXION = INTRCONN
CONSULTANT = CONSULT
ORGANIZATION = ORGA
OLYMPIQUE = OLYMP
CAPACITE = CAPA
RENFORCEMENT = RENFOR
CLEF = CLEF
FRIGORIFIQUE = FRIGO
ENTREPOSAGE = ENTREPO
COLLABORATIF = COLLAB
TROUBLE = TROUBL
ENTRAIDE = ENTRAID
REPRESENTANT = REPRESENT
FOLKLORIQUE = FOLKLO
AMI = AMI
EMPEREURS = EMPER
CONFRERIE = CONFRER
SOUTENUE = SOUTENU
LISTE = LIST
ELECTION = ELECT
ELECTORALE = ELECT
FINANCEMENT = FINANC
CATHOLIQUE = CATHO
HARMONIE = HARMO
DEBOUT = DEBOU
VENT = VENT
CERCLE = CERCL
FOOTBALL = FOOT
IMPROVISATION = IMPROV
POPULAIRE = POPU
SECOURS = SECOUR
ART = ART
DRAMATURGIE = DRAMA
POETIQUE = POET
TRAVAILLANT = TRAVAIL
SYNCHRONISEE = SYNCHRO
NATATION = NATA
LOCATAIRES = LOCAT
AMICALE = AMICA
DEPARTEMENT = DEPART
INDISCIPLINEE = INDISCIPL
PARTAGE = PARTA
MEDIATION = MEDIAT
CITOYEN = CITOY
CULTIVONS = CULTIV
QUARTIER = QUART
DOMICILE = DOMI
APPLIQUEE = APPLI
SOPHROLOGIE = SOPHRO
SPECTACLE = SPECTA
ABANDONNE = ABANDON
COMMUNAUTAIRE = COMMUN
PARTICULIER = PARTICUL
METALLIQUE = METAL
COOPERATION = COOP
PROGRAMMATION = PROGRAM
KINESITHERAPEUTE = KINESITHERAP
ENVIRON = ENVIRON
ARTISAN = ARTIS
COMMUNICATION = COM
TRANSMISSION = TRANSMIS
APPROVISIONNEMENT = APPRO
IMAGERIE = IMAGE
MANAGEMENT = MANAG
ASSOCIEE = ASSO
INFIRMIERE = INFIRM
FONDS = FOND
EMBOUTISSAGE = EMBOUTISS
DECOUPAGE = DECOUP
OUTILLAGE = OUTIL
TERRASSEMENT = TERRASS
DEMOLITION = DEMOLIT
BILINGUE = BILINGU
ECOLE = ECOL
HABITAT = HABITA
PRODUCTION = PROD
DURABLE = DURABL
PRATIQUE = PRATIQ
TRANSPORT = TRANSPOR
ASSOCIATIVE = ASSO
CRECHE = CRECH
SPECIALISEE = SPECIAL
COUVERTURE = COUVERT
ETANCHEITE = ETANCH
TOITURE = TOIT


### 1. Both versions

1. Splitting on spaces causes words not to be abbreviated if they are followed or preceded by punctuation.

2. Testing for plurals is done for every word. When there are many sentences to be reduced, it would be better to handle plurals by preprocessing the abbreviations dictionary beforehand. (Better because of speed and because of separation of concerns, for example pluralization rules are language-dependent.)

3. Each word is looked up in the abbreviations mapping four times. It would be better to look it up just once and remember the result.

### 2. First version (Maarten Fabré's)

1. There's no docstring.

2. Runtime is quadratic in the length of the input string due to the use of repeated string replacement.

3. Replacement does not respect word boundaries, for example, if CERCLE is found the in the sentence, then it would be changed to its abbreviation CERCL, but also RECERCLER would be changed to RECERCLR, which is not what is wanted.

### 3. Second version

1. There is no need for abbreviations = set(abbreviations_file) since ConfigParser objects support the mapping protocol.

2. It would be better to take the maximum sentence length as a keyword argument rather than a global variable: this is more flexible and convenient for testing.

### 4. Revised code

import re

def shorten_sentence(abbreviations, sentence, max_length=0):
"""Shorten sentence by abbreviating words until it is max_length
characters or shorter. First argument abbreviations must be a
dictionary mapping words to their abbreviations.

"""
length = len(sentence)
words = []
for word in re.split(r'(\W+)', sentence):
if length > max_length:
abbrev = abbreviations.get(word, word)
else:
abbrev = word
words.append(abbrev)
length -= len(word) - len(abbrev)
return ''.join(words)


I recommend that pluralization be implemented separately, perhaps like this:

def plural_fr(word):
"""Return a naïve guess at the French plural of word."""
if word.endswith(('AU', 'EU', 'OU')):
return word + 'X'
else:
return word + 'S'

def pluralize(abbreviations, plural):
"""Return copy of abbreviations with uppercased keys, together with the
plurals of the keys, produced by calling the plural function.

"""
result = {key.upper(): value for key, value in abbreviations.items()}
for key, value in abbreviations.items():
result.setdefault(plural(key.upper()), value)
return result


Then in the main part of the program you would build the table of abbreviations and their plurals like this:

config = configfile.ConfigParser()

• RECERCLER would only be reduced if it is found in the abbreviations, so would not be reduced to RECERCLR. My version can be considered quadratic in relation to the amount of words in the sentence, but since the goal was to get the sentence below 38 characters, the amount of words to expect would be limited. I see now that it would result in an infinite loop if the sentence can not be reduced to below the maximum length. All in all your version is a lot better. – Maarten Fabré Nov 30 '18 at 14:48