]> granicus.if.org Git - php/commitdiff
Finished array_multisort() function. Basically it sorts multiple arrays
authorAndrei Zmievski <andrei@php.net>
Mon, 13 Dec 1999 19:42:26 +0000 (19:42 +0000)
committerAndrei Zmievski <andrei@php.net>
Mon, 13 Dec 1999 19:42:26 +0000 (19:42 +0000)
similar to ORDER BY SQL clause.
@ Added array_multisort() function. (Andrei)
# Docs are coming soon

TODO
ext/standard/array.c
ext/standard/php_array.h

diff --git a/TODO b/TODO
index e5d32e6e369ba29571d3f3f46b27314dd24b0649..87c2c207daa269683c866fb5a306ad5131d04b40 100644 (file)
--- a/TODO
+++ b/TODO
@@ -24,7 +24,6 @@ ext/oci8
 ext/standard
 ------------
     * strpad() (Andrei)
-    * advanced sort (Andrei)
        * NOT binary safe:
                strcspn()
                strtok()
index cec2d13a68abfa51331aa6f62543e6dc9c01acca..707684efb8dfbb6edce4a40624bd1c045b6962e8 100644 (file)
@@ -49,7 +49,8 @@ php_array_globals array_globals;
 #define EXTR_PREFIX_SAME       2
 #define        EXTR_PREFIX_ALL         3
 
