# HG changeset patch # User Bram Moolenaar # Date 1395768263 -3600 # Node ID ddc3f32a4b2191f829206322d46f0e9c7e365e22 # Parent 798d83d153279d208b98d6f58e53f4357d35b77f updated for version 7.4.218 Problem: It's not easy to remove duplicates from a list. Solution: Add the uniq() function. (LCD) diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -1650,7 +1650,7 @@ And a few warnings: 7. Sorting text *sorting* Vim has a sorting function and a sorting command. The sorting function can be -found here: |sort()|. +found here: |sort()|, |uniq()|. *:sor* *:sort* :[range]sor[t][!] [i][u][r][n][x][o] [/{pattern}/] diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -327,6 +327,7 @@ examples: > Changing the order of items in a list: > :call sort(list) " sort a list alphabetically :call reverse(list) " reverse the order of items + :call uniq(sort(list)) " sort and remove duplicates For loop ~ @@ -2005,6 +2006,8 @@ trunc( {expr}) Float truncate Float {e type( {name}) Number type of variable {name} undofile( {name}) String undo file name for {name} undotree() List undo file tree +uniq( {list} [, {func} [, {dict}]]) + List remove adjacent duplicates from a list values( {dict}) List values in {dict} virtcol( {expr}) Number screen column of cursor or mark visualmode( [expr]) String last visual mode used @@ -5488,20 +5491,26 @@ sinh({expr}) *sinh()* sort({list} [, {func} [, {dict}]]) *sort()* *E702* - Sort the items in {list} in-place. Returns {list}. If you - want a list to remain unmodified make a copy first: > + Sort the items in {list} in-place. Returns {list}. + + If you want a list to remain unmodified make a copy first: > :let sortedlist = sort(copy(mylist)) < Uses the string representation of each item to sort on. Numbers sort after Strings, |Lists| after Numbers. For sorting text in the current buffer use |:sort|. + When {func} is given and it is one then case is ignored. - {dict} is for functions with the "dict" attribute. It will be - used to set the local variable "self". |Dictionary-function| When {func} is a |Funcref| or a function name, this function is called to compare items. The function is invoked with two items as argument and must return zero if they are equal, 1 or bigger if the first one sorts after the second one, -1 or smaller if the first one sorts before the second one. + + {dict} is for functions with the "dict" attribute. It will be + used to set the local variable "self". |Dictionary-function| + + Also see |uniq()|. + Example: > func MyCompare(i1, i2) return a:i1 == a:i2 ? 0 : a:i1 > a:i2 ? 1 : -1 @@ -6169,6 +6178,14 @@ undotree() *undotree()* blocks. Each item may again have an "alt" item. +uniq({list} [, {func} [, {dict}]]) *uniq()* *E882* + Remove second and succeeding copies of repeated adjacent + {list} items in-place. Returns {list}. If you want a list + to remain unmodified make a copy first: > + :let newlist = uniq(copy(mylist)) +< The default compare function uses the string representation of + each item. For the use of {func} and {dict} see |sort()|. + values({dict}) *values()* Return a |List| with all the values of {dict}. The |List| is in arbitrary order. diff --git a/runtime/doc/version7.txt b/runtime/doc/version7.txt --- a/runtime/doc/version7.txt +++ b/runtime/doc/version7.txt @@ -942,6 +942,7 @@ New and extended functions: ~ |tagfiles()| List with tags file names |taglist()| get list of matching tags (Yegappan Lakshmanan) |tr()| translate characters (Ron Aaron) +|uniq()| remove copies of repeated adjacent list items |values()| get List of Dictionary values |winnr()| takes an argument: what window to use |winrestview()| restore the view of the current window diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -744,6 +744,7 @@ static void f_trunc __ARGS((typval_T *ar static void f_type __ARGS((typval_T *argvars, typval_T *rettv)); static void f_undofile __ARGS((typval_T *argvars, typval_T *rettv)); static void f_undotree __ARGS((typval_T *argvars, typval_T *rettv)); +static void f_uniq __ARGS((typval_T *argvars, typval_T *rettv)); static void f_values __ARGS((typval_T *argvars, typval_T *rettv)); static void f_virtcol __ARGS((typval_T *argvars, typval_T *rettv)); static void f_visualmode __ARGS((typval_T *argvars, typval_T *rettv)); @@ -8150,6 +8151,7 @@ static struct fst {"type", 1, 1, f_type}, {"undofile", 1, 1, f_undofile}, {"undotree", 0, 0, f_undotree}, + {"uniq", 1, 3, f_uniq}, {"values", 1, 1, f_values}, {"virtcol", 1, 1, f_virtcol}, {"visualmode", 0, 1, f_visualmode}, @@ -17023,10 +17025,11 @@ static int item_compare_ic; static char_u *item_compare_func; static dict_T *item_compare_selfdict; static int item_compare_func_err; +static void do_sort_uniq __ARGS((typval_T *argvars, typval_T *rettv, int sort)); #define ITEM_COMPARE_FAIL 999 /* - * Compare functions for f_sort() below. + * Compare functions for f_sort() and f_uniq() below. */ static int #ifdef __BORLANDC__ @@ -17100,9 +17103,10 @@ item_compare2(s1, s2) * "sort({list})" function */ static void -f_sort(argvars, rettv) - typval_T *argvars; - typval_T *rettv; +do_sort_uniq(argvars, rettv, sort) + typval_T *argvars; + typval_T *rettv; + int sort; { list_T *l; listitem_T *li; @@ -17111,12 +17115,12 @@ f_sort(argvars, rettv) long i; if (argvars[0].v_type != VAR_LIST) - EMSG2(_(e_listarg), "sort()"); + EMSG2(_(e_listarg), sort ? "sort()" : "uniq()"); else { l = argvars[0].vval.v_list; if (l == NULL || tv_check_lock(l->lv_lock, - (char_u *)_("sort() argument"))) + (char_u *)(sort ? _("sort() argument") : _("uniq() argument")))) return; rettv->vval.v_list = l; rettv->v_type = VAR_LIST; @@ -17163,29 +17167,72 @@ f_sort(argvars, rettv) ptrs = (listitem_T **)alloc((int)(len * sizeof(listitem_T *))); if (ptrs == NULL) return; + i = 0; - for (li = l->lv_first; li != NULL; li = li->li_next) - ptrs[i++] = li; - - item_compare_func_err = FALSE; - /* test the compare function */ - if (item_compare_func != NULL - && item_compare2((void *)&ptrs[0], (void *)&ptrs[1]) + if (sort) + { + /* sort(): ptrs will be the list to sort */ + for (li = l->lv_first; li != NULL; li = li->li_next) + ptrs[i++] = li; + + item_compare_func_err = FALSE; + /* test the compare function */ + if (item_compare_func != NULL + && item_compare2((void *)&ptrs[0], (void *)&ptrs[1]) == ITEM_COMPARE_FAIL) - EMSG(_("E702: Sort compare function failed")); - else - { - /* Sort the array with item pointers. */ - qsort((void *)ptrs, (size_t)len, sizeof(listitem_T *), + EMSG(_("E702: Sort compare function failed")); + else + { + /* Sort the array with item pointers. */ + qsort((void *)ptrs, (size_t)len, sizeof(listitem_T *), item_compare_func == NULL ? item_compare : item_compare2); + if (!item_compare_func_err) + { + /* Clear the List and append the items in sorted order. */ + l->lv_first = l->lv_last = l->lv_idx_item = NULL; + l->lv_len = 0; + for (i = 0; i < len; ++i) + list_append(l, ptrs[i]); + } + } + } + else + { + int (*item_compare_func_ptr)__ARGS((const void *, const void *)); + + /* f_uniq(): ptrs will be a stack of items to remove */ + item_compare_func_err = FALSE; + item_compare_func_ptr = item_compare_func + ? item_compare2 : item_compare; + + for (li = l->lv_first; li != NULL && li->li_next != NULL; + li = li->li_next) + { + if (item_compare_func_ptr((void *)&li, (void *)&li->li_next) + == 0) + ptrs[i++] = li; + if (item_compare_func_err) + { + EMSG(_("E882: Uniq compare function failed")); + break; + } + } + if (!item_compare_func_err) { - /* Clear the List and append the items in the sorted order. */ - l->lv_first = l->lv_last = l->lv_idx_item = NULL; - l->lv_len = 0; - for (i = 0; i < len; ++i) - list_append(l, ptrs[i]); + while (--i >= 0) + { + li = ptrs[i]->li_next; + ptrs[i]->li_next = li->li_next; + if (li->li_next != NULL) + li->li_next->li_prev = ptrs[i]; + else + l->lv_last = ptrs[i]; + list_fix_watch(l, li); + listitem_free(li); + l->lv_len--; + } } } @@ -17194,6 +17241,28 @@ f_sort(argvars, rettv) } /* + * "sort({list})" function + */ + static void +f_sort(argvars, rettv) + typval_T *argvars; + typval_T *rettv; +{ + do_sort_uniq(argvars, rettv, TRUE); +} + +/* + * "uniq({list})" function + */ + static void +f_uniq(argvars, rettv) + typval_T *argvars; + typval_T *rettv; +{ + do_sort_uniq(argvars, rettv, FALSE); +} + +/* * "soundfold({word})" function */ static void diff --git a/src/testdir/test55.in b/src/testdir/test55.in --- a/src/testdir/test55.in +++ b/src/testdir/test55.in @@ -323,13 +323,15 @@ let l = [0, 1, 2, 3] : $put ='caught ' . v:exception :endtry :" -:" reverse() and sort() -:let l = ['-0', 'A11', 2, 'xaaa', 4, 'foo', 'foo6', [0, 1, 2], 'x8'] +:" reverse(), sort(), uniq() +:let l = ['-0', 'A11', 2, 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5] +:$put =string(uniq(copy(l))) :$put =string(reverse(l)) :$put =string(reverse(reverse(l))) :$put =string(sort(l)) :$put =string(reverse(sort(l))) :$put =string(sort(reverse(sort(l)))) +:$put =string(uniq(sort(l))) :" :" splitting a string to a List :$put =string(split(' aa bb ')) diff --git a/src/testdir/test55.ok b/src/testdir/test55.ok --- a/src/testdir/test55.ok +++ b/src/testdir/test55.ok @@ -94,11 +94,13 @@ caught a:000[0] caught a:000[2] caught a:000[3] [1, 2, [3, 9, 5, 6], {'a': 12, '5': 8}] -['x8', [0, 1, 2], 'foo6', 'foo', 4, 'xaaa', 2, 'A11', '-0'] -['x8', [0, 1, 2], 'foo6', 'foo', 4, 'xaaa', 2, 'A11', '-0'] -['-0', 'A11', 'foo', 'foo6', 'x8', 'xaaa', 2, 4, [0, 1, 2]] -[[0, 1, 2], 4, 2, 'xaaa', 'x8', 'foo6', 'foo', 'A11', '-0'] -['-0', 'A11', 'foo', 'foo6', 'x8', 'xaaa', 2, 4, [0, 1, 2]] +['-0', 'A11', 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5] +[1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'] +[1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'] +['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]] +[[0, 1, 2], [0, 1, 2], 4, 2, 2, 1.5, 'xaaa', 'x8', 'foo6', 'foo', 'foo', 'A11', '-0'] +['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]] +['-0', 'A11', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 4, [0, 1, 2]] ['aa', 'bb'] ['aa', 'bb'] ['', 'aa', 'bb', ''] diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -735,6 +735,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 218, +/**/ 217, /**/ 216,