aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jim.c47
-rw-r--r--jim_tcl.txt7
-rw-r--r--tests/lsort.test9
3 files changed, 57 insertions, 6 deletions
diff --git a/jim.c b/jim.c
index ac90725..0c471e0 100644
--- a/jim.c
+++ b/jim.c
@@ -475,7 +475,7 @@ static int JimCheckConversion(const char *str, const char *endptr)
return JIM_OK;
}
-/* Parses the front of a number to determine it's sign and base
+/* Parses the front of a number to determine it's sign and base
* Returns the index to start parsing according to the given base
*/
static int JimNumberBase(const char *str, int *base, int *sign)
@@ -6402,6 +6402,7 @@ struct lsort_info {
int order;
int index;
int indexed;
+ int unique;
int (*subfn)(Jim_Obj **, Jim_Obj **);
};
@@ -6479,6 +6480,35 @@ static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
return JimSign(ret) * sort_info->order;
}
+/* Remove duplicate elements from the (sorted) list in-place, according to the
+ * comparison function, comp.
+ *
+ * Note that the last unique value is kept, not the first
+ */
+static void ListRemoveDuplicates(Jim_Obj *listObjPtr, int (*comp)(Jim_Obj **lhs, Jim_Obj **rhs))
+{
+ int src;
+ int dst = 0;
+ Jim_Obj **ele = listObjPtr->internalRep.listValue.ele;
+
+ for (src = 1; src < listObjPtr->internalRep.listValue.len; src++) {
+ if (comp(&ele[dst], &ele[src]) == 0) {
+ /* Match, so replace the dest with the current source */
+ Jim_DecrRefCount(sort_info->interp, ele[dst]);
+ }
+ else {
+ /* No match, so keep the current source and move to the next destination */
+ dst++;
+ }
+ ele[dst] = ele[src];
+ }
+ /* At end of list, keep the final element */
+ ele[++dst] = ele[src];
+
+ /* Set the new length */
+ listObjPtr->internalRep.listValue.len = dst;
+}
+
/* Sort a list *in place*. MUST be called with non-shared objects. */
static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info)
{
@@ -6529,6 +6559,11 @@ static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsor
if ((rc = setjmp(info->jmpbuf)) == 0) {
qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn);
}
+
+ if (info->unique && len > 1) {
+ ListRemoveDuplicates(listObjPtr, fn);
+ }
+
Jim_InvalidateStringRep(listObjPtr);
sort_info = prev_info;
@@ -12533,10 +12568,10 @@ static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
{
static const char * const options[] = {
- "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", NULL
+ "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique", NULL
};
enum
- { OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX };
+ { OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE };
Jim_Obj *resObj;
int i;
int retCode;
@@ -12551,13 +12586,14 @@ static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const arg
info.type = JIM_LSORT_ASCII;
info.order = 1;
info.indexed = 0;
+ info.unique = 0;
info.command = NULL;
info.interp = interp;
for (i = 1; i < (argc - 1); i++) {
int option;
- if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG)
+ if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG)
!= JIM_OK)
return JIM_ERR;
switch (option) {
@@ -12579,6 +12615,9 @@ static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const arg
case OPT_DECREASING:
info.order = -1;
break;
+ case OPT_UNIQUE:
+ info.unique = 1;
+ break;
case OPT_COMMAND:
if (i >= (argc - 2)) {
Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1);
diff --git a/jim_tcl.txt b/jim_tcl.txt
index d2e9636..85d5046 100644
--- a/jim_tcl.txt
+++ b/jim_tcl.txt
@@ -56,6 +56,7 @@ Changes between 0.74 and 0.75
1. `binary`, `pack` and `unpack` now support floating point
2. `file copy` '-force' handles source and target as the same file
3. `format` now supports +%b+ for binary conversion
+3. `lsort` now supports '-unique' and '-real'
Changes between 0.73 and 0.74
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -3136,7 +3137,7 @@ the list are to be matched against pattern and must have one of the values below
lsort
~~~~~
-+*lsort* ?*-index* 'listindex'? ?*-nocase!-integer|-real|-command* 'cmdname'? ?*-decreasing*|*-increasing*? 'list'+
++*lsort* ?*-index* 'listindex'? ?*-nocase!-integer|-real|-command* 'cmdname'? ?*-unique*? ?*-decreasing*|*-increasing*? 'list'+
Sort the elements of +'list'+, returning a new list in sorted order.
By default, ASCII (or UTF-8) sorting is used, with the result in increasing order.
@@ -3156,6 +3157,10 @@ than, equal to, or greater than +'$value2'+, respectively.
If +-decreasing+ is specified, the resulting list is in the opposite
order to what it would be otherwise. +-increasing+ is the default.
+If +-unique+ is specified, then only the last set of duplicate elements found in the list will be retained.
+Note that duplicates are determined relative to the comparison used in the sort. Thus if +-index 0+ is used,
++{1 a}+ and +{1 b}+ would be considered duplicates and only the second element, +{1 b}+, would be retained.
+
If +-index 'listindex'+ is specified, each element of the list is treated as a list and
the given index is extracted from the list for comparison. The list index may
be any valid list index, such as +1+, +end+ or +end-2+.
diff --git a/tests/lsort.test b/tests/lsort.test
index b3096a7..a1e5500 100644
--- a/tests/lsort.test
+++ b/tests/lsort.test
@@ -17,7 +17,7 @@ test lsort-1.1 {Tcl_LsortObjCmd procedure} jim {
} {1 {wrong # args: should be "lsort ?options? list"}}
test lsort-1.2 {Tcl_LsortObjCmd procedure} jim {
list [catch {lsort -foo {1 3 2 5}} msg] $msg
-} {1 {bad option "-foo": must be -ascii, -command, -decreasing, -increasing, -index, -integer, -nocase, or -real}}
+} {1 {bad option "-foo": must be -ascii, -command, -decreasing, -increasing, -index, -integer, -nocase, -real, or -unique}}
test lsort-1.3 {Tcl_LsortObjCmd procedure, default options} {
lsort {d e c b a \{ d35 d300}
} {a b c d d300 d35 e \{}
@@ -182,6 +182,13 @@ test lsort-3.19 {SortCompare procedure, -decreasing option} {
test lsort-3.20 {SortCompare procedure, -real option} -body {
lsort -real {6...4 3}
} -returnCodes error -result {expected number but got "6...4"}
+test lsort-3.21 {lsort, unique sort} {
+ lsort -integer -unique {3 1 2 3 1 4 3}
+} {1 2 3 4}
+test lsort-3.22 {lsort, unique sort with index} {
+ # lsort -unique should return the last unique item
+ lsort -unique -index 0 {{a b} {c b} {a c} {d a}}
+} {{a c} {c b} {d a}}
test lsort-4.26 {DefaultCompare procedure, signed characters} utf8 {
set l [lsort [list "abc\u80" "abc"]]