changeset 5747:ddc3f32a4b21 v7.4.218

updated for version 7.4.218 Problem: It's not easy to remove duplicates from a list. Solution: Add the uniq() function. (LCD)
author Bram Moolenaar <bram@vim.org>
date Tue, 25 Mar 2014 18:24:23 +0100
parents 798d83d15327
children d96e8fbc3747
files runtime/doc/change.txt runtime/doc/eval.txt runtime/doc/version7.txt src/eval.c src/testdir/test55.in src/testdir/test55.ok src/version.c
diffstat 7 files changed, 128 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- 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}/]
--- 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.
--- 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
--- 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
--- 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 '))
--- 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', '']
--- 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,