Browse Source

Add avg and median methods to CollectionTrait and Interface

Ian den Hartog 8 years ago
parent
commit
f1bfd8b6e8

+ 57 - 0
src/Collection/CollectionInterface.php

@@ -286,6 +286,63 @@ interface CollectionInterface extends Iterator, JsonSerializable
     public function min($callback, $type = SORT_NUMERIC);
 
     /**
+     * Returns the average of all the values extracted with $matcher
+     * or of this collection.
+     *
+     * ### Example:
+     *
+     * ```
+     * $items = [
+     *  ['invoice' => ['total' => 100]],
+     *  ['invoice' => ['total' => 200]]
+     * ];
+     *
+     * $total = (new Collection($items))->avg('invoice.total');
+     *
+     * // Total: 150
+     *
+     * $total = (new Collection([1, 2, 3]))->avg();
+     * // Total: 2
+     * ```
+     *
+     * @param string|callable|null $matcher The property name to sum or a function
+     * If no value is passed, an identity function will be used.
+     * that will return the value of the property to sum.
+     * @return float|int|null
+     */
+    public function avg($matcher = null);
+
+    /**
+     * Returns the median of all the values extracted with $matcher
+     * or of this collection.
+     *
+     * ### Example:
+     *
+     * ```
+     * $items = [
+     *  ['invoice' => ['total' => 400]],
+     *  ['invoice' => ['total' => 500]]
+     *  ['invoice' => ['total' => 100]]
+     *  ['invoice' => ['total' => 333]]
+     *  ['invoice' => ['total' => 200]]
+     * ];
+     *
+     * $total = (new Collection($items))->median('invoice.total');
+     *
+     * // Total: 333
+     *
+     * $total = (new Collection([1, 2, 3, 4]))->median();
+     * // Total: 2.5
+     * ```
+     *
+     * @param string|callable|null $matcher The property name to sum or a function
+     * If no value is passed, an identity function will be used.
+     * that will return the value of the property to sum.
+     * @return float|int|null
+     */
+    public function median($matcher = null);
+
+    /**
      * Returns a sorted iterator out of the elements in this collection,
      * ranked in ascending order by the results of running each value through a
      * callback. $callback can also be a string representing the column or property

+ 48 - 0
src/Collection/CollectionTrait.php

@@ -193,6 +193,54 @@ trait CollectionTrait
     /**
      * {@inheritDoc}
      */
+    public function avg($matcher = null)
+    {
+        $iterator = $this->unwrap();
+        $count = $iterator instanceof Countable ?
+            count($iterator) :
+            iterator_count($iterator);
+
+        if ($count === 0) {
+            return null;
+        }
+
+        return $this->sumOf($matcher) / $count;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function median($matcher = null)
+    {
+        $iterator = $this->unwrap();
+        $count = $iterator instanceof Countable ?
+            count($iterator) :
+            iterator_count($iterator);
+
+        if ($count === 0) {
+            return null;
+        }
+
+        $middle = (int)($count / 2);
+        $elements = $this;
+        if ($matcher != null) {
+            $elements = $elements->extract($matcher);
+        }
+        $values = $elements->toArray();
+        sort($values);
+
+        if ($count % 2) {
+            return $values[$middle];
+        }
+
+        return (new static([
+            $values[$middle - 1], $values[$middle],
+        ]))->avg();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public function sortBy($callback, $dir = SORT_DESC, $type = SORT_NUMERIC)
     {
         return new SortIterator($this->unwrap(), $callback, $dir, $type);

+ 54 - 0
tests/TestCase/Collection/CollectionTest.php

@@ -60,6 +60,60 @@ class CollectionTest extends TestCase
     }
 
     /**
+     * Tests that it is possible to convert an array into a collection
+     *
+     * @return void
+     */
+    public function testAvg()
+    {
+        $items = [1, 2, 3];
+        $collection = new Collection($items);
+        $this->assertEquals(2, $collection->avg());
+
+        $collection = new Collection([]);
+        $this->assertNull($collection->avg());
+
+        $items = [['foo' => 1], ['foo' => 2], ['foo' => 3]];
+        $collection = new Collection($items);
+        $this->assertEquals(2, $collection->avg('foo'));
+        $items = [
+            ['invoice' => ['total' => 100]],
+            ['invoice' => ['total' => 200]]
+        ];
+
+        $this->assertEquals(150, (new Collection($items))->avg('invoice.total'));
+    }
+
+    /**
+     * Tests that it is possible to convert an array into a collection
+     *
+     * @return void
+     */
+    public function testMedian()
+    {
+        $items = [5, 2, 4];
+        $collection = new Collection($items);
+        $this->assertEquals(4, $collection->median());
+
+        $collection = new Collection([]);
+        $this->assertNull($collection->median());
+
+        $items = [1, 2, 3, 4];
+        $collection = new Collection($items);
+        $this->assertEquals(2.5, $collection->median());
+
+        $items = [
+            ['invoice' => ['total' => 400]],
+            ['invoice' => ['total' => 500]],
+            ['invoice' => ['total' => 200]],
+            ['invoice' => ['total' => 100]],
+            ['invoice' => ['total' => 333]]
+        ];
+
+        $this->assertEquals(333, (new Collection($items))->median('invoice.total'));
+    }
+
+    /**
      * Tests that it is possible to convert an iterator into a collection
      *
      * @return void