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;
|
||||
$helper->log('Add data (end) . time: ' . round((string) ($d . 2)) . ' s');
|
||||
$helper->log('Add data (end) . time: ' . (string) round($d, 2) . ' s');
|
||||
|
||||
// Save
|
||||
$helper->write($spreadsheet, __FILE__);
|
||||
|
|
|
|||
|
|
@ -2314,12 +2314,12 @@ class Calculation
|
|||
],
|
||||
'SUMIF' => [
|
||||
'category' => Category::CATEGORY_MATH_AND_TRIG,
|
||||
'functionCall' => [MathTrig::class, 'SUMIF'],
|
||||
'functionCall' => [Statistical\Conditional::class, 'SUMIF'],
|
||||
'argumentCount' => '2,3',
|
||||
],
|
||||
'SUMIFS' => [
|
||||
'category' => Category::CATEGORY_MATH_AND_TRIG,
|
||||
'functionCall' => [MathTrig::class, 'SUMIFS'],
|
||||
'functionCall' => [Statistical\Conditional::class, 'SUMIFS'],
|
||||
'argumentCount' => '3+',
|
||||
],
|
||||
'SUMPRODUCT' => [
|
||||
|
|
|
|||
|
|
@ -162,6 +162,10 @@ abstract class DatabaseAbstract
|
|||
$dataValue = ($dataValues[$key]) ? 'TRUE' : 'FALSE';
|
||||
} elseif ($dataValues[$key] !== null) {
|
||||
$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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1284,44 +1284,22 @@ class MathTrig
|
|||
* Totals the values of cells that contain numbers within the list of arguments
|
||||
*
|
||||
* Excel Function:
|
||||
* SUMIF(value1[,value2[, ...]],condition)
|
||||
* SUMIF(range, criteria, [sum_range])
|
||||
*
|
||||
* @param mixed $aArgs Data values
|
||||
* @param string $condition the criteria that defines which cells will be summed
|
||||
* @param mixed $sumArgs
|
||||
* @Deprecated 1.17.0
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public static function SUMIF($aArgs, $condition, $sumArgs = [])
|
||||
public static function SUMIF($range, $criteria, $sumRange = [])
|
||||
{
|
||||
$returnValue = 0;
|
||||
|
||||
$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;
|
||||
return Statistical\Conditional::SUMIF($range, $criteria, $sumRange);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1330,7 +1308,12 @@ class MathTrig
|
|||
* Totals the values of cells that contain numbers within the list of arguments
|
||||
*
|
||||
* 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
|
||||
*
|
||||
|
|
@ -1338,47 +1321,7 @@ class MathTrig
|
|||
*/
|
||||
public static function SUMIFS(...$args)
|
||||
{
|
||||
$arrayList = $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;
|
||||
return Statistical\Conditional::SUMIFS(...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Database\DAverage;
|
|||
use PhpOffice\PhpSpreadsheet\Calculation\Database\DCount;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Database\DMax;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Database\DMin;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Database\DSum;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
|
||||
class Conditional
|
||||
|
|
@ -30,18 +31,7 @@ class Conditional
|
|||
*/
|
||||
public static function AVERAGEIF($range, $condition, $averageRange = [])
|
||||
{
|
||||
$range = Functions::flattenArray($range);
|
||||
$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)
|
||||
);
|
||||
|
||||
$database = self::databaseFromRangeAndValue($range, $averageRange);
|
||||
$condition = [[self::CONDITION_COLUMN_NAME, self::VALUE_COLUMN_NAME], [$condition, null]];
|
||||
|
||||
return DAverage::evaluate($database, self::VALUE_COLUMN_NAME, $condition);
|
||||
|
|
@ -64,11 +54,11 @@ class Conditional
|
|||
if (empty($args)) {
|
||||
return 0.0;
|
||||
} 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);
|
||||
$database = self::buildDatabaseWithRange(...$args);
|
||||
$conditions = self::buildConditionSetForValueRange(...$args);
|
||||
$database = self::buildDatabaseWithValueRange(...$args);
|
||||
|
||||
return DAverage::evaluate($database, self::VALUE_COLUMN_NAME, $conditions);
|
||||
}
|
||||
|
|
@ -146,8 +136,8 @@ class Conditional
|
|||
return 0.0;
|
||||
}
|
||||
|
||||
$conditions = self::buildConditionSetForRange(...$args);
|
||||
$database = self::buildDatabaseWithRange(...$args);
|
||||
$conditions = self::buildConditionSetForValueRange(...$args);
|
||||
$database = self::buildDatabaseWithValueRange(...$args);
|
||||
|
||||
return DMax::evaluate($database, self::VALUE_COLUMN_NAME, $conditions);
|
||||
}
|
||||
|
|
@ -170,12 +160,60 @@ class Conditional
|
|||
return 0.0;
|
||||
}
|
||||
|
||||
$conditions = self::buildConditionSetForRange(...$args);
|
||||
$database = self::buildDatabaseWithRange(...$args);
|
||||
$conditions = self::buildConditionSetForValueRange(...$args);
|
||||
$database = self::buildDatabaseWithValueRange(...$args);
|
||||
|
||||
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
|
||||
{
|
||||
$conditions = self::buildConditions(1, ...$args);
|
||||
|
|
@ -183,7 +221,7 @@ class Conditional
|
|||
return array_map(null, ...$conditions);
|
||||
}
|
||||
|
||||
private static function buildConditionSetForRange(...$args): array
|
||||
private static function buildConditionSetForValueRange(...$args): array
|
||||
{
|
||||
$conditions = self::buildConditions(2, ...$args);
|
||||
|
||||
|
|
@ -220,7 +258,7 @@ class Conditional
|
|||
return self::buildDataSet(0, $database, ...$args);
|
||||
}
|
||||
|
||||
private static function buildDatabaseWithRange(...$args): array
|
||||
private static function buildDatabaseWithValueRange(...$args): array
|
||||
{
|
||||
$database = [];
|
||||
$database[] = array_merge(
|
||||
|
|
@ -245,4 +283,22 @@ class Conditional
|
|||
|
||||
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],
|
||||
],
|
||||
],
|
||||
[
|
||||
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
|
||||
|
||||
return [
|
||||
[
|
||||
0,
|
||||
],
|
||||
[
|
||||
2,
|
||||
[
|
||||
|
|
@ -41,4 +44,20 @@ return [
|
|||
],
|
||||
'=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
|
||||
|
||||
return [
|
||||
[
|
||||
0,
|
||||
],
|
||||
[
|
||||
80.5,
|
||||
[75, 94, 86, 'incomplete'],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
[
|
||||
0,
|
||||
],
|
||||
[
|
||||
2,
|
||||
['Y', 'Y', 'N'],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
[
|
||||
0,
|
||||
],
|
||||
[
|
||||
2,
|
||||
[
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
[
|
||||
0,
|
||||
],
|
||||
[
|
||||
1,
|
||||
[
|
||||
|
|
|
|||
Loading…
Reference in New Issue