Additional conditionals from math trig (#1885)
* Use our new Conditional logic to implement the SUMIF() and SUMIFS() Mathematical functions
This commit is contained in:
parent
761c84a946
commit
ee969fdcfe
|
|
@ -30,7 +30,7 @@ for ($col = 1; $col <= 50; ++$col) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$d = microtime(true) - $t;
|
$d = microtime(true) - $t;
|
||||||
$helper->log('Add data (end) . time: ' . round((string) ($d . 2)) . ' s');
|
$helper->log('Add data (end) . time: ' . (string) round($d, 2) . ' s');
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
$helper->write($spreadsheet, __FILE__);
|
$helper->write($spreadsheet, __FILE__);
|
||||||
|
|
|
||||||
|
|
@ -2314,12 +2314,12 @@ class Calculation
|
||||||
],
|
],
|
||||||
'SUMIF' => [
|
'SUMIF' => [
|
||||||
'category' => Category::CATEGORY_MATH_AND_TRIG,
|
'category' => Category::CATEGORY_MATH_AND_TRIG,
|
||||||
'functionCall' => [MathTrig::class, 'SUMIF'],
|
'functionCall' => [Statistical\Conditional::class, 'SUMIF'],
|
||||||
'argumentCount' => '2,3',
|
'argumentCount' => '2,3',
|
||||||
],
|
],
|
||||||
'SUMIFS' => [
|
'SUMIFS' => [
|
||||||
'category' => Category::CATEGORY_MATH_AND_TRIG,
|
'category' => Category::CATEGORY_MATH_AND_TRIG,
|
||||||
'functionCall' => [MathTrig::class, 'SUMIFS'],
|
'functionCall' => [Statistical\Conditional::class, 'SUMIFS'],
|
||||||
'argumentCount' => '3+',
|
'argumentCount' => '3+',
|
||||||
],
|
],
|
||||||
'SUMPRODUCT' => [
|
'SUMPRODUCT' => [
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,10 @@ abstract class DatabaseAbstract
|
||||||
$dataValue = ($dataValues[$key]) ? 'TRUE' : 'FALSE';
|
$dataValue = ($dataValues[$key]) ? 'TRUE' : 'FALSE';
|
||||||
} elseif ($dataValues[$key] !== null) {
|
} elseif ($dataValues[$key] !== null) {
|
||||||
$dataValue = $dataValues[$key];
|
$dataValue = $dataValues[$key];
|
||||||
|
// escape quotes if we have a string containing quotes
|
||||||
|
if (is_string($dataValue) && strpos($dataValue, '"') !== false) {
|
||||||
|
$dataValue = str_replace('"', '""', $dataValue);
|
||||||
|
}
|
||||||
$dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;
|
$dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1284,44 +1284,22 @@ class MathTrig
|
||||||
* Totals the values of cells that contain numbers within the list of arguments
|
* Totals the values of cells that contain numbers within the list of arguments
|
||||||
*
|
*
|
||||||
* Excel Function:
|
* Excel Function:
|
||||||
* SUMIF(value1[,value2[, ...]],condition)
|
* SUMIF(range, criteria, [sum_range])
|
||||||
*
|
*
|
||||||
* @param mixed $aArgs Data values
|
* @Deprecated 1.17.0
|
||||||
* @param string $condition the criteria that defines which cells will be summed
|
*
|
||||||
* @param mixed $sumArgs
|
* @see Statistical\Conditional::SUMIF()
|
||||||
|
* Use the SUMIF() method in the Statistical\Conditional class instead
|
||||||
|
*
|
||||||
|
* @param mixed $range Data values
|
||||||
|
* @param string $criteria the criteria that defines which cells will be summed
|
||||||
|
* @param mixed $sumRange
|
||||||
*
|
*
|
||||||
* @return float
|
* @return float
|
||||||
*/
|
*/
|
||||||
public static function SUMIF($aArgs, $condition, $sumArgs = [])
|
public static function SUMIF($range, $criteria, $sumRange = [])
|
||||||
{
|
{
|
||||||
$returnValue = 0;
|
return Statistical\Conditional::SUMIF($range, $criteria, $sumRange);
|
||||||
|
|
||||||
$aArgs = Functions::flattenArray($aArgs);
|
|
||||||
$sumArgs = Functions::flattenArray($sumArgs);
|
|
||||||
if (empty($sumArgs)) {
|
|
||||||
$sumArgs = $aArgs;
|
|
||||||
}
|
|
||||||
$condition = Functions::ifCondition($condition);
|
|
||||||
// Loop through arguments
|
|
||||||
foreach ($aArgs as $key => $arg) {
|
|
||||||
if (!is_numeric($arg)) {
|
|
||||||
$arg = str_replace('"', '""', $arg);
|
|
||||||
$arg = Calculation::wrapResult(strtoupper($arg));
|
|
||||||
}
|
|
||||||
|
|
||||||
$testCondition = '=' . $arg . $condition;
|
|
||||||
$sumValue = array_key_exists($key, $sumArgs) ? $sumArgs[$key] : 0;
|
|
||||||
|
|
||||||
if (
|
|
||||||
is_numeric($sumValue) &&
|
|
||||||
Calculation::getInstance()->_calculateFormulaValue($testCondition)
|
|
||||||
) {
|
|
||||||
// Is it a value within our criteria and only numeric can be added to the result
|
|
||||||
$returnValue += $sumValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $returnValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1330,7 +1308,12 @@ class MathTrig
|
||||||
* Totals the values of cells that contain numbers within the list of arguments
|
* Totals the values of cells that contain numbers within the list of arguments
|
||||||
*
|
*
|
||||||
* Excel Function:
|
* Excel Function:
|
||||||
* SUMIFS(value1[,value2[, ...]],condition)
|
* SUMIFS(sum_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...)
|
||||||
|
*
|
||||||
|
* @Deprecated 1.17.0
|
||||||
|
*
|
||||||
|
* @see Statistical\Conditional::SUMIFS()
|
||||||
|
* Use the SUMIFS() method in the Statistical\Conditional class instead
|
||||||
*
|
*
|
||||||
* @param mixed $args Data values
|
* @param mixed $args Data values
|
||||||
*
|
*
|
||||||
|
|
@ -1338,47 +1321,7 @@ class MathTrig
|
||||||
*/
|
*/
|
||||||
public static function SUMIFS(...$args)
|
public static function SUMIFS(...$args)
|
||||||
{
|
{
|
||||||
$arrayList = $args;
|
return Statistical\Conditional::SUMIFS(...$args);
|
||||||
|
|
||||||
// Return value
|
|
||||||
$returnValue = 0;
|
|
||||||
|
|
||||||
$sumArgs = Functions::flattenArray(array_shift($arrayList));
|
|
||||||
$aArgsArray = [];
|
|
||||||
$conditions = [];
|
|
||||||
|
|
||||||
while (count($arrayList) > 0) {
|
|
||||||
$aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
|
|
||||||
$conditions[] = Functions::ifCondition(array_shift($arrayList));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop through each sum and see if arguments and conditions are true
|
|
||||||
foreach ($sumArgs as $index => $value) {
|
|
||||||
$valid = true;
|
|
||||||
|
|
||||||
foreach ($conditions as $cidx => $condition) {
|
|
||||||
$arg = $aArgsArray[$cidx][$index];
|
|
||||||
|
|
||||||
// Loop through arguments
|
|
||||||
if (!is_numeric($arg)) {
|
|
||||||
$arg = Calculation::wrapResult(strtoupper($arg));
|
|
||||||
}
|
|
||||||
$testCondition = '=' . $arg . $condition;
|
|
||||||
if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
|
|
||||||
// Is not a value within our criteria
|
|
||||||
$valid = false;
|
|
||||||
|
|
||||||
break; // if false found, don't need to check other conditions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($valid) {
|
|
||||||
$returnValue += $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return
|
|
||||||
return $returnValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Database\DAverage;
|
||||||
use PhpOffice\PhpSpreadsheet\Calculation\Database\DCount;
|
use PhpOffice\PhpSpreadsheet\Calculation\Database\DCount;
|
||||||
use PhpOffice\PhpSpreadsheet\Calculation\Database\DMax;
|
use PhpOffice\PhpSpreadsheet\Calculation\Database\DMax;
|
||||||
use PhpOffice\PhpSpreadsheet\Calculation\Database\DMin;
|
use PhpOffice\PhpSpreadsheet\Calculation\Database\DMin;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Database\DSum;
|
||||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||||
|
|
||||||
class Conditional
|
class Conditional
|
||||||
|
|
@ -30,18 +31,7 @@ class Conditional
|
||||||
*/
|
*/
|
||||||
public static function AVERAGEIF($range, $condition, $averageRange = [])
|
public static function AVERAGEIF($range, $condition, $averageRange = [])
|
||||||
{
|
{
|
||||||
$range = Functions::flattenArray($range);
|
$database = self::databaseFromRangeAndValue($range, $averageRange);
|
||||||
$averageRange = Functions::flattenArray($averageRange);
|
|
||||||
if (empty($averageRange)) {
|
|
||||||
$averageRange = $range;
|
|
||||||
}
|
|
||||||
|
|
||||||
$database = array_map(
|
|
||||||
null,
|
|
||||||
array_merge([self::CONDITION_COLUMN_NAME], $range),
|
|
||||||
array_merge([self::VALUE_COLUMN_NAME], $averageRange)
|
|
||||||
);
|
|
||||||
|
|
||||||
$condition = [[self::CONDITION_COLUMN_NAME, self::VALUE_COLUMN_NAME], [$condition, null]];
|
$condition = [[self::CONDITION_COLUMN_NAME, self::VALUE_COLUMN_NAME], [$condition, null]];
|
||||||
|
|
||||||
return DAverage::evaluate($database, self::VALUE_COLUMN_NAME, $condition);
|
return DAverage::evaluate($database, self::VALUE_COLUMN_NAME, $condition);
|
||||||
|
|
@ -64,11 +54,11 @@ class Conditional
|
||||||
if (empty($args)) {
|
if (empty($args)) {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
} elseif (count($args) === 3) {
|
} elseif (count($args) === 3) {
|
||||||
return self::AVERAGEIF($args[2], $args[1], $args[0]);
|
return self::AVERAGEIF($args[1], $args[2], $args[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$conditions = self::buildConditionSetForRange(...$args);
|
$conditions = self::buildConditionSetForValueRange(...$args);
|
||||||
$database = self::buildDatabaseWithRange(...$args);
|
$database = self::buildDatabaseWithValueRange(...$args);
|
||||||
|
|
||||||
return DAverage::evaluate($database, self::VALUE_COLUMN_NAME, $conditions);
|
return DAverage::evaluate($database, self::VALUE_COLUMN_NAME, $conditions);
|
||||||
}
|
}
|
||||||
|
|
@ -146,8 +136,8 @@ class Conditional
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$conditions = self::buildConditionSetForRange(...$args);
|
$conditions = self::buildConditionSetForValueRange(...$args);
|
||||||
$database = self::buildDatabaseWithRange(...$args);
|
$database = self::buildDatabaseWithValueRange(...$args);
|
||||||
|
|
||||||
return DMax::evaluate($database, self::VALUE_COLUMN_NAME, $conditions);
|
return DMax::evaluate($database, self::VALUE_COLUMN_NAME, $conditions);
|
||||||
}
|
}
|
||||||
|
|
@ -170,12 +160,60 @@ class Conditional
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$conditions = self::buildConditionSetForRange(...$args);
|
$conditions = self::buildConditionSetForValueRange(...$args);
|
||||||
$database = self::buildDatabaseWithRange(...$args);
|
$database = self::buildDatabaseWithValueRange(...$args);
|
||||||
|
|
||||||
return DMin::evaluate($database, self::VALUE_COLUMN_NAME, $conditions);
|
return DMin::evaluate($database, self::VALUE_COLUMN_NAME, $conditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SUMIF.
|
||||||
|
*
|
||||||
|
* Totals the values of cells that contain numbers within the list of arguments
|
||||||
|
*
|
||||||
|
* Excel Function:
|
||||||
|
* SUMIF(range, criteria, [sum_range])
|
||||||
|
*
|
||||||
|
* @param mixed $range Data values
|
||||||
|
* @param mixed $sumRange
|
||||||
|
* @param mixed $condition
|
||||||
|
*
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
public static function SUMIF($range, $condition, $sumRange = [])
|
||||||
|
{
|
||||||
|
$database = self::databaseFromRangeAndValue($range, $sumRange);
|
||||||
|
$condition = [[self::CONDITION_COLUMN_NAME, self::VALUE_COLUMN_NAME], [$condition, null]];
|
||||||
|
|
||||||
|
return DSum::evaluate($database, self::VALUE_COLUMN_NAME, $condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SUMIFS.
|
||||||
|
*
|
||||||
|
* Counts the number of cells that contain numbers within the list of arguments
|
||||||
|
*
|
||||||
|
* Excel Function:
|
||||||
|
* SUMIFS(average_range, criteria_range1, criteria1, [criteria_range2, criteria2]…)
|
||||||
|
*
|
||||||
|
* @param mixed $args Pairs of Ranges and Criteria
|
||||||
|
*
|
||||||
|
* @return null|float|string
|
||||||
|
*/
|
||||||
|
public static function SUMIFS(...$args)
|
||||||
|
{
|
||||||
|
if (empty($args)) {
|
||||||
|
return 0.0;
|
||||||
|
} elseif (count($args) === 3) {
|
||||||
|
return self::SUMIF($args[1], $args[2], $args[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$conditions = self::buildConditionSetForValueRange(...$args);
|
||||||
|
$database = self::buildDatabaseWithValueRange(...$args);
|
||||||
|
|
||||||
|
return DSum::evaluate($database, self::VALUE_COLUMN_NAME, $conditions);
|
||||||
|
}
|
||||||
|
|
||||||
private static function buildConditionSet(...$args): array
|
private static function buildConditionSet(...$args): array
|
||||||
{
|
{
|
||||||
$conditions = self::buildConditions(1, ...$args);
|
$conditions = self::buildConditions(1, ...$args);
|
||||||
|
|
@ -183,7 +221,7 @@ class Conditional
|
||||||
return array_map(null, ...$conditions);
|
return array_map(null, ...$conditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function buildConditionSetForRange(...$args): array
|
private static function buildConditionSetForValueRange(...$args): array
|
||||||
{
|
{
|
||||||
$conditions = self::buildConditions(2, ...$args);
|
$conditions = self::buildConditions(2, ...$args);
|
||||||
|
|
||||||
|
|
@ -220,7 +258,7 @@ class Conditional
|
||||||
return self::buildDataSet(0, $database, ...$args);
|
return self::buildDataSet(0, $database, ...$args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function buildDatabaseWithRange(...$args): array
|
private static function buildDatabaseWithValueRange(...$args): array
|
||||||
{
|
{
|
||||||
$database = [];
|
$database = [];
|
||||||
$database[] = array_merge(
|
$database[] = array_merge(
|
||||||
|
|
@ -245,4 +283,22 @@ class Conditional
|
||||||
|
|
||||||
return array_map(null, ...$database);
|
return array_map(null, ...$database);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function databaseFromRangeAndValue(array $range, array $valueRange = []): array
|
||||||
|
{
|
||||||
|
$range = Functions::flattenArray($range);
|
||||||
|
|
||||||
|
$valueRange = Functions::flattenArray($valueRange);
|
||||||
|
if (empty($valueRange)) {
|
||||||
|
$valueRange = $range;
|
||||||
|
}
|
||||||
|
|
||||||
|
$database = array_map(
|
||||||
|
null,
|
||||||
|
array_merge([self::CONDITION_COLUMN_NAME], $range),
|
||||||
|
array_merge([self::VALUE_COLUMN_NAME], $valueRange)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $database;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,4 +120,22 @@ return [
|
||||||
[5],
|
[5],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
157559,
|
||||||
|
['Jan', 'Jan', 'Jan', 'Jan', 'Feb', 'Feb', 'Feb', 'Feb'],
|
||||||
|
'Feb',
|
||||||
|
[36693, 22100, 53321, 34440, 29889, 50090, 32080, 45500],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
66582,
|
||||||
|
['North 1', 'North 2', 'South 1', 'South 2', 'North 1', 'North 2', 'South 1', 'South 2,'],
|
||||||
|
'North 1',
|
||||||
|
[36693, 22100, 53321, 34440, 29889, 50090, 32080, 45500],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
138772,
|
||||||
|
['North 1', 'North 2', 'South 1', 'South 2', 'North 1', 'North 2', 'South 1', 'South 2,'],
|
||||||
|
'North ?',
|
||||||
|
[36693, 22100, 53321, 34440, 29889, 50090, 32080, 45500],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
],
|
||||||
[
|
[
|
||||||
2,
|
2,
|
||||||
[
|
[
|
||||||
|
|
@ -41,4 +44,20 @@ return [
|
||||||
],
|
],
|
||||||
'=B',
|
'=B',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
348000,
|
||||||
|
[223000, 125000, 456000, 322000, 340000, 198000, 310000, 250000, 460000, 261000, 389000, 305000],
|
||||||
|
[1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4],
|
||||||
|
1,
|
||||||
|
['North', 'North', 'South', 'North', 'North', 'South', 'North', 'North', 'South', 'North', 'North', 'South'],
|
||||||
|
'North',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
571000,
|
||||||
|
[223000, 125000, 456000, 322000, 340000, 198000, 310000, 250000, 460000, 261000, 389000, 305000],
|
||||||
|
[1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4],
|
||||||
|
'>2',
|
||||||
|
['Jeff', 'Chris', 'Carol', 'Jeff', 'Chris', 'Carol', 'Jeff', 'Chris', 'Carol', 'Jeff', 'Chris', 'Carol'],
|
||||||
|
'Jeff',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
],
|
||||||
[
|
[
|
||||||
80.5,
|
80.5,
|
||||||
[75, 94, 86, 'incomplete'],
|
[75, 94, 86, 'incomplete'],
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
],
|
||||||
[
|
[
|
||||||
2,
|
2,
|
||||||
['Y', 'Y', 'N'],
|
['Y', 'Y', 'N'],
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
],
|
||||||
[
|
[
|
||||||
2,
|
2,
|
||||||
[
|
[
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
],
|
||||||
[
|
[
|
||||||
1,
|
1,
|
||||||
[
|
[
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue