深入PHP内核之array_multisort
这个函数是我第一次看手册的时候,没看明白是怎么回事,所以有必要记录一下
用法
bool array_multisort ( array &$arr
[, mixed $arg
= SORT_ASC [, mixed $arg
= SORT_REGULAR [, mixed $...
]]] )
array_multisort() 可以用来一次对多个数组进行排序,或者根据某一维或多维对多维数组进行排序。
关联(string)键名保持不变,但数字键名会被重新索引。
排序顺序标志:
SORT_ASC
- 按照上升顺序排序SORT_DESC
- 按照下降顺序排序
排序类型标志:
SORT_REGULAR
- 将项目按照通常方法比较SORT_NUMERIC
- 将项目按照数值比较SORT_STRING
- 将项目按照字符串比较
每个数组之后不能指定两个同类的排序标志。每个数组后指定的排序标志仅对该数组有效 - 在此之前为默认值 SORT_ASC
和 SORT_REGULAR
。
输入数组被当成一个表的列并以行来排序——这类似于 SQL 的 ORDER BY 子句的功能。第一个数组是要排序的主要数组。数组中的行(值)比较为相同的话就按照下一个输入数组中相应值的大小来排序,依此类推。
本函数的参数结构有些不同寻常,但是非常灵活。第一个参数必须是一个数组。接下来的每个参数可以是数组或者是下面列出的排序标志。
示例一:
<?php function my_sort($arrays, $sort_key, $sort_order = SORT_ASC, $sort_type = SORT_NUMERIC) { if (is_array($arrays)) { foreach ($arrays as $array) { if (is_array($array)) { $key_arrays[] = $array[$sort_key]; } else { return false; } } } else { return false; } array_multisort($key_arrays, $sort_order, $sort_type, $arrays); return $arrays; } $person = array( array('id' => 1, 'name' => 'fj', 'weight' => 100, 'height' => 180), array('id' => 2, 'name' => 'tom', 'weight' => 53, 'height' => 150), array('id' => 3, 'name' => 'jerry', 'weight' => 120, 'height' => 156), array('id' => 4, 'name' => 'bill', 'weight' => 110, 'height' => 190), array('id' => 5, 'name' => 'linken', 'weight' => 80, 'height' => 200), array('id' => 6, 'name' => 'madana', 'weight' => 95, 'height' => 110), array('id' => 7, 'name' => 'jordan', 'weight' => 70, 'height' => 170) ); $person = my_sort($person, 'name', SORT_ASC, SORT_STRING); var_dump($person); ?>
结果
array (size=7) 0 => array (size=4) 'id' => int 4 'name' => string 'bill' (length=4) 'weight' => int 110 'height' => int 190 1 => array (size=4) 'id' => int 1 'name' => string 'fj' (length=2) 'weight' => int 100 'height' => int 180 2 => array (size=4) 'id' => int 3 'name' => string 'jerry' (length=5) 'weight' => int 120 'height' => int 156 3 => array (size=4) 'id' => int 7 'name' => string 'jordan' (length=6) 'weight' => int 70 'height' => int 170 4 => array (size=4) 'id' => int 5 'name' => string 'linken' (length=6) 'weight' => int 80 'height' => int 200 5 => array (size=4) 'id' => int 6 'name' => string 'madana' (length=6) 'weight' => int 95 'height' => int 110 6 => array (size=4) 'id' => int 2 'name' => string 'tom' (length=3) 'weight' => int 53 'height' => int 150
示例二:
<?php $grade = array( "score" => array(70, 95, 70.0, 60, "70"), "name" => array("Zhang San", "Li Si", "Wang Wu", "Zhao Liu", "Liu Qi") ); array_multisort($grade["score"], SORT_NUMERIC, SORT_DESC, // 将分数作为数值,由高到低排序 $grade["name"], SORT_STRING, SORT_ASC); // 将名字作为字符串,由小到大排序 var_dump($grade);
结果
array (size=2) 'score' => array (size=5) 0 => int 95 1 => string '70' (length=2) 2 => float 70 3 => int 70 4 => int 60 'name' => array (size=5) 0 => string 'Li Si' (length=5) 1 => string 'Liu Qi' (length=6) 2 => string 'Wang Wu' (length=7) 3 => string 'Zhang San' (length=9) 4 => string 'Zhao Liu' (length=8)
我看来,这算一个比较神奇的函数了
/* {{{ proto bool array_multisort(array ar1 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING|SORT_NATURAL|SORT_FLAG_CASE]] [, array ar2 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING|SORT_NATURAL|SORT_FLAG_CASE]], ...]) 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[MULTISORT_LAST]; /* 0 - flag not allowed 1 - flag allowed */ int sort_order = PHP_SORT_ASC; int sort_type = PHP_SORT_REGULAR; int i, k; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) { return; } /* Allocate space for storing pointers to input arrays and sort flags. */ arrays = (zval ***)ecalloc(argc, sizeof(zval **)); for (i = 0; i < MULTISORT_LAST; i++) { parse_state[i] = 0; ARRAYG(multisort_flags)[i] = (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 flag which follows an array. If not * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR * accordingly. There can't be two sort flags of the same type after an * array, and the very first argument has to be an array. */ for (i = 0; i < argc; i++) { if (Z_TYPE_PP(args[i]) == IS_ARRAY) { /* We see the next array, so we update the sort flags of * the previous array and reset the sort flags. */ if (i > 0) { ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays - 1] = sort_order; ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays - 1] = sort_type; sort_order = PHP_SORT_ASC; sort_type = PHP_SORT_REGULAR; } arrays[num_arrays++] = args[i]; /* Next one may be an array or a list of sort flags. */ for (k = 0; k < MULTISORT_LAST; k++) { parse_state[k] = 1; } } else if (Z_TYPE_PP(args[i]) == IS_LONG) { switch (Z_LVAL_PP(args[i]) & ~PHP_SORT_FLAG_CASE) { case PHP_SORT_ASC: case PHP_SORT_DESC: /* flag allowed here */ if (parse_state[MULTISORT_ORDER] == 1) { /* Save the flag and make sure then next arg is not the current flag. */ sort_order = Z_LVAL_PP(args[i]) == PHP_SORT_DESC ? -1 : 1; parse_state[MULTISORT_ORDER] = 0; } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1); MULTISORT_ABORT; } break; case PHP_SORT_REGULAR: case PHP_SORT_NUMERIC: case PHP_SORT_STRING: case PHP_SORT_NATURAL: #if HAVE_STRCOLL case PHP_SORT_LOCALE_STRING: #endif /* flag allowed here */ if (parse_state[MULTISORT_TYPE] == 1) { /* Save the flag and make sure then next arg is not the current flag. */ sort_type = Z_LVAL_PP(args[i]); parse_state[MULTISORT_TYPE] = 0; } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1); MULTISORT_ABORT; } break; default: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is an unknown sort flag", i + 1); MULTISORT_ABORT; break; } } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or a sort flag", i + 1); MULTISORT_ABORT; } } /* Take care of the last array sort flags. */ ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays - 1] = sort_order; ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays - 1] = sort_type; /* Make sure the arrays are of the same size. */ array_size = zend_hash_num_elements(Z_ARRVAL_PP(arrays[0])); for (i = 0; i < num_arrays; i++) { if (zend_hash_num_elements(Z_ARRVAL_PP(arrays[i])) != array_size) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array sizes are inconsistent"); MULTISORT_ABORT; } } /* If all arrays are empty we don't need to do anything. */ if (array_size < 1) { for (k = 0; k < MULTISORT_LAST; k++) { efree(ARRAYG(multisort_flags)[k]); } efree(arrays); efree(args); RETURN_TRUE; } /* 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 ***)safe_emalloc(array_size, sizeof(Bucket **), 0); for (i = 0; i < array_size; i++) { indirect[i] = (Bucket **)safe_emalloc((num_arrays + 1), sizeof(Bucket *), 0); } for (i = 0; i < num_arrays; i++) { k = 0; for (p = Z_ARRVAL_PP(arrays[i])->pListHead; p; p = p->pListNext, k++) { indirect[k][i] = p; } } for (k = 0; k < array_size; k++) { indirect[k][num_arrays] = NULL; } /* Do the actual sort magic - bada-bim, bada-boom. */ zend_qsort(indirect, array_size, sizeof(Bucket **), php_multisort_compare TSRMLS_CC); /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */ HANDLE_BLOCK_INTERRUPTIONS(); for (i = 0; i < num_arrays; i++) { hash = Z_ARRVAL_PP(arrays[i]); hash->pListHead = indirect[0][i];; hash->pListTail = NULL; hash->pInternalPointer = hash->pListHead; for (k = 0; k < array_size; k++) { if (hash->pListTail) { hash->pListTail->pListNext = indirect[k][i]; } indirect[k][i]->pListLast = hash->pListTail; indirect[k][i]->pListNext = NULL; hash->pListTail = indirect[k][i]; } p = hash->pListHead; k = 0; while (p != NULL) { if (p->nKeyLength == 0) p->h = k++; p = p->pListNext; } hash->nNextFreeElement = array_size; zend_hash_rehash(hash); } HANDLE_UNBLOCK_INTERRUPTIONS(); /* Clean up. */ for (i = 0; i < array_size; i++) { efree(indirect[i]); } efree(indirect); for (k = 0; k < MULTISORT_LAST; k++) { efree(ARRAYG(multisort_flags)[k]); } efree(arrays); efree(args); RETURN_TRUE; }
待续
参考:http://php.net/manual/zh/function.array-multisort.php
- 作者:踏雪无痕
- 出处:http://www.cnblogs.com/chenpingzhao/
- 本文版权归作者和博客园共有,如需转载,请联系 pingzhao1990#163.com