From f3096fe30a3dd1d33920686e0942f7220b8e32f8 Mon Sep 17 00:00:00 2001 From: CismonX Date: Thu, 6 Sep 2018 10:30:18 +0800 Subject: [PATCH] Add `sum()` and `sumBy()`. --- src/collections_me.c | 2 ++ src/collections_methods.c | 65 +++++++++++++++++++++++++++++++++++++++ src/php_collections_me.h | 2 ++ stubs/Collection.php | 21 +++++++++++++ tests/054-sum-by.phpt | 26 ++++++++++++++++ 5 files changed, 116 insertions(+) create mode 100644 tests/054-sum-by.phpt diff --git a/src/collections_me.c b/src/collections_me.c index c1594aa..dedd281 100644 --- a/src/collections_me.c +++ b/src/collections_me.c @@ -206,6 +206,8 @@ const zend_function_entry collection_methods[] = { PHP_ME(Collection, sortedByDescending, selector_flags_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, sortedDescending, flags_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, sortedWith, comparator_arginfo, ZEND_ACC_PUBLIC) + PHP_ME(Collection, sum, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Collection, sumBy, selector_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, take, n_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, takeLast, n_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, takeLastWhile, predicate_arginfo, ZEND_ACC_PUBLIC) diff --git a/src/collections_methods.c b/src/collections_methods.c index 6cc2423..bff72fd 100644 --- a/src/collections_methods.c +++ b/src/collections_methods.c @@ -2702,6 +2702,71 @@ PHP_METHOD(Collection, sortedWith) RETVAL_NEW_COLLECTION(sorted_with); } +PHP_METHOD(Collection, sum) +{ + zend_array* current = COLLECTION_FETCH_CURRENT(); + zval sum; + ZVAL_NULL(&sum); + ZEND_HASH_FOREACH_VAL(current, zval* val) + if (UNEXPECTED(ZVAL_IS_NULL(&sum))) { + if (Z_TYPE_P(val) == IS_LONG) { + ZVAL_LONG(&sum, 0); + } else if (EXPECTED(Z_TYPE_P(val) == IS_DOUBLE)) { + ZVAL_DOUBLE(&sum, 0.0); + } else { + ERR_NOT_NUMERIC(); + RETURN_NULL(); + } + } + if (Z_TYPE_P(val) == IS_LONG) { + Z_LVAL(sum) += Z_LVAL_P(val); + } else if (EXPECTED(Z_TYPE_P(val) == IS_DOUBLE)) { + Z_DVAL(sum) += Z_DVAL_P(val); + } else { + ERR_NOT_NUMERIC(); + RETURN_NULL(); + } + ZEND_HASH_FOREACH_END(); + RETVAL_ZVAL(&sum, 0, 0); +} + +PHP_METHOD(Collection, sumBy) +{ + zend_fcall_info fci; + zend_fcall_info_cache fcc; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); + zend_array* current = COLLECTION_FETCH_CURRENT(); + zval sum; + ZVAL_NULL(&sum); + INIT_FCI(&fci, 2); + ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) + CALLBACK_KEYVAL_INVOKE(params, bucket); + if (UNEXPECTED(ZVAL_IS_NULL(&sum))) { + if (Z_TYPE(retval) == IS_LONG) { + ZVAL_LONG(&sum, 0); + } else if (EXPECTED(Z_TYPE(retval) == IS_DOUBLE)) { + ZVAL_DOUBLE(&sum, 0.0); + } else { + ERR_NOT_NUMERIC(); + zval_ptr_dtor(&retval); + RETURN_NULL(); + } + } + if (Z_TYPE(retval) == IS_LONG) { + Z_LVAL(sum) += Z_LVAL(retval); + } else if (EXPECTED(Z_TYPE(retval) == IS_DOUBLE)) { + Z_DVAL(sum) += Z_DVAL(retval); + } else { + ERR_NOT_NUMERIC(); + zval_ptr_dtor(&retval); + RETURN_NULL(); + } + ZEND_HASH_FOREACH_END(); + RETVAL_ZVAL(&sum, 0, 0); +} + PHP_METHOD(Collection, take) { zend_long n; diff --git a/src/php_collections_me.h b/src/php_collections_me.h index 3c53ceb..f5bab68 100644 --- a/src/php_collections_me.h +++ b/src/php_collections_me.h @@ -94,6 +94,8 @@ PHP_METHOD(Collection, sortedBy); PHP_METHOD(Collection, sortedByDescending); PHP_METHOD(Collection, sortedDescending); PHP_METHOD(Collection, sortedWith); +PHP_METHOD(Collection, sum); +PHP_METHOD(Collection, sumBy); PHP_METHOD(Collection, take); PHP_METHOD(Collection, takeLast); PHP_METHOD(Collection, takeLastWhile); diff --git a/stubs/Collection.php b/stubs/Collection.php index 0782e5e..c5acd73 100644 --- a/stubs/Collection.php +++ b/stubs/Collection.php @@ -807,6 +807,27 @@ class Collection implements ArrayAccess, Countable */ function sortedWith($comparator) {} + /** + * Returns the sum of all elements in the collection. + * + * All elements should be of the same type, int or double. Otherwise result is undefined. + * + * @return int|double|null + */ + function sum() {} + + /** + * Returns the sum of all values produced by selector function applied to each element + * in the collection. + * + * All return values of the selector function should be of the same type, int or double. + * Otherwise result is undefined. + * + * @param callable $selector ($value, $key) -> int|double + * @return int|double|null + */ + function sumBy($selector) {} + /** * Returns a collection containing first n elements. * diff --git a/tests/054-sum-by.phpt b/tests/054-sum-by.phpt new file mode 100644 index 0000000..5002ab8 --- /dev/null +++ b/tests/054-sum-by.phpt @@ -0,0 +1,26 @@ +--TEST-- +Test Collection::sum() and Collection::sumBy(). +--FILE-- +sum() != $sum) { + echo 'Collection::sum() failed.', PHP_EOL; +} +$array = array_map(function ($value) { + return [$value, floatval($value / random_int(3, 7))]; +}, $array); +$collection = Collection::init($array); +$sum = array_sum(array_column($array, 1)); +$sum_by = function ($value) { + return $value[1]; +}; +if ($collection->sumBy($sum_by) != $sum) { + echo 'Collection::sumBy() failed.', PHP_EOL; +} +?> +--EXPECT--