-static unsigned char all_args_force_ref[] = { 1, BYREF_FORCE_REST };
+#define SORT_DESC                 -1
+#define SORT_ASC                   1
 
 function_entry array_functions[] = {
        PHP_FE(ksort,                                                                   first_arg_force_ref)
@@ -76,7 +77,7 @@ function_entry array_functions[] = {
        PHP_FE(extract,                                                                 NULL)
        PHP_FE(compact,                                                                 NULL)
        PHP_FE(range,                                                                   NULL)
-       PHP_FE(multisort,                                                               all_args_force_ref)
+       PHP_FE(array_multisort,                                                 NULL)
        PHP_FE(array_push,                                                              first_arg_force_ref)
        PHP_FE(array_pop,                                                               first_arg_force_ref)
        PHP_FE(array_shift,                                                             first_arg_force_ref)
@@ -122,6 +123,9 @@ PHP_MINIT_FUNCTION(array)
        REGISTER_LONG_CONSTANT("EXTR_PREFIX_SAME", EXTR_PREFIX_SAME, CONST_CS | CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("EXTR_PREFIX_ALL", EXTR_PREFIX_ALL, CONST_CS | CONST_PERSISTENT);
        
+       REGISTER_LONG_CONSTANT("SORT_ASC", SORT_ASC, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SORT_DESC", SORT_DESC, CONST_CS | CONST_PERSISTENT);
+
        return SUCCESS;
 }
 
@@ -1960,11 +1964,12 @@ int multisort_compare(const void *a, const void *b)
        int                       r;
        int                       result = 0;
        zval              temp;
+       ARRAYLS_FETCH();
        
        r = 0;
        do {
                compare_function(&temp, *((zval **)ab[r]->pData), *((zval **)bb[r]->pData));
-               result = temp.value.lval;
+               result = ARRAYG(multisort_flags)[r] * temp.value.lval;
                if (result != 0)
                        return result;
                r++;
@@ -1972,15 +1977,29 @@ int multisort_compare(const void *a, const void *b)
        return result;
 }
 
-PHP_FUNCTION(multisort)
+#define MULTISORT_ABORT                                                \
+       efree(ARRAYG(multisort_flags));                 \
+       efree(arrays);                                                  \
+       efree(args);                                                    \
+       RETURN_FALSE;
+
+/* {{{ proto bool array_multisort(array $ar1 [, SORT_ASC|SORT_DESC] [, array $ar2 [ICASE|NUM] [DESC|ASC], ...])
+   Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
+PHP_FUNCTION(array_multisort)
 {
        zval***                 args;
+       zval***                 arrays;
        Bucket***               indirect;
        Bucket*                 p;
        HashTable*              hash;
        int                             argc;
        int                             array_size;
+       int                             num_arrays = 0;
+       int                             parse_state = 0;      /* 0 - flag not allowed
+                                             1 - flag allowed     */
+       int                             sort_order = SORT_ASC;
        int                             i, k;
+       ARRAYLS_FETCH();
        
        /* Get the argument count and check it */
        argc = ARG_COUNT(ht);
@@ -1995,46 +2014,89 @@ PHP_FUNCTION(multisort)
                WRONG_PARAM_COUNT;
        }
 
+       /* Allocate space for storing pointers to input arrays and sort flags */
+       arrays = (zval ***)ecalloc(argc, sizeof(zval **)); 
+       ARRAYG(multisort_flags) = (int *)ecalloc(argc, sizeof(int));
+
+       /* Here we go through the input arguments and parse them. Each one can
+          be either an array or a sort order flag which follows an array. If
+          not specified, the sort order flag defaults to SORT_ASC. There can't
+          be two sort order flags in a row, and the very first argument has
+          to be an array.
+        */
        for (i = 0; i < argc; i++) {
-               if ((*args[i])->type != IS_ARRAY) {
-                       php_error(E_WARNING, "Argument %i to %s() is not an array", i+1,
+               if ((*args[i])->type == IS_ARRAY) {
+                       /* We see the next array so update the sort order of
+                          the previous array and reset the sort order */
+                       if (i > 0) {
+                               ARRAYG(multisort_flags)[num_arrays-1] = sort_order;
+                               sort_order = SORT_ASC;
+                       }
+                       arrays[num_arrays++] = args[i];
+
+                       /* next one may be array or sort flag */
+                       parse_state = 1;
+               } else if ((*args[i])->type == IS_LONG) {
+                       /* flag allowed here */
+                       if (parse_state == 1) {
+                               if ((*args[i])->value.lval == SORT_ASC || (*args[i])->value.lval == SORT_DESC) {
+                                       /* Save the flag and make sure then next arg is not a flag */
+                                       sort_order = (*args[i])->value.lval;
+                                       parse_state = 0;
+                               } else {
+                                       php_error(E_WARNING, "Argument %i to %s() is an unknown sort flag", i+1,
+                                                         get_active_function_name());
+                                       MULTISORT_ABORT;
+                               }
+                       } else {
+                               php_error(E_WARNING, "Argument %i to %s() is expected to be an array", i+1,
+                                                 get_active_function_name());
+                               MULTISORT_ABORT;
+                       }
+               } else {
+                       php_error(E_WARNING, "Argument %i to %s() is expected to be an array or a sort flag", i+1,
                                          get_active_function_name());
-                       efree(args);
-                       return;
+                       MULTISORT_ABORT;
                }
        }
+       /* Take care of the last array sort flag */
+       ARRAYG(multisort_flags)[num_arrays-1] = sort_order;
        
        /* Make sure the arrays are of the same size */
-       array_size = zend_hash_num_elements((*args[0])->value.ht);
-       for (i = 0; i < argc; i++) {
-               if (zend_hash_num_elements((*args[i])->value.ht) != array_size) {
+       array_size = zend_hash_num_elements((*arrays[0])->value.ht);
+       for (i = 0; i < num_arrays; i++) {
+               if (zend_hash_num_elements((*arrays[i])->value.ht) != array_size) {
                        php_error(E_WARNING, "Array sizes are inconsistent");
-                       efree(args);
-                       return;
+                       MULTISORT_ABORT;
                }
        }
 
-       /* Create the indirection array */
+       /* Create the indirection array. This array is of size MxN, where 
+          M is the number of entries in each input array and N is the number
+          of the input arrays + 1. The last column is NULL to indicate the end
+          of the row.
+        */
        indirect = (Bucket ***)emalloc(array_size * sizeof(Bucket **));
        for (i = 0; i < array_size; i++)
-               indirect[i] = (Bucket **)emalloc((argc+1) * sizeof(Bucket *));
+               indirect[i] = (Bucket **)emalloc((num_arrays+1) * sizeof(Bucket *));
        
-       for (i = 0; i < argc; i++) {
+       for (i = 0; i < num_arrays; i++) {
                k = 0;
-               for (p = (*args[i])->value.ht->pListHead; p; p = p->pListNext, k++) {
+               for (p = (*arrays[i])->value.ht->pListHead; p; p = p->pListNext, k++) {
                        indirect[k][i] = p;
                }
        }
        for (k = 0; k < array_size; k++)
-               indirect[k][argc] = NULL;
+               indirect[k][num_arrays] = NULL;
 
-       /* Do the actual sort */
+       /* Do the actual sort magic - bada-bim, bada-boom */
        qsort(indirect, array_size, sizeof(Bucket **), multisort_compare);
        
-       /* Restructure the arrays based on sorted indirect */
+       /* Restructure the arrays based on sorted indirect - this is mostly
+          take from zend_hash_sort() function. */
        HANDLE_BLOCK_INTERRUPTIONS();
-       for (i = 0; i < argc; i++) {
-               hash = (*args[i])->value.ht;
+       for (i = 0; i < num_arrays; i++) {
+               hash = (*arrays[i])->value.ht;
                hash->pListHead = indirect[0][i];;
                hash->pListTail = NULL;
                hash->pInternalPointer = hash->pListHead;
@@ -2064,5 +2126,8 @@ PHP_FUNCTION(multisort)
        for (i = 0; i < array_size; i++)
                efree(indirect[i]);
        efree(indirect);
+       efree(ARRAYG(multisort_flags));
+       efree(arrays);
        efree(args);
+       RETURN_TRUE;
 }
index 2fee1a35898e4d516ec7430902cf0ff518612f13..1f4f28cdb2ca2857142a0aec9d40476f16973c80 100644 (file)
@@ -62,7 +62,7 @@ PHP_FUNCTION(extract);
 PHP_FUNCTION(compact);
 PHP_FUNCTION(range);
 PHP_FUNCTION(shuffle);
-PHP_FUNCTION(multisort);
+PHP_FUNCTION(array_multisort);
 PHP_FUNCTION(array_push);
 PHP_FUNCTION(array_pop);
 PHP_FUNCTION(array_shift);