5

Say I have a list of strings in my code, for example :

var array = [
    "foo",
    "bar",
    "John",
    "Doe",
    "Mary"
];

test("foo");
test("bar", "Doe");

Those strings are repeated at the bottom of the file, without any particular structure.

I would like to be able to know which strings are only there once.

For example, in the case above, the strings "Mary" and "John" should be highlighted, as they only appear once.

It would be even better if you could explain the different steps, especially before/after the highlight, it would be best if we could have the list of unique words in a vimscript variable, so that we could do more than just highlight them (for example delete them)

1 Answer 1

7

The following code defines the command :UniqueWords which tries to do what you want:

command! UniqueWords call UniqueWords()

function! UniqueWords() abort
    let words = []
    silent! %s/"\zs\w\{-}\ze"/\=add(words, submatch(0))/gn
    let dic = {}
    let uniq_words = uniq(sort(copy(words)))
    for i in range(len(uniq_words))
        if uniq_words[i] != ''
            call extend(dic, {uniq_words[i]: count(words, uniq_words[i])})
        endif
    endfor
    call filter(dic, 'v:val == 1')
    let pattern = join(keys(dic), '\|')
    silent! execute 'match SpellBad /' . pattern . '/'
    return [keys(dic), pattern]
endfunction

The command calls the UniqueWords() function which does the following:

let words = []

Define the empty list words whose purpose will be to contain all the words surrounded by double quotes in your current buffer.

silent! %s/"\zs\w\{-}\ze"/\=add(words, submatch(0))/gn

Call the substitute command :s to look for the pattern "\zs\w\{-}\ze" (word characters between 2 consecutive double quotes). Whenever such a text is found it's added to the words list, but without actually performing any substitution thanks to the n flag.

let dic = {}

Define an empty dictionary, whose purpose will be to contain all your quoted words as well as the number of times they appear.

let uniq_words = uniq(sort(copy(words)))

Define the list uniq_words which again contains all the words, but this time the list is sorted with the sort() function, and the duplicates are removed with the uniq() function. A copy of words is also made, so that the latter is not altered in the process.

for i in range(len(uniq_words))

Begin a loop which iterates over the words in uniq_words.

    call extend(dic, {uniq_words[i]: count(words, uniq_words[i])})

Add a new item to the dictionary dic, whose key is a word from uniq_words (uniq_words[i]) and whose value is the number of times it appears (count(words, uniq_words[i])).

call filter(dic, 'v:val == 1')

Remove all the items from dic whose values are different than one, because you're only interested in words which appear once.

let pattern = join(keys(dic), '\|')

Build a pattern by joining all the keys from dic and separating them with \| (that's how you separate two branches, see :h /bar).

silent! execute 'match SpellBad /' . pattern . '/'

Highlight the words which appear inside pattern with the highlighting group SpellBad.

return [keys(dic), pattern]

Return the keys from dic, so that you can access them through a list, as well as the pattern so that you can use it later in a substitute command.

For example, to see the list, you could type: :echo UniqueWords()[0]
And to remove the words which appear only once, you could type:

:exe '%s/' . UniqueWords()[1] . '//g'

For more information, see:

:help :command
:help add()
:help copy()
:help sort()
:help uniq()
:help len()
:help range()
:help count()
:help extend()
:help filter()
:help keys()
:help join()
:help :match
1
  • Excellent show reel of vimscript! Mar 9, 2016 at 7:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.