Adding fuctions array_unique, array_intersect and array_subtract.

This commit is contained in:
Stig Venaas 2000-06-10 18:58:45 +00:00
parent a1c036b8cf
commit 99f151f1bd
3 changed files with 255 additions and 0 deletions

View File

@ -2142,6 +2142,254 @@ PHP_FUNCTION(array_flip)
}
/* }}} */
/* {{{ proto array array_unique(array input)
Removes duplicate values from array */
PHP_FUNCTION(array_unique)
{
zval **array;
HashTable *target_hash;
Bucket **arTmp, **cmpdata, **lastkept;
Bucket *p;
int i;
if (ARG_COUNT(ht) != 1 || zend_get_parameters_ex(1, &array) == FAILURE) {
WRONG_PARAM_COUNT;
}
target_hash = HASH_OF(*array);
if (!target_hash) {
php_error(E_WARNING, "Wrong datatype in array_unique() call");
RETURN_FALSE;
}
/* copy the argument array */
*return_value = **array;
zval_copy_ctor(return_value);
if (target_hash->nNumOfElements <= 1) /* nothing to do */
return;
/* create and sort array with pointers to the target_hash buckets */
arTmp = (Bucket **) pemalloc((target_hash->nNumOfElements + 1) * sizeof(Bucket *), target_hash->persistent);
if (!arTmp)
RETURN_FALSE;
for (i = 0, p = target_hash->pListHead; p; i++, p = p->pListNext)
arTmp[i] = p;
arTmp[i] = NULL;
qsort((void *) arTmp, i, sizeof(Bucket *), array_data_compare);
/* go through the sorted array and delete duplicates from the copy */
lastkept = arTmp;
for (cmpdata = arTmp + 1; *cmpdata; cmpdata++) {
if (array_data_compare(lastkept, cmpdata)) {
lastkept = cmpdata;
} else {
p = *cmpdata;
if (p->nKeyLength)
zend_hash_del(return_value->value.ht, p->arKey, p->nKeyLength);
else
zend_hash_index_del(return_value->value.ht, p->h);
}
}
pefree(arTmp, target_hash->persistent);
}
/* }}} */
/* {{{ proto array array_intersect(array arr1, array arr2 [, ...])
Returns the entries of arr1 that have values which are present in
all the others arguments */
PHP_FUNCTION(array_intersect)
{
zval ***args = NULL;
HashTable *hash;
int argc, i, c = 0;
Bucket ***lists, **list, ***ptrs, *p;
zval *entry;
/* Get the argument count and check it */
argc = ARG_COUNT(ht);
if (argc < 2) {
WRONG_PARAM_COUNT;
}
/* Allocate arguments array and get the arguments, checking for errors. */
args = (zval ***)emalloc(argc * sizeof(zval **));
if (zend_get_parameters_array_ex(argc, args) == FAILURE) {
efree(args);
WRONG_PARAM_COUNT;
}
array_init(return_value);
/* for each argument, create and sort list with pointers to the hash buckets */
lists = (Bucket ***)emalloc(argc * sizeof(Bucket **));
ptrs = (Bucket ***)emalloc(argc * sizeof(Bucket **));
for (i=0; i<argc; i++) {
if ((*args[i])->type != IS_ARRAY) {
php_error(E_WARNING, "Argument #%d to array_intersect() is not an array", i+1);
argc = i; /* only free up to i-1 */
goto out;
}
hash = HASH_OF(*args[i]);
list = (Bucket **) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket *), hash->persistent);
if (!list)
RETURN_FALSE;
lists[i] = list;
ptrs[i] = list;
for (p = hash->pListHead; p; p = p->pListNext)
*list++ = p;
*list = NULL;
qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), array_data_compare);
}
/* go through the lists and look for common values */
while (*ptrs[0]) {
for (i=1; i<argc; i++) {
while (*ptrs[i] && (0 < (c = array_data_compare(ptrs[0], ptrs[i]))))
ptrs[i]++;
if (!*ptrs[i])
goto out;
if (c)
break;
ptrs[i]++;
}
if (c) {
/* ptrs[0] not in all arguments, next candidate is ptrs[i] */
for (;;) {
if (!*++ptrs[0])
goto out;
if (0 <= array_data_compare(ptrs[0], ptrs[i]))
break;
}
} else {
/* ptrs[0] is present in all the arguments */
/* Go through all entries with same value as ptrs[0] */
for (;;) {
p = *ptrs[0];
entry = *((zval **)p->pData);
entry->refcount++;
if (p->nKeyLength)
zend_hash_update(return_value->value.ht,
p->arKey, p->nKeyLength,
&entry, sizeof(zval *),
NULL);
else
zend_hash_index_update(return_value->value.ht,
p->h, &entry,
sizeof(zval *),
NULL);
if (!*++ptrs[0])
goto out;
if (array_data_compare(ptrs[0]-1, ptrs[0]))
break;
}
}
}
out:
for (i=0; i<argc; i++) {
hash = HASH_OF(*args[i]);
pefree(lists[i], hash->persistent);
}
efree(ptrs);
efree(lists);
efree(args);
}
/* }}} */
/* {{{ proto array array_subtract(array arr1, array arr2 [, ...])
Returns the entries of arr1 that have values which are not present in
any of the others arguments */
PHP_FUNCTION(array_subtract)
{
zval ***args = NULL;
HashTable *hash;
int argc, i, c;
Bucket ***lists, **list, ***ptrs, *p;
zval *entry;
/* Get the argument count and check it */
argc = ARG_COUNT(ht);
if (argc < 2) {
WRONG_PARAM_COUNT;
}
/* Allocate arguments array and get the arguments, checking for errors. */
args = (zval ***)emalloc(argc * sizeof(zval **));
if (zend_get_parameters_array_ex(argc, args) == FAILURE) {
efree(args);
WRONG_PARAM_COUNT;
}
array_init(return_value);
/* for each argument, create and sort list with pointers to the hash buckets */
lists = (Bucket ***)emalloc(argc * sizeof(Bucket **));
ptrs = (Bucket ***)emalloc(argc * sizeof(Bucket **));
for (i=0; i<argc; i++) {
if ((*args[i])->type != IS_ARRAY) {
php_error(E_WARNING, "Argument #%d to array_intersect() is not an array", i+1);
argc = i; /* only free up to i-1 */
goto out;
}
hash = HASH_OF(*args[i]);
list = (Bucket **) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket *), hash->persistent);
if (!list)
RETURN_FALSE;
lists[i] = list;
ptrs[i] = list;
for (p = hash->pListHead; p; p = p->pListNext)
*list++ = p;
*list = NULL;
qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), array_data_compare);
}
/* go through the lists and look for values of ptr[0]
that are not in the others */
while (*ptrs[0]) {
c = 1;
for (i=1; i<argc; i++) {
while (*ptrs[i] && (0 < (c = array_data_compare(ptrs[0], ptrs[i]))))
ptrs[i]++;
if (!c) {
if (*ptrs[i])
ptrs[i]++;
break;
}
}
if (!c) {
/* ptrs[0] in one of the other arguments */
/* skip all entries with value as ptrs[0] */
for (;;) {
if (!*++ptrs[0])
goto out;
if (array_data_compare(ptrs[0]-1, ptrs[0]))
break;
}
} else {
/* ptrs[0] is in none of the other arguments */
for (;;) {
p = *ptrs[0];
entry = *((zval **)p->pData);
entry->refcount++;
if (p->nKeyLength)
zend_hash_update(return_value->value.ht,
p->arKey, p->nKeyLength,
&entry, sizeof(zval *),
NULL);
else
zend_hash_index_update(return_value->value.ht,
p->h, &entry,
sizeof(zval *),
NULL);
if (!*++ptrs[0])
goto out;
if (array_data_compare(ptrs[0]-1, ptrs[0]))
break;
}
}
}
out:
for (i=0; i<argc; i++) {
hash = HASH_OF(*args[i]);
pefree(lists[i], hash->persistent);
}
efree(ptrs);
efree(lists);
efree(args);
}
/* }}} */
int multisort_compare(const void *a, const void *b)
{
Bucket** ab = *(Bucket ***)a;

View File

@ -507,6 +507,10 @@ function_entry basic_functions[] = {
PHP_FE(array_pad, NULL)
PHP_FE(array_flip, NULL)
PHP_FE(array_rand, NULL)
PHP_FE(array_unique, NULL)
PHP_FE(array_intersect, NULL)
PHP_FE(array_subtract, NULL)
/* aliases from array.c */
PHP_FALIAS(pos, current, first_arg_force_ref)
PHP_FALIAS(sizeof, count, first_arg_allow_ref)

View File

@ -69,6 +69,9 @@ PHP_FUNCTION(array_reverse);
PHP_FUNCTION(array_pad);
PHP_FUNCTION(array_flip);
PHP_FUNCTION(array_rand);
PHP_FUNCTION(array_unique);
PHP_FUNCTION(array_intersect);
PHP_FUNCTION(array_subtract);
HashTable* php_splice(HashTable *, int, int, zval ***, int, HashTable **);
int multisort_compare(const void *a, const void *b);