From 66b81eb282b38a6c815ce2dab8e89f62bc219a43 Mon Sep 17 00:00:00 2001 From: Joby Elliott Date: Wed, 14 Aug 2024 08:48:19 -0600 Subject: [PATCH] initial commit of array functions --- src/Arrays/ArrayFunctions.php | 119 ++++++++++++++++++++++++++++ tests/Arrays/ArrayFunctionsTest.php | 76 ++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 src/Arrays/ArrayFunctions.php create mode 100644 tests/Arrays/ArrayFunctionsTest.php diff --git a/src/Arrays/ArrayFunctions.php b/src/Arrays/ArrayFunctions.php new file mode 100644 index 0000000..5dc2d04 --- /dev/null +++ b/src/Arrays/ArrayFunctions.php @@ -0,0 +1,119 @@ + $data + * @param bool $null_high + * @return T|null + */ + public static function min(iterable $data, bool $null_high = false): mixed + { + $values = []; + foreach ($data as $value) { + // if nulls are low, they are the lowest value possible so short-circuit if we find one + if (!$null_high && is_null($value)) return null; + $values[] = $value; + } + if (empty($values)) throw new Exception("Minimum is undefined if there are no values"); + $values = array_unique($values); + if ($null_high) { + usort( + $values, + function ($a, $b) { + if ($a === $b) return 0; + if (is_null($a)) return 1; + if (is_null($b)) return -1; + return $a <=> $b; + } + ); + } else { + usort( + $values, + function ($a, $b) { + if ($a === $b) return 0; + if (is_null($a)) return -1; + if (is_null($b)) return 1; + return $a <=> $b; + } + ); + } + return $values[0]; + } + + /** + * Mostly this behaves like the built-in max function, but it has an + * optional parameter to control whether null values are considered high or + * low. By default they are considered low. + * + * @template T + * @param iterable $data + * @param bool $null_high + * @return T|null + */ + public static function max(iterable $data, bool $null_high = false): mixed + { + $values = []; + foreach ($data as $value) { + // if nulls are high, they are the highest value possible so short-circuit if we find one + if ($null_high && is_null($value)) return null; + $values[] = $value; + } + if (empty($values)) throw new Exception("Minimum is undefined if there are no values"); + $values = array_unique($values); + if ($null_high) { + usort( + $values, + function ($a, $b) { + if ($a === $b) return 0; + if (is_null($a)) return 1; + if (is_null($b)) return -1; + return $a <=> $b; + } + ); + } else { + usort( + $values, + function ($a, $b) { + if ($a === $b) return 0; + if (is_null($a)) return -1; + if (is_null($b)) return 1; + return $a <=> $b; + } + ); + } + return end($values); + } +} diff --git a/tests/Arrays/ArrayFunctionsTest.php b/tests/Arrays/ArrayFunctionsTest.php new file mode 100644 index 0000000..fcd0016 --- /dev/null +++ b/tests/Arrays/ArrayFunctionsTest.php @@ -0,0 +1,76 @@ +assertEquals(1, ArrayFunctions::min($array)); + $array = [5, 4, 3, 2, 1]; + $this->assertEquals(1, ArrayFunctions::min($array)); + // by default nulls are low, and should be returned as if they are the lowest value + $array = [1, 2, 3, 4, 5, null]; + $this->assertEquals(null, ArrayFunctions::min($array)); + // if nulls are high, they should be skipped because they aren't the min + $this->assertEquals(1, ArrayFunctions::min($array, true)); + // if the array only contains a null value it should be returned either way + $array = [null]; + $this->assertEquals(null, ArrayFunctions::min($array)); + $this->assertEquals(null, ArrayFunctions::min($array, true)); + // should behave alphabetically for strings + $array = ['a', 'b', 'c', 'd', 'e']; + $this->assertEquals('a', ArrayFunctions::min($array)); + $array = ['e', 'd', 'c', 'b', 'a']; + $this->assertEquals('a', ArrayFunctions::min($array)); + } + + public function testMax() + { + $array = [1, 2, 3, 4, 5]; + $this->assertEquals(5, ArrayFunctions::max($array)); + $array = [5, 4, 3, 2, 1]; + $this->assertEquals(5, ArrayFunctions::max($array)); + // by default nulls are low, and should be treated as if they are the lowest value + $array = [1, 2, 3, 4, 5, null]; + $this->assertEquals(5, ArrayFunctions::max($array)); + // if nulls are high, they should be returned as if they are the highest value + $this->assertEquals(null, ArrayFunctions::max($array, true)); + // if the array only contains a null value it should be returned either way + $array = [null]; + $this->assertEquals(null, ArrayFunctions::max($array)); + $this->assertEquals(null, ArrayFunctions::max($array, true)); + // should behave alphabetically for strings + $array = ['a', 'b', 'c', 'd', 'e']; + $this->assertEquals('e', ArrayFunctions::max($array)); + $array = ['e', 'd', 'c', 'b', 'a']; + $this->assertEquals('e', ArrayFunctions::max($array)); + } +}