Question: I have 2 tables:

* candidates (candidat\_id, first\_name, name)
* files (file\_id, candidat\_id, category, operation)

I want to display the list, sorted in alphabetical order of the names, of candidates who have more than one examination file with the same category and the same operation.

Answer: 
Here's a sloppy, brute-force, imperfect attempt. It will almost certainly miss something. In fact, it's more a conversation about the process, hoping that you can build a better "dictionary".

First, a discussion about this "dictionary": ideally it should contain a word and its plural, `*ing`, and `*ed` forms. We'll be attempting to replace each word with a snake-wrapped (`_word_`) version, so we'll go in reverse-order based on length. For sanity, we should probably remove too-short words (`and`, `an`, `a`), so let's start with `stringr::words` (simply a *"sample character vectors for practicing string manipulations"*, not a great start).

```r
words <- stringr::words[ order(nchar(stringr::words), decreasing = TRUE) ]
# see words[nchar(words) < 4] for what we are removing here
words <- words[nchar(words) > 3]
Reduce(function(txt, ptn) gsub(ptn, paste0("_", ptn ,"_"), txt, perl = TRUE),
       toupper(words), init = vec)
# [1] "_BOOK_ING_PROCESS__NOTE_ADDED" "_BOOK_ED_DATE__TIME_"          "_BOOK_INGSTATUS"              

```

That looks odd, certainly. We can note that some of the words we *know* are in our vector are missing in `stringr::words`:

```r
c("booking", "process", "status") %in% words
# [1] FALSE  TRUE FALSE

```

We can augment our list:

```r
words2 <- c(words, "booking", "booked", "status")
words2 <- words2[ order(nchar(words2), decreasing = TRUE) ]
Reduce(function(txt, ptn) gsub(ptn, paste0("_", ptn ,"_"), txt, perl = TRUE),
       toupper(words2), init = vec)
# [1] "__BOOK_ING__PROCESS__NOTE_ADDED" "__BOOK_ED__DATE__TIME_"          "__BOOK_ING__STATUS_"            

```

The issue here is that since we have both `"booking"` and `"book"`, it will always double-change `"BOOKING"`. Given my naïve start here, I don't know that there's an easy quick-patch other than to remove `"book"` (and `"king"`, incidentally).

```r
words3 <- setdiff(words2, c("book", "king"))
Reduce(function(txt, ptn) gsub(ptn, paste0("_", ptn ,"_"), txt, perl = TRUE),
       toupper(words3), init = vec)
# [1] "_BOOKING__PROCESS__NOTE_ADDED" "_BOOKED__DATE__TIME_"          "_BOOKING__STATUS_"            

```

From here, we can remove leading/trailing and double `_`.

```r
gsub("__", "_",
     gsub("^_|_$", "", 
          Reduce(function(txt, ptn) gsub(ptn, paste0("_", ptn ,"_"), txt, perl = TRUE),
                 toupper(words3), init = vec)))
# [1] "BOOKING_PROCESS_NOTE_ADDED" "BOOKED_DATE_TIME"           "BOOKING_STATUS"            

```

The quality is completely dependent on starting with a good dictionary. If all of your UPPERNOSPACEWORDS are well defined, then perhaps you can build it manually. (Note that some words may just self-isolate because there is a known word both before and after (note that `"added"` is not in `words3` but it is still broken out).
I would build the dictionary manually:

* Start with an empty dictionary
* Get all names
* Find one containing uppercase
* Manually add words to the dictionary to split that one
* Split all of them using the current dictionary

Repeat the last 3 steps until all words are split. For example, with the 3 names you posted, the dictionary would first get `c("booking", "status")`, and that name would have no uppercase. The name `BOOKINGPROCESSNOTEADDED` would become `booking_PROCESSNOTEADDED`; if you chose that, you'd add `c("process", "note", "added")` to the dictionary, and find `BOOKEDDATETIME` next. Now you need to decide on the words: is it `c("booked", "date", "time")` or `c("booked", "datetime")`?

And so on.