├── .gitignore ├── README.md ├── composer.json ├── phpunit.xml ├── src ├── Arr.php ├── ArrTrait.php ├── Chain.php ├── Collection.php ├── CollectionTrait.php ├── DollarDash.php ├── Object.php └── ObjectTrait.php └── test ├── ArrayFunctionsTest.php ├── ChainTest.php ├── CollectionFunctionsTest.php ├── ObjectFunctionsTest.php └── TestCase.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.lock -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DollarDash 2 | 3 | > A Treasure Chest of Functional/Utility Goodness and Normalized Sanity for PHP. 4 | 5 | **Basically, [Lodash](https://lodash.com/) for PHP** 6 | 7 | ~~Very much~~ 100% inspired by the great work in Javascript-land over at: https://lodash.com/docs 8 | _Currently_, **much of the documentation and test cases** are either very closely or **exactly** duplicated from the https://lodash.com/docs project. 9 | 10 | ~~Much~~ All real credit goes to the [lodash contributors](https://github.com/lodash/lodash/graphs/contributors) 11 | 12 | ### Example Usages 13 | 14 | #### Method Chaining 15 | ```php 16 | use Eaybar\DollarDash\DollarDash as Dash; 17 | 18 | Dash::chain([1, 2, 3, 1, 2, 3]) 19 | ->without(2, 3) 20 | ->first(); 21 | // -> 1 22 | 23 | Dash::chain([1, 2, 3, 1, 2, 3]) 24 | ->without(2, 3) 25 | ->value(); 26 | // -> [1, 1] 27 | 28 | Dash::chain([1, 2, 3, 4, 5]) 29 | ->without(4) 30 | ->every(function ($item) { 31 | return $item !== 4; 32 | }); 33 | // -> true 34 | ``` 35 | 36 | #### Import the kitchen sink 37 | ```php 38 | use Eaybar\DollarDash\DollarDash as Dash; 39 | 40 | Dash::dropRightWhile([0, 1, 2, 3], function ($element, $index, $array) { 41 | return $element > 1; 42 | }); 43 | // -> [0, 1] 44 | 45 | Dash::compact([0, 1, false, 2, '', 3]); 46 | // -> [1, 2, 3] 47 | 48 | Dash::every([2, 4, 6], function ($item) { 49 | return $item > 1; 50 | }); 51 | // -> true 52 | 53 | Dash::some([2, 3, 6], function ($item) { 54 | return $item < 3; 55 | }); 56 | // -> true 57 | ``` 58 | 59 | #### Import by module 60 | ```php 61 | use Eaybar\DollarDash\Arr; 62 | 63 | Arr::dropRightWhile([0, 1, 2, 3], function ($element, $index, $array) { 64 | return $element > 1; 65 | }); 66 | // -> [0, 1] 67 | 68 | Arr::compact([0, 1, false, 2, '', 3]); 69 | // -> [1, 2, 3] 70 | ``` 71 | 72 | **W**ork **I**n **P**rogress. For example usage and to see it in action, head over the the `test/` directory and/or the [Lodash documentation](https://lodash.com/docs) 73 | 74 | ### Current Features/Test Coverage see `test/` 75 | 76 | ArrayFunctions 77 | - [x] test array chunk 78 | - [x] test array compact 79 | - [x] test array difference 80 | - [x] test array drop 81 | - [x] test array dropRight 82 | - [x] test array dropRightWhile 83 | - [x] test array dropWhile 84 | - [x] test array findIndex 85 | - [x] test array first 86 | - [x] test array pull 87 | - [x] test array without 88 | 89 | Chain 90 | - [x] test chain instance 91 | - [x] test chain array chainable method 92 | - [x] test chain array chainable into value method 93 | - [x] test chain array method into collection value method 94 | 95 | CollectionFunctions 96 | - [x] test collection every 97 | - [x] test collection every has alias all 98 | - [x] test collection some 99 | - [x] test collection some has alias any 100 | 101 | ObjectFunctions 102 | - [x] test object assign 103 | 104 | #### Thoughts? Comments? 105 | 106 | Find me at [erikaybar.name](http://erikaybar.name/) or ping me at [@erikthedev_](https://twitter.com/erikthedev_) 107 | 108 | #### To run the tests 109 | 110 | ``` 111 | # From the project root 112 | composer install 113 | vendor/bin/phpunit 114 | ``` 115 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "erikaybar/dollar-dash", 3 | "description": "A Treasure Chest of Functional Goodness and Normalized Sanity for PHP. Basically, Lodash for PHP", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Erik Aybar", 8 | "email": "erikthedeveloper@gmail.com" 9 | } 10 | ], 11 | "require": {}, 12 | "require-dev": { 13 | "phpunit/phpunit": "^4.7" 14 | }, 15 | "autoload": { 16 | "psr-4": {"Eaybar\\DollarDash\\": "src/"} 17 | }, 18 | "autoload-dev": { 19 | "classmap": ["test/"] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | test/ 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Arr.php: -------------------------------------------------------------------------------- 1 | $old_length) { 70 | return []; 71 | } 72 | 73 | return array_slice($array, 0, $old_length - $n); 74 | } 75 | 76 | /** 77 | * Creates a slice of array excluding elements dropped from the end. 78 | * Elements are dropped until predicate returns falsey. 79 | * The predicate is bound to thisArg and invoked with three arguments: (value, index, array). 80 | * 81 | * TODO: utilize _.matches, _.matchesProperty, and _.property callback shorthands 82 | * 83 | * @param $array 84 | * @param $predicate 85 | * @return array 86 | */ 87 | public static function dropRightWhile($array, $predicate) 88 | { 89 | $i = count($array) - 1; 90 | for (; $i !== 0 ; $i--) { 91 | if ($predicate($array[$i], $i, $array)) { 92 | array_pop($array); 93 | } else { 94 | break; 95 | } 96 | } 97 | 98 | return $array; 99 | } 100 | 101 | /** 102 | * Creates a slice of array excluding elements dropped from the beginning. 103 | * Elements are dropped until predicate returns falsey. 104 | * The predicate is bound to thisArg and invoked with three arguments: (value, index, array). 105 | * 106 | * TODO: utilize _.matches, _.matchesProperty, and _.property callback shorthands 107 | * 108 | * @param $array 109 | * @param $predicate 110 | * @return array 111 | */ 112 | public static function dropWhile($array, $predicate) 113 | { 114 | $original_length = count($array); 115 | $counter = 1; 116 | for (; $counter !== $original_length ; $counter++) { 117 | if ($predicate($array[0], 0, $array)) { 118 | array_shift($array); 119 | } else { 120 | break; 121 | } 122 | } 123 | 124 | return $array; 125 | } 126 | 127 | /** 128 | * This method is like _.find except that it returns the index of the first element predicate returns truthy for 129 | * instead of the element itself. 130 | * @param $array 131 | * @param $test 132 | * @return int|string 133 | */ 134 | public static function findIndex($array, $test) 135 | { 136 | foreach ($array as $key => $value) { 137 | if ($test($value)) { 138 | return $key; 139 | } 140 | } 141 | 142 | return -1; 143 | } 144 | 145 | /** 146 | * Gets the first element of array. 147 | * @param $array 148 | * @return mixed 149 | */ 150 | public static function first($array) 151 | { 152 | return array_shift($array); 153 | } 154 | 155 | /** 156 | * Removes all provided values from array using SameValueZero for equality comparisons. 157 | * Note: Unlike _.without, this method mutates array. 158 | * @param $array 159 | * @param $_values 160 | * @return array 161 | */ 162 | public static function pull(&$array, $_values) 163 | { 164 | $values = array_slice(func_get_args(), 1); 165 | $array = array_values( 166 | array_diff($array, $values) 167 | ); 168 | 169 | return $array; 170 | } 171 | 172 | /** 173 | * Creates an array excluding all provided values using SameValueZero for equality comparisons. 174 | * @param $array 175 | * @param $_values 176 | * @return array 177 | */ 178 | public static function without($array, $_values) 179 | { 180 | $values = array_slice(func_get_args(), 1); 181 | return array_values( 182 | array_diff($array, $values) 183 | ); 184 | } 185 | } -------------------------------------------------------------------------------- /src/Chain.php: -------------------------------------------------------------------------------- 1 | value = $value; 21 | } 22 | 23 | public function value() 24 | { 25 | return $this->value; 26 | } 27 | 28 | public function __call($method_name, $arguments) 29 | { 30 | if (!method_exists(DollarDash::class, $method_name)) { 31 | throw new \Exception("WTF no {$method_name} method..."); 32 | } 33 | 34 | array_unshift($arguments, $this->value); 35 | $result = call_user_func_array([DollarDash::class, $method_name], $arguments); 36 | 37 | if (array_search($method_name, static::$non_chainable_methods)) { 38 | return $result; 39 | } else { 40 | $this->value = $result; 41 | return $this; 42 | } 43 | } 44 | 45 | 46 | } -------------------------------------------------------------------------------- /src/Collection.php: -------------------------------------------------------------------------------- 1 | $value) { 22 | if (!$testFunc($value, $key, $collection)) { 23 | return false; 24 | } 25 | } 26 | 27 | return true; 28 | } 29 | 30 | /** 31 | * Checks if predicate returns truthy for any element of collection. 32 | * The function returns as soon as it finds a passing value and does not iterate over the entire collection. 33 | * The predicate is invoked with three arguments: (value, index|key, collection). 34 | * @param array $collection 35 | * @param callable $testFunc 36 | * @return bool 37 | */ 38 | public static function some($collection, $testFunc) 39 | { 40 | // No need to iterate through entire collection. Short circuit upon first test "success". 41 | foreach ($collection as $key => $value) { 42 | if ($testFunc($value, $key, $collection)) { 43 | return true; 44 | } 45 | } 46 | 47 | return false; 48 | } 49 | 50 | /** 51 | * Iterates over elements of collection, returning an array of all elements predicate returns truthy for. 52 | * @param mixed|array $collection 53 | * @param callable $predicate 54 | * @return array 55 | */ 56 | public static function filter($collection, $predicate) 57 | { 58 | // TODO: Consider how to treat array vs. associative array vs. other iterables? 59 | return array_values( 60 | array_filter($collection, $predicate) 61 | ); 62 | } 63 | 64 | /** 65 | * @see CollectionTrait::every 66 | * @param array $collection 67 | * @param callable $testFunc 68 | * @return bool 69 | */ 70 | public static function all() 71 | { 72 | return call_user_func_array([static::class, 'every'], func_get_args()); 73 | } 74 | 75 | /** 76 | * @see CollectionTrait::some 77 | * @param array $collection 78 | * @param callable $testFunc 79 | * @return bool 80 | */ 81 | public static function any() 82 | { 83 | return call_user_func_array([static::class, 'some'], func_get_args()); 84 | } 85 | } -------------------------------------------------------------------------------- /src/DollarDash.php: -------------------------------------------------------------------------------- 1 | assertCount(4, $chunked); 13 | $this->assertCount(1, array_pop($chunked)); 14 | } 15 | 16 | public function test_array_compact() 17 | { 18 | $original = [0, 1, false, 2, '', 3]; 19 | $compacted = Arr::compact($original); 20 | $this->assertEquals([1, 2, 3], $compacted); 21 | } 22 | 23 | public function test_array_difference() 24 | { 25 | $original_01 = [1, 2, 3, 9]; 26 | $original_02 = [4, 2]; 27 | $original_03 = [4, 9]; 28 | $original_04 = [0, 3]; 29 | 30 | $this->assertEquals([1, 3, 9], Arr::difference($original_01, $original_02)); 31 | // Accepts n-number "exclude arrays" 32 | $this->assertEquals([1, 3], Arr::difference($original_01, $original_02, $original_03)); 33 | $this->assertEquals([1], Arr::difference($original_01, $original_02, $original_03, $original_04)); 34 | } 35 | 36 | public function test_array_drop() 37 | { 38 | $this->assertEquals( 39 | [2, 3], 40 | Arr::drop([1, 2, 3]) 41 | ); 42 | 43 | $this->assertEquals( 44 | [3], 45 | Arr::drop([1, 2, 3], 2) 46 | ); 47 | 48 | $this->assertEquals( 49 | [], 50 | Arr::drop([1, 2, 3], 5) 51 | ); 52 | } 53 | 54 | public function test_array_dropRight() 55 | { 56 | $this->assertEquals( 57 | [1, 2], 58 | Arr::dropRight([1, 2, 3]) 59 | ); 60 | 61 | $this->assertEquals( 62 | [1], 63 | Arr::dropRight([1, 2, 3], 2) 64 | ); 65 | 66 | $this->assertEquals( 67 | [], 68 | Arr::dropRight([1, 2, 3], 5) 69 | ); 70 | } 71 | 72 | public function test_array_dropRightWhile() 73 | { 74 | $this->assertEquals( 75 | [1], 76 | Arr::dropRightWhile([1, 2, 3], function ($element) { 77 | return $element > 1; 78 | }) 79 | ); 80 | 81 | // Now with all 3 arguments in predicate 82 | $this->assertEquals( 83 | [0, 1], 84 | Arr::dropRightWhile([0, 1, 2, 3], function ($element, $index, $array) { 85 | return $element > 1; 86 | }) 87 | ); 88 | } 89 | 90 | public function test_array_dropWhile() 91 | { 92 | $this->assertEquals( 93 | [3], 94 | Arr::dropWhile([1, 2, 3], function ($element) { 95 | return $element < 3; 96 | }) 97 | ); 98 | 99 | // Now with all 3 arguments in predicate 100 | $this->assertEquals( 101 | ['Bill', 'Bob'], 102 | Arr::dropWhile(['Andrew', 'Yolanda', 'Bill', 'Bob'], function ($element) { 103 | return substr($element, 0, 1) !== 'B'; 104 | }) 105 | ); 106 | } 107 | 108 | public function test_array_findIndex() 109 | { 110 | $users = [ 111 | ['user' => 'barney', 'active' => false], 112 | ['user' => 'fred', 'active' => false], 113 | ['user' => 'pebbles', 'active' => true] 114 | ]; 115 | 116 | $this->assertEquals( 117 | 1, 118 | Arr::findIndex($users, function ($user) { 119 | return $user['user'] == 'fred'; 120 | }) 121 | ); 122 | 123 | $this->assertEquals( 124 | -1, 125 | Arr::findIndex($users, function ($user) { 126 | return $user['user'] == 'billy'; 127 | }) 128 | ); 129 | } 130 | 131 | public function test_array_first() 132 | { 133 | $target = [4, 5, 6]; 134 | $this->assertEquals(4, Arr::first($target)); 135 | $this->assertCount(3, $target); 136 | $this->assertEquals(null, Arr::first([])); 137 | } 138 | 139 | public function test_array_pull() 140 | { 141 | $target = [1, 2, 3, 1, 2, 3]; 142 | $this->assertEquals([1, 1], Arr::pull($target, 2, 3)); 143 | $this->assertEquals([1, 1], $target); 144 | } 145 | 146 | public function test_array_without() 147 | { 148 | $target = [1, 2, 3, 1, 2, 3]; 149 | $this->assertEquals([1, 1], Arr::without($target, 2, 3)); 150 | $this->assertCount(6, $target); 151 | } 152 | } -------------------------------------------------------------------------------- /test/ChainTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf( 12 | Chain::class, 13 | DollarDash::chain([1, 2, 3]) 14 | ); 15 | } 16 | 17 | public function test_chain_array_chainable_method() 18 | { 19 | $this->assertEquals( 20 | [1, 1], 21 | DollarDash::chain([1, 2, 3, 1, 2, 3]) 22 | ->without(2, 3) 23 | ->value() 24 | ); 25 | } 26 | 27 | public function test_chain_array_chainable_into_value_method() 28 | { 29 | $this->assertEquals( 30 | 1, 31 | DollarDash::chain([1, 2, 3, 1, 2, 3]) 32 | ->without(2, 3) 33 | ->first() 34 | ); 35 | } 36 | 37 | public function test_chain_array_method_into_collection_value_method() 38 | { 39 | $this->assertTrue( 40 | DollarDash::chain([1, 2, 3, 4, 5]) 41 | ->without(4) 42 | ->every(function ($item) { 43 | return $item !== 4; 44 | }) 45 | ); 46 | } 47 | } -------------------------------------------------------------------------------- /test/CollectionFunctionsTest.php: -------------------------------------------------------------------------------- 1 | assertTrue( 11 | Collection::every([2, 4, 6], function ($item) { 12 | return $item > 1; 13 | }) 14 | ); 15 | 16 | $this->assertFalse( 17 | Collection::every([2, 4, 6, 7], function ($item) { 18 | return $item > 6; 19 | }) 20 | ); 21 | 22 | $this->assertFalse( 23 | Collection::every([9, 2, 4, 6], function ($item) { 24 | return $item > 6; 25 | }) 26 | ); 27 | } 28 | 29 | public function test_collection_every_has_alias_all() 30 | { 31 | $this->assertTrue( 32 | Collection::all([2, 4, 6], function ($item) { 33 | return $item > 1; 34 | }) 35 | ); 36 | } 37 | 38 | public function test_collection_some() 39 | { 40 | $this->assertTrue( 41 | Collection::some([2, 3, 6], function ($item) { 42 | return $item < 3; 43 | }) 44 | ); 45 | 46 | $this->assertFalse( 47 | Collection::some([2, 3, 6], function ($item) { 48 | return $item > 6; 49 | }) 50 | ); 51 | } 52 | 53 | public function test_collection_some_has_alias_any() 54 | { 55 | $this->assertTrue( 56 | Collection::any([2, 3, 6], function ($item) { 57 | return $item < 3; 58 | }) 59 | ); 60 | } 61 | 62 | public function test_collection_filter() 63 | { 64 | $this->assertNotContains( 65 | 'joe', 66 | Collection::filter(['bob', 'joe', 'sally', 'joe'], function ($item) { 67 | return $item !== 'joe'; 68 | }) 69 | ); 70 | 71 | $this->assertEquals( 72 | ['bob', 'sally'], 73 | Collection::filter(['bob', 'joe', 'sally', 'joe'], function ($item) { 74 | return $item !== 'joe'; 75 | }) 76 | ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /test/ObjectFunctionsTest.php: -------------------------------------------------------------------------------- 1 | assertEquals( 11 | ['user' => 'fred', 'age' => 40], 12 | Object::assign(['user' => 'barney'], ['age' => 40], ['user' => 'fred']) 13 | ); 14 | } 15 | } -------------------------------------------------------------------------------- /test/TestCase.php: -------------------------------------------------------------------------------- 1 |