.scrutinizer.yml 0000666 00000002126 13436746323 0007746 0 ustar 00 build:
environment:
php: '5.6.9'
tests:
override:
-
command: 'vendor/bin/phpspec run -c ci/phpspec-with-coverage.yml -vvv'
coverage:
file: 'coverage.clover'
format: 'php-clover'
-
command: 'vendor/bin/phpunit'
-
command: 'phpenv global hhvm && vendor/bin/phpspec run -vvv'
-
command: 'phpenv global hhvm vendor/bin/phpunit'
-
command: 'phpenv global 7.0.0 && vendor/bin/phpspec run -vvv'
-
command: 'phpenv global 7.0.0 vendor/bin/phpunit'
tools:
php_code_sniffer:
config:
standard: "PSR2"
sensiolabs_security_checker: true
php_mess_detector:
config:
ruleset: ci/phpmd.xml
checks:
php:
code_rating: true
duplication: true
build_failure_conditions:
- 'issues.label("coding-style").exists'
- 'issues.severity(>= MINOR).exists'
- 'project.metric("scrutinizer.quality", < 7)'
- 'project.metric("scrutinizer.test_coverage", < 0.95)'
filter:
excluded_paths:
- tests/performance/*
README.md 0000666 00000125046 13436746323 0006052 0 ustar 00 # Knapsack
**Collection pipeline library for PHP**
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/5fcb3dc2-2061-4da3-853b-a5e2a35a35fb/mini.png)](https://insight.sensiolabs.com/projects/5fcb3dc2-2061-4da3-853b-a5e2a35a35fb) [![Build Status](https://scrutinizer-ci.com/g/DusanKasan/Knapsack/badges/build.png?b=master)](https://scrutinizer-ci.com/g/DusanKasan/Knapsack/build-status/master) [![Code Coverage](https://scrutinizer-ci.com/g/DusanKasan/Knapsack/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/DusanKasan/Knapsack/?branch=master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/DusanKasan/Knapsack/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/DusanKasan/Knapsack/?branch=master)
Knapsack is a collection library for PHP >= 5.6 that implements most of the sequence operations proposed by [Clojures sequences](http://clojure.org/sequences) plus some additional ones. All its features are available as functions (for functional programming) and as a [collection pipeline](http://martinfowler.com/articles/collection-pipeline/) object methods.
The heart of Knapsack is its [Collection class](https://github.com/DusanKasan/Knapsack/blob/master/src/Collection.php). However its every method calls a simple function with the same name that does the actual heavy lifting. These are located in `DusanKasan\Knapsack` namespace and you can find them [here](https://github.com/DusanKasan/Knapsack/blob/master/src/collection_functions.php). Collection is an iterator implementor that accepts Traversable object, array or even a callable that produces a Traversable object or array as constructor argument. It provides most of Clojures sequence functionality plus some extra features. It is also immutable - operations preformed on the collection will return new collection (or value) instead of modifying the original collection.
Most of the methods of Collection return lazy collections (such as filter/map/etc.). However, some return non-lazy collections (reverse) or simple values (count). For these operations all of the items in the collection must be iterated over (and realized). There are also operations (drop) that iterate over some items of the collection but do not affect/return them in the result. This behaviour as well as laziness is noted for each of the operations.
If you want more example usage beyond what is provided here, check the [specs](https://github.com/DusanKasan/Knapsack/tree/master/tests/spec) and/or [scenarios](https://github.com/DusanKasan/Knapsack/tree/master/tests/scenarios). There are also [performance tests](https://github.com/DusanKasan/Knapsack/tree/master/tests/performance) you can run on your machine and see the computation time impact of this library (the output of these is included below).
Feel free to report any [issues](https://github.com/DusanKasan/Knapsack/issues) you find. I will do my best to fix them as soon as possible, but community [pull requests](https://github.com/DusanKasan/Knapsack/pulls) to fix them are more than welcome.
## Documentation
Check out the documentation (which is prettified version of this readme) at http://dusankasan.github.io/Knapsack
## Installation
Require this package using Composer.
```
composer require dusank/knapsack
```
## Usage
### Instantiate via static or dynamic constructor
```php
use DusanKasan\Knapsack\Collection;
$collection1 = new Collection([1, 2, 3]);
$collection2 = Collection::from([1, 2, 3]); //preferred since you can call methods on its result directly.
```
### Work with arrays, Traversable objects or callables that produce Traversables
```php
$collection1 = Collection::from([1, 2, 3]);
$collection2 = Collection::from(new ArrayIterator([1, 2, 3]);
//Used because Generator can not be rewound
$collection2 = Collection::from(function() { //must have 0 arguments
foreach ([1, 2, 3] as $value) {
yield $value;
}
});
```
### Basic map/reduce
```php
$result = Collection::from([1, 2])
->map(function($v) {return $v*2;})
->reduce(function($tmp, $v) {return $tmp+$v;}, 0);
echo $result; //6
```
### The same map/reduce using Knapsack's collection functions
```php
$result = reduce(
map(
[1, 2],
function($v) {return $v*2;}
),
function($tmp, $v) {return $tmp+$v;},
0
);
echo $result; //6
```
### Get first 5 items of Fibonacci's sequence
```php
$result = Collection::iterate([1,1], function($v) {
return [$v[1], $v[0] + $v[1]]; //[1, 2], [2, 3] ...
})
->map('\DusanKasan\Knapsack\first') //one of the collection functions
->take(5);
foreach ($result as $item) {
echo $item . PHP_EOL;
}
//1
//1
//2
//3
//5
```
### If array or Traversable would be returned from functions that return an item from the collection, it can be converted to Collection using the optional flag. By default it returns the item as is.
```php
$result = Collection::from([[[1]]])
->first(true)
->first();
vardump($result); //[1]
```
### Collections are immutable
```php
function multiplyBy2($v)
{
return $v * 2;
}
function multiplyBy3($v)
{
return $v * 3;
}
function add($a, $b)
{
return $a + $b;
}
$collection = Collection::from([1, 2]);
$result = $collection
->map('multiplyBy2')
->reduce(0, 'add');
echo $result; //6
//On the same collection
$differentResult = $collection
->map('multiplyBy3')
->reduce(0, 'add');
echo $differentResult; //9
```
### Keys are not unique by design
It would harm performance. This is only a problem if you need to call toArray(), then you should call values() before.
```php
$result = Collection::from([1, 2])->concat([3,4]);
//arrays have unique keys
$result->toArray(); //[3,4]
$result->values()->toArray(); //[1, 2, 3, 4]
//When iterating, you can have multiple keys.
foreach ($result as $key => $item) {
echo $key . ':' . $item . PHP_EOL;
}
//0:1
//1:2
//0:3
//1:4
```
### Collection trait is provided
If you wish to use all the Collection methods in your existing classes directly, no need to proxy their calls, you can just use the provided [CollectionTrait](https://github.com/DusanKasan/Knapsack/blob/master/src/CollectionTrait.php). This will work on any Traversable by default. In any other class you will have to override the getItems() method provided by the trait. Keep in mind that after calling filter or any other method that returns collection, the returned type will be actually Collection, not the original Traversable.
```php
class AwesomeIterator extends ArrayIterator {
use CollectionTrait;
}
$iterator = new AwesomeIterator([1, 2, 3]);
$iterator->size(); //3
```
## Performance tests
### PHP 5.6
```php
+------------------------------------------------------------------------------------+-----------------------+---------------------------+----------------------+
| operation details | native execution time | collection execution time | difference (percent) |
+------------------------------------------------------------------------------------+-----------------------+---------------------------+----------------------+
| array_map vs Collection::map on 10000 integers (addition) | 0.030887317657471s | 0.1881504535675s | 609% |
| array_map vs Collection::map on 10000 strings (concatenation) | 0.034685015678406s | 0.19358699321747s | 558% |
| array_map vs Collection::map on 10000 objects (object to field value) | 0.045831298828125s | 0.20278341770172s | 442% |
| array_map vs Collection::map on 10000 md5 invocations | 0.042794704437256s | 0.1973245382309s | 461% |
| array_map vs Collection::map on 10000 integers n, counting sum(0, n) the naive way | 5.0926527023315s | 5.2498435258865s | 103% |
+------------------------------------------------------------------------------------+-----------------------+---------------------------+----------------------+
```
### PHP 7.0.2
```php
+------------------------------------------------------------------------------------+-----------------------+---------------------------+----------------------+
| operation details | native execution time | collection execution time | difference (percent) |
+------------------------------------------------------------------------------------+-----------------------+---------------------------+----------------------+
| array_map vs Collection::map on 10000 integers (addition) | 0.0012169599533081s | 0.013433384895325s | 1103% |
| array_map vs Collection::map on 10000 strings (concatenation) | 0.0014688730239868s | 0.013579154014587s | 924% |
| array_map vs Collection::map on 10000 objects (object to field value) | 0.0023677349090576s | 0.016839718818665s | 711% |
| array_map vs Collection::map on 10000 md5 invocations | 0.0041363000869751s | 0.017279839515686s | 417% |
| array_map vs Collection::map on 10000 integers n, counting sum(0, n) the naive way | 1.4253485679626s | 1.2336760282516s | 86% |
+------------------------------------------------------------------------------------+-----------------------+---------------------------+----------------------+
```
## Constructors
These are ways how to create the Collection class. There is one default constructor and few named (static) ones.
#### new(array|Traversable|callback $input)
The default constructor accepts array, Traversable or a callable that takes no arguments and produces Traversable or array. The use case for the callable argument is for example a Generator, which can not be rewound so the Collection must be able to reconstruct it when rewinding itself.
```php
$collection = new Collection([1, 2, 3]);
```
```php
$collection = new Collection(new ArrayIterator([1, 2, 3]));
```
```php
$generatorFactory = function () {
foreach ([1, 2] as $value) {
yield $value;
}
};
$collection = new Collection($generatorFactory);
```
#### from(array|Traversable|callback $input)
Collection::from is a static alias of the default constructor. This is the preferred way to create a Collection.
```php
$collection = Collection::from([1, 2, 3]);
```
```php
$collection = Collection::from(new ArrayIterator([1, 2, 3]));
```
```php
$generatorFactory = function () {
foreach ([1, 2] as $value) {
yield $value;
}
};
$collection = Collection::from($generatorFactory);
```
#### iterate(mixed $input, callable $function)
Returns lazy collection of values, where first value is $input and all subsequent values are computed by applying $function to the last value in the collection. By default this produces an infinite collection. However you can end the collection by throwing a NoMoreItems exception.
```php
$collection = Collection::iterate(1, function ($value) {return $value + 1;}); // 1, 2, 3, 4 ...
```
#### repeat(mixed $value, int $times = -1)
Returns a lazy collection of $value repeated $times times. If $times is not provided the collection is infinite.
```php
Collection::repeat(1); //infinite collection of ones
```
```php
Collection::repeat(1, 4)->toArray(); //[1, 1, 1, 1]
```
#### range(int $start = 0, int $end = null, int step = 1)
Returns a lazy collection of numbers starting at $start, incremented by $step until $end is reached.
```php
Collection::range(0, 6, 2)->toArray(); //[0, 2, 4, 6]
```
## Operations
These are the operations (methods) provided by Collection class. For each one, there is a function with the same name in Knapsack namespace. The function has the same footprint as the method, except it has one extra argument prepended - the collection (array or Traversable).
### Standard Iterator methods
It implements http://php.net/manual/en/class.iterator.php
#### append(mixed $item, mixed $key = null) : Collection
Returns a lazy collection of items of this collection with $item added as last element. If $key is not provided, its key will be the next integer in the sequence.
```php
Collection::from([1, 3, 3, 2])
->append(1)
->toArray(); //[1, 3, 3, 2, 1]
```
```php
Collection::from([1, 3, 3, 2])
->append(1, 'key')
->toArray(); //[1, 3, 3, 2, 'key' => 1]
```
```php
toArray(append([1, 3, 3, 2], 1, 'key')); //[1, 3, 3, 2, 'key' => 1]
```
#### average() : int|float
Returns average of values in this collection.
```php
Collection::from([1, 2, 3, 2])->average(); //2
Collection::from([1, 2, 3, 2, 2])->average(); //2.2
Collection::from([])->average(); //0
```
```php
average([1, 2, 3]); //2
```
#### combine(array|Traversable $collection, bool $strict = false) : Collection
Combines the values of this collection as keys, with values of $collection as values. The resulting collection has length equal to the size of smaller collection. If $strict is true, the size of both collections must be equal, otherwise ItemNotFound is thrown. When strict, the collection is realized immediately.
```php
Collection::from(['a', 'b'])
->combine([1, 2])
->toArray(); //['a' => 1, 'b' => 2]
```
```php
toArray(combine(['a', 'b'], [1, 2])); //['a' => 1, 'b' => 2]
```
#### concat(...array|Traversable) : Collection
Returns a lazy collection with items from this collection followed by items from the collection from first argument, then second and so on.
```php
Collection::from([1, 3, 3, 2])
->concat([4, 5]) //If we would convert to array here, we would loose 2 items because of same keys [4, 5, 3, 2]
->values()
->toArray(); //[1, 3, 3, 2, 4, 5]
```
```php
toArray(values(concat([1, 3, 3, 2], [4, 5]))); //[1, 3, 3, 2, 4, 5]
```
#### contains(mixed $needle) : bool
Returns true if $needle is present in the collection.
```php
Collection::from([1, 3, 3, 2])->contains(2); //true
```
```php
contains([1, 3, 3, 2], 2); //true
```
#### countBy(callable $function) : Collection
Returns a collection of items whose keys are the return values of $function(value, key) and values are the number of items in this collection for which the $function returned this value.
```php
Collection::from([1, 2, 3, 4, 5])
->countBy(function ($value) {
return $value % 2 == 0 ? 'even' : 'odd';
})
->toArray(); //['odd' => [1, 3, 5], 'even' => [2, 4]]
```
```php
toArray(countBy([1, 2, 3, 4, 5], function ($value) {return $value % 2 == 0 ? 'even' : 'odd';}));
```
#### cycle() : Collection
Returns an infinite lazy collection of items in this collection repeated infinitely.
```php
Collection::from([1, 3, 3, 2])
->cycle()
->take(8) //we take just 8 items, since this collection is infinite
->values()
->toArray(); //[1, 3, 3, 2, 1, 3, 3, 2]
```
```php
toArray(values(take(cycle([1, 3, 3, 2]), 8))); //[1, 3, 3, 2, 1, 3, 3, 2]
```
#### diff(array|Traversable ...$collections) : Collection
Returns a lazy collection of items that are in $this but are not in any of the other arguments. Note that the ...$collections are iterated non-lazily.
```php
Collection::from([1, 3, 3, 2])
->diff([1, 3])
->toArray() //[3 => 2]
```
```php
toArray(diff([1, 3, 3, 2], [1, 3])); //[3 => 2]
```
#### distinct() : Collection
Returns a lazy collection of distinct items. The comparison whether the item is in the collection or not is the same as in in_array.
```php
Collection::from([1, 3, 3, 2])
->distinct()
->toArray() //[1, 3, 3 => 2] - each item has key of the first occurrence
```
```php
toArray(distinct([1, 3, 3, 2])); //[1, 3, 3 => 2] - each item has key of the first occurrence
```
#### drop(int $numberOfItems) : Collection
A form of slice that returns all but first $numberOfItems items.
```php
Collection::from([1, 2, 3, 4, 5])
->drop(4)
->toArray(); //[4 => 5]
```
```php
toArray(drop([1, 2, 3, 4, 5], 4)); //[4 => 5]
```
#### dropLast($numberOfItems = 1) : Collection
Returns a lazy collection with last $numberOfItems items skipped. These are still realized, just skipped.
```php
Collection::from([1, 2, 3])
->dropLast()
->toArray(); //[1, 2]
```
```php
Collection::from([1, 2, 3])
$collection
->dropLast(2)
->toArray(); //[1]
```
```php
toArray(dropLast([1, 2, 3], 2)); //[1]
```
#### dropWhile(callable $function) : Collection
Returns a lazy collection by removing items from this collection until first item for which $function(value, key) returns false.
```php
Collection::from([1, 3, 3, 2])
->dropWhile(function ($v) {
return $v < 3;
})
->toArray(); //[1 => 3, 2 => 3, 3 => 2])
```
```php
Collection::from([1, 3, 3, 2])
->dropWhile(function ($v, $k) {
return $k < 2 && $v < 3;
})
->toArray(); //[1 => 3, 2 => 3, 3 => 2])
```
```php
Collection::from([1, 3, 3, 2])
->dropWhile(function ($v, $k) {
return $k < 2 && $v < 3;
})
->toArray(); //[1 => 3, 2 => 3, 3 => 2])
```
```php
toArray(values(dropWhile([1, 3, 3, 2], function ($v) {return $v < 3;}))); // [3, 3, 2]
```
#### each(callable $function) : Collection
Returns a lazy collection in which $function(value, key) is executed for each item.
```php
Collection::from([1, 2, 3, 4, 5])
->each(function ($i) {
echo $i . PHP_EOL;
})
->toArray(); //[1, 2, 3, 4, 5]
//1
//2
//3
//4
//5
```
```php
each([1, 2, 3, 4, 5], function ($v) {echo $v . PHP_EOL;});
//1
//2
//3
//4
//5
```
#### every(callable $function) : bool
Returns true if $function(value, key) returns true for every item in this collection, false otherwise.
```php
Collection::from([1, 3, 3, 2])
->every(function ($v) {
return $v < 3;
}); //false
```
```php
Collection::from([1, 3, 3, 2])
->find(function ($v. $k) {
return $v < 4 && $k < 2;
}, 10); //false
```
```php
every([1, 3, 3, 2], function ($v) {return $v < 5;}); //true
```
#### except(array|Traversable $keys) : Collection
Returns a lazy collection without the items associated to any of the keys from $keys.
```php
Collection::from(['a' => 1, 'b' => 2])
->except(['a'])
->toArray(); //['b' => 2]
```
```php
toArray(except(['a' => 1, 'b' => 2], ['a'])); //['b' => 2]
```
#### extract(mixed $keyPath) : Collection
Returns a lazy collection of data extracted from $collection items by dot separated key path. Supports the * wildcard. If a key contains \ or * it must be escaped using \ character.
```php
$collection = Collection::from([['a' => ['b' => 1]], ['a' => ['b' => 2]], ['c' => ['b' => 3]]])
$collection->extract('a.b')->toArray(); //[1, 2]
$collection->extract('*.b')->toArray(); //[1, 2, 3]
```
```php
toArray(extract([['a' => ['b' => 1]], ['a' => ['b' => 2]]], 'a.b')); //[1, 2]
```
#### filter(callable $function = null) : Collection
Returns a lazy collection of items for which $function(value, key) returned true.
```php
Collection::from([1, 3, 3, 2])
->filter(function ($value) {
return $value > 2;
})
->values()
->toArray() //[3, 3]
```
```php
Collection::from([1, 3, 3, 2])
->filter(function ($value, $key) {
return $value > 2 && $key > 1;
})
->toArray() //[2 => 3]
```
```php
toArray(values(filter([1, 3, 3, 2], function ($value) {return $value > 2;}))); //[3, 3]
```
If `$function` is not provided, `\DusanKasan\Knapsack\identity` is used so every falsy value is removed.
```php
Collection::from([0, 0.0, false, null, "", []])
->filter()
->isEmpty() //true
```
```php
isEmpty(values(filter([0, 0.0, false, null, "", []]))); //true
```
#### find(callable $function, mixed $ifNotFound = null, bool $convertToCollection = false) : mixed|Collection
Returns first value for which $function(value, key) returns true. If no item is matched, returns $ifNotFound. If $convertToCollection is true and the return value is a collection (array|Traversable) an instance of Collection will be returned.
```php
Collection::from([1, 3, 3, 2])
->find(function ($value) {
return $value < 3;
}); //1
```
```php
Collection::from([1, 3, 3, 2])
->find(function ($value) {
return $value > 3;
}, 10); //10
```
```php
Collection::from([1, 3, 3, 2])
->find(function ($value, $key) {
return $value < 3 && $key > 1;
}); //2
```
```php
//if the output can be converted to Collection (it's array or Traversable), it will be.
Collection::from([1, [4, 5], 3, 2])
->find(function ($value) {
return is_array($value);
}, true)
->size(); //2
```
```php
find([1, 3, 3, 2], function ($value) {return $value > 2;}); //3
```
#### first(bool $convertToCollection = false) : mixed|Collection
Returns first value in the collection or throws ItemNotFound if the collection is empty. If $convertToCollection is true and the return value is a collection (array|Traversable) an instance of Collection will be returned.
```php
Collection::from([1, 2, 3])->first(); //1
```
```php
Collection::from([[1], 2, 3])->first(); //[1]
```
```php
Collection::from([])->first(); //throws ItemNotFound
```
```php
first([1, 2, 3]); //1
```
#### flatten(int $depth = -1) : Collection
Returns a lazy collection with one or multiple levels of nesting flattened. Removes all nesting when no $depth value is passed.
```php
Collection::from([1,[2, [3]]])
->flatten()
->values() //1, 2 and 3 have all key 0
->toArray() //[1, 2, 3]
```
```php
Collection::from([1,[2, [3]]])
->flatten(1)
->values() //1, 2 and 3 have all key 0
->toArray() //[1, 2, [3]]
```
```php
toArray(values(flatten([1, [2, [3]]]))); //[1, 2, 3]
```
#### flip() : Collection
Returns a lazy collection where keys and values are flipped.
```php
Collection::from(['a' => 0, 'b' => 1])
->flip()
->toArray() //['a', 'b']
```
```php
toArray(flip(['a' => 0, 'b' => 1])); //['a', 'b']
```
#### frequencies() : Collection
Returns a collection where keys are distinct items from this collection and their values are number of occurrences of each value.
```php
Collection::from([1, 3, 3, 2])
->frequencies()
->toArray(); //[1 => 1, 3 => 2, 2 => 1]
```
```php
toArray(frequencies([1, 3, 3, 2])); //[1 => 1, 3 => 2, 2 => 1]
```
#### get(mixed $key, bool $convertToCollection = false) : mixed|Collection
Returns value at the key $key. If multiple values have this key, return first. If no value has this key, throw `ItemNotFound`. If $convertToCollection is true and the return value is a collection (array|Traversable) an instance of Collection will be returned.
```php
Collection::from([1, 3, 3, 2])->get(2); //3
```
```php
Collection::from([1, [1, 2]])->get(1, true)->toArray(); //[1, 2]
```
```php
Collection::from([1, 3, 3, 2])->get(5); //throws ItemNotFound
```
```php
get([1, 3, 3, 2], 2); //3
```
#### getOrDefault(mixed $key, mixed $default = null, bool $convertToCollection = false) : mixed|Collection
Returns value at the key $key. If multiple values have this key, return first. If no value has this key, return $default. If $convertToCollection is true and the return value is a collection (array|Traversable) an instance of Collection is returned.
```php
Collection::from([1, 3, 3, 2])->getOrDefault(2); //3
```
```php
Collection::from([1, 3, 3, 2])->getOrDefault(5); //null
```
```php
Collection::from([1, 3, 3, 2])->getOrDefault(5, 'asd'); //'asd'
```
```php
Collection::from([1, 3, 3, 2])->getOrDefault(5, [1, 2], null, true)->toArray(); //[1, 2]
```
```php
getOrDefault([1, 3, 3, 2], 5, 'asd'); //'asd'
```
#### groupBy(callable $function) : Collection
Returns collection which items are separated into groups indexed by the return value of $function(value, key).
```php
Collection::from([1, 2, 3, 4, 5])
->groupBy(function ($value) {
return $value % 2;
})
->toArray(); //[1 => [1, 3, 5], 0 => [2, 4]]
```
```php
toArray(groupBy([1, 2, 3, 4, 5], function ($value) {return $value % 2;})); //[1 => [1, 3, 5], 0 => [2, 4]]
```
#### groupByKey(mixed $key) : Collection
Returns collection where items are separated into groups indexed by the value at given key.
```php
Collection::from([
['letter' => 'A', 'type' => 'caps'],
['letter' => 'a', 'type' => 'small'],
['letter' => 'B', 'type' => 'caps'],
])
->groupByKey('type')
->map('DusanKasan\Knapsack\toArray')
->toArray();
// [ 'caps' => [['letter' => 'A', 'type' => 'caps'], ...], 'small' => [['letter' => 'a', 'type' => 'small']]]
```
```php
$data = [
['letter' => 'A', 'type' => 'caps'],
['letter' => 'a', 'type' => 'small'],
['letter' => 'B', 'type' => 'caps'],
];
toArray(map(groupByKey($data, 'type'), 'toArray')); //[ 'caps' => [['letter' => 'A', 'type' => 'caps'], ...], 'small' => [['letter' => 'a', 'type' => 'small']]]
```
#### has(mixed $key) : bool
Checks for the existence of $key in this collection.
```php
Collection::from(['a' => 1])->has('a'); //true
```
```php
has(['a' => 1], 'a'); //true
```
#### indexBy(callable $function) : Collection
Returns a lazy collection by changing keys of this collection for each item to the result of $function(value, key) for that key/value.
```php
Collection::from([1, 3, 3, 2])
->indexBy(function ($v) {
return $v;
})
->toArray(); //[1 => 1, 3 => 3, 2 => 2]
```
```php
toArray(indexBy([1, 3, 3, 2], '\DusanKasan\Knapsack\identity')); //[1 => 1, 3 => 3, 2 => 2]
```
#### interleave(...array|Traversable $collection) : Collection
Returns a lazy collection of first item from first collection, first item from second, second from first and so on. Works with any number of arguments.
```php
Collection::from([1, 3, 3, 2])
->interleave(['a', 'b', 'c', 'd', 'e'])
->values()
->toArray(); //[1, 'a', 3, 'b', 3, 'c', 2, 'd', 'e']
```
```php
toArray(interleave([1, 3, 3, 2], ['a', 'b', 'c', 'd', 'e'])); //[1, 'a', 3, 'b', 3, 'c', 2, 'd', 'e']
```
#### interpose(mixed $separator) : Collection
Returns a lazy collection of items of this collection separated by $separator item.
```php
Collection::from([1, 2, 3])
->interpose('a')
->values() // we must reset the keys, because each 'a' has undecided key
->toArray(); //[1, 'a', 2, 'a', 3]
```
```php
toArray(interpose([1, 3, 3, 2], 'a')); //[1, 'a', 2, 'a', 3]
```
#### intersect(array|Traversable) : Collection
Returns a lazy collection of items that are in $collection and all the other arguments, indexed by the keys from the first collection. Note that the ...$collections are iterated non-lazily.
```php
Collection::from([1, 2, 3])
->intersect([1, 3])
->toArray(); //[1, 2 => 3]
```
```php
toArray(intersect([1, 2, 3],[1, 3])); //[1, 2 => 3]
```
#### isEmpty() : bool
Returns true if is collection is empty. False otherwise.
```php
Collection::from([1, 3, 3, 2])->isEmpty(); //false
```
```php
isEmpty([1]); //false
```
#### isNotEmpty() : bool
Opposite of isEmpty
```php
Collection::from([1, 3, 3, 2])>isNotEmpty(); //true
```
```php
isNotEmpty([1]); //true
```
#### keys() : Collection
Returns a lazy collection of the keys of this collection.
```php
Collection::from(['a' => [1, 2], 'b' => [2, 3]])
->keys()
->toArray(); //['a', 'b']
```
```php
toArray(keys(['a' => [1, 2], 'b' => [2, 3]])); //['a', 'b']
```
#### last(bool $convertToCollection = false) : mixed|Collection
Returns last value in the collection or throws ItemNotFound if the collection is empty. If $convertToCollection is true and the return value is a collection (array|Traversable) an instance of Collection is returned.
```php
Collection::from([1, 2, 3])->last(); //3
```
```php
Collection::from([1, 2, [3]])->last(true)->toArray(); //[1]
```
```php
Collection::from([])->last(); //throws ItemNotFound
```
```php
last([1, 2, 3]); //3
```
#### map(callable $function) : Collection
Returns collection where each value is changed to the output of executing $function(value, key).
```php
Collection::from([1, 3, 3, 2])
->map(function ($value) {
return $value + 1;
})
->toArray() //[2, 4, 4, 3]
```
```php
toArray(map([1, 3, 3, 2], '\DusanKasan\Knapsack\increment')); //[2, 4, 4, 3]
```
#### mapcat(callable $mapper) : Collection
Returns a lazy collection which is a result of calling map($mapper(value, key)) and then flatten(1).
```php
Collection::from([1, 3, 3, 2])
->mapcat(function ($value) {
return [[$value]];
})
->values()
->toArray(); //[[1], [3], [3], [2]]
```
```php
Collection::from([1, 3, 3, 2])
->mapcat(function ($key, $value) {
return [[$key]];
})
->values()
->toArray(); //[[0], [1], [2], [3]]
```
```php
toArray(values(mapcat([1, 3, 3, 2], function ($value) {return [[$value]];}))); //[[1], [3], [3], [2]]
```
#### max() : mixed
Returns mthe maximal value in this collection.
```php
Collection::from([1, 2, 3, 2])->max(); //3
Collection::from([])->max(); //null
```
```php
max([1, 2, 3, 2]); //3
```
#### min() : mixed
Returns mthe minimal value in this collection.
```php
Collection::from([1, 2, 3, 2])->min(); //1
Collection::from([])->min(); //null
```
```php
min([1, 2, 3, 2]); //1
```
#### only(array|Traversable $keys) : Collection
Returns a lazy collection of items associated to any of the keys from $keys.
```php
Collection::from(['a' => 1, 'b' => 2])
->only(['b'])
->toArray(); //['b' => 2]
```
```php
toArray(only(['a' => 1, 'b' => 2], ['b'])); //['b' => 2]
```
#### partition(int $numberOfItems, int $step = 0, array|Traversable $padding = []) : Collection
Returns a lazy collection of collections of $numberOfItems items each, at $step step apart. If $step is not supplied, defaults to $numberOfItems, i.e. the partitionsdo not overlap. If a $padding collection is supplied, use its elements asnecessary to complete last partition up to $numberOfItems items. In case there are not enough padding elements, return a partition with less than $numberOfItems items.
```php
Collection::from([1, 3, 3, 2])
->partition(3, 2, [0, 1])
->toArray(); //[[1, 3, 3], [2 => 3, 3 => 2, 0 => 0]]
```
```php
Collection::from([1, 3, 3, 2])
->partition(3, 2)
->toArray(); //[[1, 3, 3], [2 => 3, 3 => 2]]
```
```php
Collection::from([1, 3, 3, 2])
->partition(3)
->toArray(); //[[1, 3, 3], [3 => 2]]
```
```php
toArray(partition([1, 3, 3, 2], 3)); //[[1, 3, 3], [3 => 2]]
```
#### partitionBy(callable $function) : Collection
Creates a lazy collection of collections created by partitioning this collection every time $function(value, key) will return different result.
```php
Collection::from([1, 3, 3, 2])
->partitionBy(function ($v) {
return $v % 3 == 0;
})
->toArray(); //[[1], [1 => 3, 2 => 3], [3 => 2]]
```
```php
toArray(partitionBy([1, 3, 3, 2], function ($value) {return $value % 3 == 0;})); //[[1], [1 => 3, 2 => 3], [3 => 2]]
```
#### prepend(mixed $item, mixed $key = null) : Collection
Returns a lazy collection of items of this collection with $item added as first element. Its key will be $key. If $key is not provided, its key will be the next numerical index.
```php
Collection::from([1, 3, 3, 2])
->prepend(1)
->values() //both 1 have 0 key
->toArray(); //[1, 1, 3, 3, 2]
```
```php
Collection::from([1, 3, 3, 2])
->prepend('a', 1)
->toArray(); //['a' => 1, 0 => 1, 1 => 3, 2 => 3, 3 => 2]
```
#### realize() : Collection
Realizes collection - turns lazy collection into non-lazy one by iterating over it and storing the key/values.
```php
$helper->setValue(1);
$realizedCollection = Collection::from([1, 3, 3, 2])
->map(function ($item) use ($helper) {return $helper->getValue() + $item;})
->realize();
$helper->setValue(2);
$realizedCollection->toArray([2, 4, 4, 3]);
```
```php
toArray(realize([1, 3, 3, 2])); //[1, 3, 3, 2]
```
#### reduce(callable $function, mixed $start, bool $convertToCollectionv = false) : mixed
Reduces the collection to single value by iterating over the collection and calling $function(tmp, value, key) while passing $start and current key/item as parameters. The output of callable is used as $start in next iteration. The output of callable on last element is the return value of this function. If $convertToCollection is true and the return value is a collection (array|Traversable) an instance of Collection is returned.
```php
Collection::from([1, 3, 3, 2])
->reduce(
function ($tmp, $i) {
return $tmp + $i;
},
0
); //9
Collection::from([1, 3, 3, 2])
->reduce(
function ($tmp, $i) {
$tmp[] = $i + 1;
return $tmp;
},
[],
true
)
->first(); //2
```
```php
reduce([1, 3, 3, 2], function ($tmp, $value) {return $tmp + $value;}, 0); //9
```
#### reduceRight(callable $function, mixed $start, bool $convertToColleciton = false) : mixed
Like reduce, but walks from last item to the first one. If $convertToCollection is true and the return value is a collection (array|Traversable) an instance of Collection is returned.
```php
Collection::from([1, 3, 3, 2])
->reduceRight(
function ($tmp, $i) {
return $tmp + $i;
},
0
); //9
Collection::from([1, 3, 3, 2])
->reduce(
function ($tmp, $i) {
$tmp[] = $i + 1;
return $tmp;
},
[],
true
)
->first(); //3
```
```php
reduceRight([1, 3, 3, 2], function ($tmp, $value) {return $tmp + $value;}, 0); //9
```
#### reductions(callable $reduction, $start) : Collection
Returns a lazy collection of reduction steps.
```php
Collection::from([1, 3, 3, 2])
->reductions(function ($tmp, $i) {
return $tmp + $i;
}, 0)
->toArray(); //[1, 4, 7, 9]
```
```php
toArray(reductions([1, 3, 3, 2], function ($tmp, $value) {return $tmp + $value;}, 0)); //[1, 4, 7, 9]
```
#### reject(callable $filter) : Collection
Returns a lazy collection of items for which $filter(value, key) returned false.
```php
Collection::from([1, 3, 3, 2])
->reject(function ($value) {
return $value > 2;
})
->toArray() //[1, 3 => 2]
```
```php
Collection::from([1, 3, 3, 2])
->reject(function ($value, $key) {
return $value > 2 && $key > 1;
})
->toArray() //[1, 1 => 3, 3 => 2]
```
```php
toArray(reject([1, 3, 3, 2], function ($value) {return $value > 2;})); //[1, 1 => 3, 3 => 2]
```
#### replace(array|Traversable $replacementMap) : Collection
Returns a lazy collection with items from this collection equal to any key in $replacementMap replaced for their value.
```php
Collection::from([1, 3, 3, 2])
->replace([3 => 'a'])
->toArray(); //[1, 'a', 'a', 2]
```
```php
toArray(replace([1, 3, 3, 2], [3 => 'a'])); //[1, 'a', 'a', 2]
```
#### replaceByKeys(array|Traversable $replacementMap) : Collection
Returns a lazy collection with items from $collection, but items with keys that are found in keys of $replacementMap are replaced by their values.
```php
Collection::from([1, 3, 3, 2])
->replace([3 => 'a'])
->toArray(); //[1, 3, 3, 'a']
```
```php
toArray(replace([1, 3, 3, 2], [3 => 'a'])); //[1, 3, 3, 'a']
```
#### reverse() : Collection
Returns a non-lazy collection of items in this collection in reverse order.
```php
Collection::from([1, 2, 3])
->reverse()
->toArray(); //[2 => 3, 1 => 2, 0 => 1]
```
```php
toArray(reverse([1, 2, 3])); //[2 => 3, 1 => 2, 0 => 1]
```
#### second(bool $convertToCollection = false) : mixed
Returns the second item of $collection or throws ItemNotFound if $collection is empty or has 1 item. If $convertToCollection is true and the return value is a collection (array|Traversable) it is converted to Collection.
```php
Collection::from([1, 3, 3, 2])->second(); //3
```
```php
second([1, 3]); //3
```
#### shuffle() : Collection
Returns a collection of shuffled items from this collection
```php
Collection::from([1, 3, 3, 2])
->shuffle()
->toArray(); //something like [2 => 3, 0 => 1, 3 => 2, 1 => 3]
```
```php
toArray(shuffle([1, 3, 3, 2])); //something like [2 => 3, 0 => 1, 3 => 2, 1 => 3]
```
#### size() : int
Returns the number of items in this collection.
```php
Collection::from([1, 3, 3, 2])->size(); //4
```
```php
size([1, 3, 3, 2]); //4
```
#### sizeBetween(int $fromSize, int $toSize) : bool
Checks whether this collection has between $fromSize to $toSize items. $toSize can be smaller than $fromSize.
```php
Collection::from([1, 3, 3, 2])->sizeIsBetween(3, 5); //true
```
```php
sizeIsBetween([1, 3, 3, 2], 3, 5); //true
```
#### sizeIs(int $size) : bool
Checks whether this collection has exactly $size items.
```php
Collection::from([1, 3, 3, 2])->sizeIs(4); //true
```
```php
sizeIs([1, 3, 3, 2], 4); //true
```
#### sizeIsGreaterThan(int $size) : bool
Checks whether this collection has more than $size items.
```php
Collection::from([1, 3, 3, 2])->sizeIsGreaterThan(3); //true
```
```php
sizeIsGreaterThan([1, 3, 3, 2], 3); //true
```
#### sizeIsLessThan(int $size) : bool
Checks whether this collection has less than $size items.
```php
Collection::from([1, 3, 3, 2])->sizeIsLessThan(5); //true
```
```php
sizeIsLessThan([1, 3, 3, 2], 5); //true
```
#### slice(int $from, int $to) : Collection
Returns a lazy collection of items which are part of the original collection from item number $from to item number $to inclusive. The items before $from are also realized, just not returned.
```php
Collection::from([1, 2, 3, 4, 5])
->slice(2, 4)
->toArray(); //[2 => 3, 3 => 4]
```
```php
Collection::from([1, 2, 3, 4, 5])
->slice(4)
->toArray(); //[4 => 5]
```
```php
toArray(slice([1, 2, 3, 4, 5], 4)); //[4 => 5]
```
#### some(callable $function) : bool
Returns true if $function(value, key) returns true for at least one item in this collection, false otherwise.
```php
Collection::from([1, 3, 3, 2])
->every(function ($value) {
return $value < 3;
}); //true
```
```php
Collection::from([1, 3, 3, 2])
->find(function ($value, $key) {
return $value < 4 && $key < 2;
}, 10); //true
```
```php
some([1, 3, 3 ,2], function ($value) {return $value < 3;}); //true
```
#### sort(callable $function) : Collection
Returns collection sorted using $function(value1, value2, key1, key2). $function should return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second.
```php
Collection::from([3, 1, 2])
->sort(function ($a, $b) {
return $a > $b;
})
->toArray(); //[1 => 1, 2 => 2, 0 => 3]
```
```php
Collection::from([3, 1, 2])
->sort(function ($v1, $v2, $k1, $k2) {
return $v1 < $v2;
})
->toArray(); //[2 => 2, 1 => 1, 0 => 3]
```
```php
toArray(sort([3, 1, 2], function ($a, $b) {return $a > $b;})); //[1 => 1, 2 => 2, 0 => 3]
```
#### splitAt(int $position) : Collection
Returns a collection of lazy collections: [take($position), drop($position)].
```php
Collection::from([1, 3, 3, 2])
->splitAt(2)
->toArray(); //[[1, 3], [2 => 3, 3 => 2]]
```
```php
toArray(splitAt([1, 3, 3, 2], 2)); //[[1, 3], [2 => 3, 3 => 2]]
```
#### splitWith(callable $function) : Collection
Returns a collection of lazy collections: [takeWhile($function(value, key)), dropWhile($function(value, key))].
```php
Collection::from([1, 3, 3, 2])
->splitWith(function ($value) {
return $value < 3;
})
->toArray(); //[[1], [1 => 3, 2 => 3, 3 => 2]]
```
```php
Collection::from([1, 3, 3, 2])
->splitWith(function ($value, $key) {
return $key < 2 && $value < 3;
})
->toArray(); //[[1], [1 => 3, 2 => 3, 3 => 2]]
```
```php
toArray(splitWith([1, 3, 3, 2], function ($value) {return $value < 3;})); //[[1], [1 => 3, 2 => 3, 3 => 2]]
```
#### sum() : int|float
Returns a sum of all values in this collection.
```php
Collection::from([1, 2, 3])->sum(); //6
Collection::from([1, 2, 3, 1.5])->sum(); //7.5
Collection::from([])->sum(); //0
```
```php
sum([1, 2, 3]); //6
```
#### take(int $numberOfItems) : Collection
A form of slice that returns first $numberOfItems items.
```php
Collection::from([1, 2, 3, 4, 5])
->take(2)
->toArray(); //[1, 2]
```
```php
toArray(take([1, 2, 3, 4, 5], 2)); //[1, 2]
```
#### takeNth(int $step) : Collection
Returns a lazy collection of every nth item in this collection
```php
Collection::from([1, 3, 3, 2])
->takeNth(2)
->toArray(); //[1, 2 => 3]
```
```php
toArray(takeNth([1, 3, 3, 2], 2)); //[1, 2 => 3]
```
#### takeWhile(callable $function) : Collection
Returns a lazy collection of items from the start of the collection until the first item for which $function(value, key) returns false.
```php
Collection::from([1, 3, 3, 2])
->takeWhile(function ($value) {
return $value < 3;
})
->toArray(); //[1]
```
```php
Collection::from([1, 3, 3, 2])
->takeWhile(function ($value, $key) {
return $key < 2 && $value < 3;
})
->toArray(); //[1]
```
```php
toArray(takeWhile([1, 3, 3, 2], function ($value) {return $value < 3;})); //[1]
```
#### transform(callable $transformer) : Collection
Uses a $transformer callable on itself that takes a Collection and returns Collection. This allows for creating a separate and reusable algorithms.
```php
$transformer = function (Collection $collection) {
return $collection
->filter(function ($item) {
return $item > 1;
})
->map('\DusanKasan\Knapsack\increment');
};
Collection::from([1, 3, 3, 2])
->transform($transformer)
->toArray(); //[4, 4, 3]
```
#### toArray() : array
Converts the collection to array recursively. Obviously this is not lazy since all the items must be realized. Calls iterator_to_array internaly.
```php
Collection::from([1, 3, 3, 2])->toArray(); //[1, 3, 3, 2]
```
```php
toArray([1, 3, 3, 2]); //[1, 3, 3, 2]
```
#### toString() : string
Returns a string by concatenating this collection's values into a string.
```php
Collection::from([1, 'a', 3, null])->toString(); //'1a3'
Collection::from([])->toString(); //''
```
```php
toString([1, 'a', 3, null]); //'1a3'
```
#### zip(array|Traversable[] ...$collections) : Collection
Returns a lazy collection of non-lazy collections of items from nth position from this collection and each passed collection. Stops when any of the collections don't have an item at the nth position.
```php
Collection::from([1, 2, 3])
->zip(['a' => 1, 'b' => 2, 'c' => 4])
->map('\DusanKasan\Knapsack\toArray')
->toArray(); //[[1, 'a' => 1], [1 => 2, 'b' => 2], [2 => 3, 'c' => 4]]
Collection::from([1, 2, 3])
->zip(['a' => 1, 'b' => 2])
->map('\DusanKasan\Knapsack\toArray')
->toArray(); //[[1, 'a' => 1], [1 => 2, 'b' => 2]]
```
```php
toArray(map(zip([1, 2, 3], ['a' => 1, 'b' => 2, 'c' => 4]), '\DusanKasan\Knapsack\toArray')); //[[1, 'a' => 1], [1 => 2, 'b' => 2], [2 => 3, 'c' => 4]]
```
## Utility functions
These are the functions bundled with Knapsack to make your life easier when transitioning into functional programming.
#### identity(mixed $value)
Returns $value
```php
$value === identity($value); //true
```
#### compare(mixed $a, mixed $b)
Default comparator function. Returns a negative number, zero, or a positive number when $a is logically 'less than', 'equal to', or 'greater than' $b.
```php
compare(1, 2); //-1
```
#### increment(int $value)
Returns value of $value incremented by one.
```php
increment(0) === 1; //true
```
#### decrement(int $value)
Returns value of $value decremented by one.
```php
decrement(2) === 1; //true
```
phpspec.yml 0000666 00000000262 13436746323 0006750 0 ustar 00 extensions:
- Cjm\PhpSpec\Extension\TypeHintedMethodsExtension
suites:
default:
spec_path: tests
namespace: DusanKasan\Knapsack
psr4_prefix: DusanKasan\Knapsack
src/utility_functions.php 0000666 00000001334 13436746323 0011657 0 ustar 00 $value) {
$arr[$key] = $value;
}
return $arr;
}
/**
* Returns a lazy collection of distinct items in $collection.
*
* @param array|Traversable $collection
* @return Collection
*/
function distinct($collection)
{
$generatorFactory = function () use ($collection) {
$distinctValues = [];
foreach ($collection as $key => $value) {
if (!in_array($value, $distinctValues)) {
$distinctValues[] = $value;
yield $key => $value;
}
}
};
return new Collection($generatorFactory);
}
/**
* Returns number of items in $collection.
*
* @param array|Traversable $collection
* @return int
*/
function size($collection)
{
$result = 0;
foreach ($collection as $value) {
$result++;
}
return $result;
}
/**
* Returns a non-lazy collection with items from $collection in reversed order.
*
* @param array|Traversable $collection
* @return Collection
*/
function reverse($collection)
{
$generatorFactory = function () use ($collection) {
$array = [];
foreach ($collection as $key => $value) {
$array[] = [$key, $value];
}
return map(
indexBy(
array_reverse($array),
function($item) {
return $item[0];
}
),
function($item) {
return $item[1];
}
);
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection of values from $collection (i.e. the keys are reset).
*
* @param array|Traversable $collection
* @return Collection
*/
function values($collection)
{
$generatorFactory = function () use ($collection) {
foreach ($collection as $value) {
yield $value;
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection of keys from $collection.
*
* @param array|Traversable $collection
* @return Collection
*/
function keys($collection)
{
$generatorFactory = function () use ($collection) {
foreach ($collection as $key => $value) {
yield $key;
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection of items from $collection repeated infinitely.
*
* @param array|Traversable $collection
* @return Collection
*/
function cycle($collection)
{
$generatorFactory = function () use ($collection) {
while (true) {
foreach ($collection as $key => $value) {
yield $key => $value;
}
}
};
return new Collection($generatorFactory);
}
/**
* Returns a non-lazy collection of shuffled items from $collection.
*
* @param array|Traversable $collection
* @return Collection
*/
function shuffle($collection)
{
$buffer = [];
foreach ($collection as $key => $value) {
$buffer[] = [$key, $value];
}
\shuffle($buffer);
return dereferenceKeyValue($buffer);
}
/**
* Returns true if $collection does not contain any items.
*
* @param array|Traversable $collection
* @return bool
*/
function isEmpty($collection)
{
foreach ($collection as $value) {
return false;
}
return true;
}
/**
* Returns true if $collection does contain any items.
*
* @param array|Traversable $collection
* @return bool
*/
function isNotEmpty($collection)
{
return !isEmpty($collection);
}
/**
* Returns a collection where keys are distinct values from $collection and values are number of occurrences of each
* value.
*
* @param array|Traversable $collection
* @return Collection
*/
function frequencies($collection)
{
return countBy($collection, '\DusanKasan\Knapsack\identity');
}
/**
* Returns the first item of $collection or throws ItemNotFound if #collection is empty.
*
* @param array|Traversable $collection
* @return mixed
*/
function first($collection)
{
return get(values($collection), 0);
}
/**
* Returns the last item of $collection or throws ItemNotFound if #collection is empty.
*
* @param array|Traversable $collection
* @return mixed
*/
function last($collection)
{
return first(reverse($collection));
}
/**
* Returns a lazy collection of items of $collection where value of each item is set to the return value of calling
* $function on its value and key.
*
* @param array|Traversable $collection
* @param callable $function ($value, $key)
* @return Collection
*/
function map($collection, callable $function)
{
$generatorFactory = function () use ($collection, $function) {
foreach ($collection as $key => $value) {
yield $key => $function($value, $key);
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection of items from $collection for which $function returns true.
*
* @param array|Traversable $collection
* @param callable|null $function ($value, $key)
* @return Collection
*/
function filter($collection, callable $function = null)
{
if (null === $function) {
$function = function ($value) {
return (bool)$value;
};
};
$generatorFactory = function () use ($collection, $function) {
foreach ($collection as $key => $value) {
if ($function($value, $key)) {
yield $key => $value;
}
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection with items from all $collections passed as argument appended together
*
* @param array|Traversable ...$collections
* @return Collection
*/
function concat(...$collections)
{
$generatorFactory = function () use ($collections) {
foreach ($collections as $collection) {
foreach ($collection as $key => $value) {
yield $key => $value;
}
}
};
return new Collection($generatorFactory);
}
/**
* Reduces the collection to single value by iterating over the collection and calling $reduction while
* passing $startValue and current key/item as parameters. The output of $function is used as $startValue in
* next iteration. The output of $function on last element is the return value of this function.
*
* @param array|Traversable $collection
* @param callable $function ($value, $key)
* @param mixed $startValue
* @return mixed
*/
function reduce($collection, callable $function, $startValue)
{
$tmp = duplicate($startValue);
foreach ($collection as $key => $value) {
$tmp = $function($tmp, $value, $key);
}
return $tmp;
}
/**
* Flattens multiple levels of nesting in collection. If $levelsToFlatten is not specified, flattens all levels of
* nesting.
*
* @param array|Traversable $collection
* @param int $levelsToFlatten -1 to flatten everything
* @return Collection
*/
function flatten($collection, $levelsToFlatten = -1)
{
$generatorFactory = function () use ($collection, $levelsToFlatten) {
$flattenNextLevel = $levelsToFlatten < 0 || $levelsToFlatten > 0;
$childLevelsToFlatten = $levelsToFlatten > 0 ? $levelsToFlatten - 1 : $levelsToFlatten;
foreach ($collection as $key => $value) {
if ($flattenNextLevel && (is_array($value) || $value instanceof Traversable)) {
foreach (flatten($value, $childLevelsToFlatten) as $childKey => $childValue) {
yield $childKey => $childValue;
}
} else {
yield $key => $value;
}
}
};
return new Collection($generatorFactory);
}
/**
* Returns a non-lazy collection sorted using $collection($item1, $item2, $key1, $key2 ). $collection should
* return true if first item is larger than the second and false otherwise.
*
* @param array|Traversable $collection
* @param callable $function ($value1, $value2, $key1, $key2)
* @return Collection
*/
function sort($collection, callable $function)
{
$array = iterator_to_array(
values(
map(
$collection,
function ($value, $key) {
return [$key, $value];
}
)
)
);
uasort(
$array,
function ($a, $b) use ($function) {
return $function($a[1], $b[1], $a[0], $b[0]);
}
);
return dereferenceKeyValue($array);
}
/**
* Returns a lazy collection that is a part of $collection starting from $from position and ending in $to position.
* If $to is not provided, the returned collection is contains all items from $from until end of $collection. All items
* before $from are iterated over, but not included in result.
*
* @param array|Traversable $collection
* @param int $from
* @param int $to -1 to slice until end
* @return Collection
*/
function slice($collection, $from, $to = -1)
{
$generatorFactory = function () use ($collection, $from, $to) {
$index = 0;
foreach ($collection as $key => $value) {
if ($index >= $from && ($index < $to || $to == -1)) {
yield $key => $value;
} elseif ($index >= $to && $to >= 0) {
break;
}
$index++;
}
};
return new Collection($generatorFactory);
}
/**
* Returns a non-lazy collection of items grouped by the result of $function.
*
* @param array|Traversable $collection
* @param callable $function ($value, $key)
* @return Collection
*/
function groupBy($collection, callable $function)
{
$result = [];
foreach ($collection as $key => $value) {
$newKey = $function($value, $key);
$group = isset($result[$newKey]) ? $result[$newKey] : new Collection([]);
$result[$newKey] = $group->append($value);
}
return Collection::from($result);
}
/**
* Returns a non-lazy collection of items grouped by the value at given key. Ignores non-collection items and items
* without the given keys
*
* @param array|Traversable $collection
* @param mixed $key
* @return Collection
*/
function groupByKey($collection, $key)
{
$generatorFactory = function () use ($collection, $key) {
return groupBy(
filter(
$collection,
function ($item) use ($key) {
return isCollection($item) && has($item, $key);
}
),
function($value) use ($key) {
return get($value, $key);
}
);
};
return new Collection($generatorFactory);
}
/**
* Executes $function for each item in $collection
*
* @param array|Traversable $collection
* @param callable $function ($value, $key)
* @return Collection
*/
function each($collection, callable $function)
{
$generatorFactory = function () use ($collection, $function) {
foreach ($collection as $key => $value) {
$function($value, $key);
yield $key => $value;
}
};
return new Collection($generatorFactory);
}
/**
* Returns an item with $key key from $collection. If that key is not present, throws ItemNotFound.
*
* @param array|Traversable $collection
* @param mixed $key
* @return mixed
*/
function get($collection, $key)
{
foreach ($collection as $valueKey => $value) {
if ($key === $valueKey) {
return $value;
}
}
throw new ItemNotFound;
}
/**
* Returns an item with $key key from $collection. If that key is not present, returns $default.
*
* @param array|Traversable $collection
* @param mixed $key
* @param mixed $default value returned if key is not found
* @return mixed
*/
function getOrDefault($collection, $key, $default)
{
try {
return get($collection, $key);
} catch (ItemNotFound $e) {
return $default;
}
}
/**
* Returns the first item from $collection for which $function returns true. If item like that is not present, returns
* $default.
*
* @param array|Traversable $collection
* @param callable $function ($value, $key)
* @param mixed $default
* @return mixed
*/
function find($collection, callable $function, $default = null)
{
foreach ($collection as $key => $value) {
if ($function($value, $key)) {
return $value;
}
}
return $default;
}
/**
* Returns a lazy collection by changing keys of $collection for each item to the result of $function for
* that item.
*
* @param array|Traversable $collection
* @param callable $function ($value, $key)
* @return Collection
*/
function indexBy($collection, callable $function)
{
$generatorFactory = function () use ($collection, $function) {
foreach ($collection as $key => $value) {
yield $function($value, $key) => $value;
}
};
return new Collection($generatorFactory);
}
/**
* Returns a non-lazy collection of items whose keys are the return values of $function and values are the number of
* items in this collection for which the $function returned this value.
*
* @param array|Traversable $collection
* @param callable $function ($value, $key)
* @return Collection
*/
function countBy($collection, callable $function)
{
return map(
groupBy($collection, $function),
'\DusanKasan\Knapsack\size'
);
}
/**
* Returns true if $function returns true for every item in $collection
*
* @param array|Traversable $collection
* @param callable $function ($value, $key)
* @return bool
*/
function every($collection, callable $function)
{
foreach ($collection as $key => $value) {
if (!$function($value, $key)) {
return false;
}
}
return true;
}
/**
* Returns true if $function returns true for at least one item in $collection.
*
* @param array|Traversable $collection
* @param callable $function ($value, $key)
* @return bool
*/
function some($collection, callable $function)
{
foreach ($collection as $key => $value) {
if ($function($value, $key)) {
return true;
}
}
return false;
}
/**
* Returns true if $needle is found in $collection values.
*
* @param $collection
* @param mixed $needle
* @return bool
*/
function contains($collection, $needle)
{
foreach ($collection as $key => $value) {
if ($value === $needle) {
return true;
}
}
return false;
}
/**
* Reduce that walks from right to the left.
*
* @param array|Traversable $collection
* @param callable $function
* @param mixed $startValue
* @return mixed
*/
function reduceRight($collection, callable $function, $startValue)
{
return reduce(reverse($collection), $function, $startValue);
}
/**
* Returns a lazy collection of first $numberOfItems items of $collection.
*
* @param array|Traversable $collection
* @param int $numberOfItems
* @return Collection
*/
function take($collection, $numberOfItems)
{
return slice($collection, 0, $numberOfItems);
}
/**
* Returns a lazy collection of all but first $numberOfItems items of $collection.
*
* @param array|Traversable $collection
* @param int $numberOfItems
* @return Collection
*/
function drop($collection, $numberOfItems)
{
return slice($collection, $numberOfItems);
}
/**
* Returns a lazy collection of values, where first value is $value and all subsequent values are computed by applying
* $function to the last value in the collection. By default this produces an infinite collection. However you can
* end the collection by throwing a NoMoreItems exception.
*
* @param mixed $value
* @param callable $function ($value, $key)
* @return Collection
*/
function iterate($value, callable $function)
{
$duplicated = duplicate($value);
$generatorFactory = function () use ($duplicated, $function) {
$value = $duplicated;
yield $value;
while (true) {
try {
$value = $function($value);
yield $value;
} catch (NoMoreItems $e) {
break;
}
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection of items from $collection for which $function returned true.
*
* @param array|Traversable $collection
* @param callable $function ($value, $key)
* @return Collection
*/
function reject($collection, callable $function)
{
return filter(
$collection,
function($value, $key) use ($function) {
return !$function($value, $key);
}
);
}
/**
* Returns a lazy collection of items in $collection without the last $numberOfItems items.
*
* @param array|Traversable $collection
* @param int $numberOfItems
* @return Collection
*/
function dropLast($collection, $numberOfItems = 1)
{
$generatorFactory = function () use ($collection, $numberOfItems) {
$buffer = [];
foreach ($collection as $key => $value) {
$buffer[] = [$key, $value];
if (count($buffer) > $numberOfItems) {
$val = array_shift($buffer);
yield $val[0] => $val[1];
}
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection of items from $collection separated by $separator.
*
* @param array|Traversable $collection
* @param mixed $separator
* @return Collection
*/
function interpose($collection, $separator)
{
$generatorFactory = function () use ($collection, $separator) {
foreach (take($collection, 1) as $key => $value) {
yield $key => $value;
}
foreach (drop($collection, 1) as $key => $value) {
yield $separator;
yield $key => $value;
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection of first item from first collection, first item from second, second from first and
* so on. Accepts any number of collections.
*
* @param array|Traversable ...$collections
* @return Collection
*/
function interleave(...$collections)
{
$generatorFactory = function () use ($collections) {
$normalizedCollection = array_map(
function ($collection) {
$traversable = new Collection($collection);
$traversable->rewind();
return $traversable;
},
$collections
);
do {
$valid = false;
foreach ($normalizedCollection as $collection) {
if ($collection->valid()) {
yield $collection->key() => $collection->current();
$collection->next();
$valid = true;
}
}
} while ($valid);
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection of items in $collection with $value added as first element. If $key is not provided
* it will be next integer index.
*
* @param array|Traversable $collection
* @param mixed $value
* @param mixed|null $key
* @return Collection
*/
function prepend($collection, $value, $key = null)
{
$generatorFactory = function () use ($collection, $value, $key) {
if ($key === null) {
yield $value;
} else {
yield $key => $value;
}
foreach ($collection as $key => $value) {
yield $key => $value;
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection of items in $collection with $value added as last element. If $key is not provided
* it will be next integer index.
*
* @param array|Traversable $collection
* @param mixed $value
* @param mixed|null $key
* @return Collection
*/
function append($collection, $value, $key = null)
{
$generatorFactory = function () use ($collection, $value, $key) {
foreach ($collection as $k => $v) {
yield $k => $v;
}
if ($key === null) {
yield $value;
} else {
yield $key => $value;
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection by removing items from $collection until first item for which $function returns false.
*
* @param array|Traversable $collection
* @param callable $function ($value, $key)
* @return Collection
*/
function dropWhile($collection, callable $function)
{
$generatorFactory = function () use ($collection, $function) {
$shouldDrop = true;
foreach ($collection as $key => $value) {
if ($shouldDrop) {
$shouldDrop = $function($value, $key);
}
if (!$shouldDrop) {
yield $key => $value;
}
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection of items from $collection until first item for which $function returns false.
*
* @param array|Traversable $collection
* @param callable $function ($value, $key)
* @return Collection
*/
function takeWhile($collection, callable $function)
{
$generatorFactory = function () use ($collection, $function) {
$shouldTake = true;
foreach ($collection as $key => $value) {
if ($shouldTake) {
$shouldTake = $function($value, $key);
}
if ($shouldTake) {
yield $key => $value;
}
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection. A result of calling map and flatten(1)
*
* @param array|Traversable $collection
* @param callable $function ($value, $key)
* @return Collection
*/
function mapcat($collection, callable $function)
{
return flatten(map($collection, $function), 1);
}
/**
* Returns a lazy collection [take($collection, $position), drop($collection, $position)]
*
* @param array|Traversable $collection
* @param int $position
* @return Collection
*/
function splitAt($collection, $position)
{
$generatorFactory = function () use ($collection, $position) {
yield take($collection, $position);
yield drop($collection, $position);
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection [takeWhile($collection, $function), dropWhile($collection, $function)]
*
* @param array|Traversable $collection
* @param callable $function ($value, $key)
* @return Collection
*/
function splitWith($collection, callable $function)
{
$generatorFactory = function () use ($collection, $function) {
yield takeWhile($collection, $function);
yield dropWhile($collection, $function);
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection with items from $collection but values that are found in keys of $replacementMap
* are replaced by their values.
*
* @param array|Traversable $collection
* @param array|Traversable $replacementMap
* @return Collection
*/
function replace($collection, $replacementMap)
{
$generatorFactory = function () use ($collection, $replacementMap) {
foreach ($collection as $key => $value) {
$newValue = getOrDefault($replacementMap, $value, $value);
yield $key => $newValue;
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection of reduction steps.
*
* @param array|Traversable $collection
* @param callable $function
* @param mixed $startValue
* @return Collection
*/
function reductions($collection, callable $function, $startValue)
{
$generatorFactory = function () use ($collection, $function, $startValue) {
$tmp = duplicate($startValue);
yield $tmp;
foreach ($collection as $key => $value) {
$tmp = $function($tmp, $value, $key);
yield $tmp;
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection of every nth ($step) item in $collection.
*
* @param array|Traversable $collection
* @param int $step
* @return Collection
*/
function takeNth($collection, $step)
{
$generatorFactory = function () use ($collection, $step) {
$index = 0;
foreach ($collection as $key => $value) {
if ($index % $step == 0) {
yield $key => $value;
}
$index++;
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection of collections of $numberOfItems items each, at $step step
* apart. If $step is not supplied, defaults to $numberOfItems, i.e. the partitions
* do not overlap. If a $padding collection is supplied, use its elements as
* necessary to complete last partition up to $numberOfItems items. In case there are
* not enough padding elements, return a partition with less than $numberOfItems items.
*
* @param array|Traversable $collection
* @param $numberOfItems
* @param int $step
* @param array|Traversable $padding
* @return Collection
*/
function partition($collection, $numberOfItems, $step = -1, $padding = [])
{
$generatorFactory = function () use ($collection, $numberOfItems, $step, $padding) {
$buffer = [];
$itemsToSkip = 0;
$tmpStep = $step ?: $numberOfItems;
foreach ($collection as $key => $value) {
if (count($buffer) == $numberOfItems) {
yield dereferenceKeyValue($buffer);
$buffer = array_slice($buffer, $tmpStep);
$itemsToSkip = $tmpStep - $numberOfItems;
}
if ($itemsToSkip <= 0) {
$buffer[] = [$key, $value];
} else {
$itemsToSkip--;
}
}
yield take(
concat(dereferenceKeyValue($buffer), $padding),
$numberOfItems
);
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection created by partitioning $collection each time $function returned a different value.
*
* @param array|Traversable $collection
* @param callable $function
* @return Collection
*/
function partitionBy($collection, callable $function)
{
$generatorFactory = function () use ($collection, $function) {
$result = null;
$buffer = [];
foreach ($collection as $key => $value) {
$newResult = $function($value, $key);
if (!empty($buffer) && $result != $newResult) {
yield dereferenceKeyValue($buffer);
$buffer = [];
}
$result = $newResult;
$buffer[] = [$key, $value];
}
if (!empty($buffer)) {
yield dereferenceKeyValue($buffer);
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection of $value repeated $times times. If $times is not provided the collection is infinite.
*
* @param mixed $value
* @param int $times
* @return Collection
*/
function repeat($value, $times = -1)
{
$generatorFactory = function () use ($value, $times) {
$tmpTimes = $times;
while ($tmpTimes != 0) {
yield $value;
$tmpTimes = $tmpTimes < 0 ? -1 : $tmpTimes - 1;
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection of numbers starting at $start, incremented by $step until $end is reached.
*
* @param int $start
* @param int|null $end
* @param int $step
* @return Collection
*/
function range($start = 0, $end = null, $step = 1)
{
$generatorFactory = function () use ($start, $end, $step) {
return iterate(
$start,
function($value) use ($step, $end) {
$result = $value + $step;
if ($end !== null && $result > $end) {
throw new NoMoreItems;
}
return $result;
}
);
};
return new Collection($generatorFactory);
}
/**
* Returns true if $input is array or Traversable object.
*
* @param mixed $input
* @return bool
*/
function isCollection($input)
{
return is_array($input) || $input instanceof Traversable;
}
/**
* Returns duplicated/cloned $input that has no relation to the original one. Used for making sure there are no side
* effect in functions.
*
* @param $input
* @return mixed
*/
function duplicate($input)
{
if (is_array($input)) {
return toArray(
map(
$input,
function($i) {
return duplicate($i);
}
)
);
} elseif (is_object($input)) {
return clone $input;
} else {
return $input;
}
}
/**
* Transforms [[$key, $value], [$key2, $value2]] into [$key => $value, $key2 => $value2]. Used as a helper
*
* @param array|Traversable $collection
* @return Collection
*/
function dereferenceKeyValue($collection)
{
$generatorFactory = function () use ($collection) {
foreach ($collection as $value) {
yield $value[0] => $value[1];
}
};
return new Collection($generatorFactory);
}
/**
* Realizes collection - turns lazy collection into non-lazy one by iterating over it and storing the key/values.
*
* @param array|Traversable $collection
* @return Collection
*/
function realize($collection)
{
return dereferenceKeyValue(
toArray(
map(
$collection,
function ($value, $key) {
return [$key, $value];
}
)
)
);
}
/**
* Returns the second item of $collection or throws ItemNotFound if $collection is empty or has 1 item.
*
* @param array|Traversable $collection
* @return mixed
*/
function second($collection)
{
return get(values($collection), 1);
}
/**
* Combines $keys and $values into a lazy collection. The resulting collection has length equal to the size of smaller
* argument.
*
* @param array|Traversable $keys
* @param array|Traversable $values
* @return Collection
*/
function combine($keys, $values)
{
$generatorFactory = function () use ($keys, $values) {
$keyCollection = new Collection($keys);
$valueCollection = new Collection($values);
$valueCollection->rewind();
foreach ($keyCollection as $key) {
if (!$valueCollection->valid()) {
break;
}
yield $key => $valueCollection->current();
$valueCollection->next();
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection without the items associated to any of the keys from $keys.
*
* @param array|Traversable $collection
* @param array|Traversable $keys
* @return Collection
*/
function except($collection, $keys)
{
$keys = toArray(values($keys));
return reject(
$collection,
function ($value, $key) use ($keys) {
return in_array($key, $keys);
}
);
}
/**
* Returns a lazy collection of items associated to any of the keys from $keys.
*
* @param array|Traversable $collection
* @param array|Traversable $keys
* @return Collection
*/
function only($collection, $keys)
{
$keys = toArray(values($keys));
return filter(
$collection,
function ($value, $key) use ($keys) {
return in_array($key, $keys);
}
);
}
/**
* Returns a lazy collection of items that are in $collection but are not in any of the other arguments. Note that the
* ...$collections are iterated non-lazily.
*
* @param array|Traversable $collection
* @param array|Traversable ...$collections
* @return Collection
*/
function diff($collection, ...$collections)
{
$valuesToCompare = toArray(values(concat(...$collections)));
$generatorFactory = function () use ($collection, $valuesToCompare) {
foreach ($collection as $key => $value) {
if (!in_array($value, $valuesToCompare)) {
yield $key => $value;
}
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection of items that are in $collection and all the other arguments, indexed by the keys from the
* first collection. Note that the ...$collections are iterated non-lazily.
*
* @param array|Traversable $collection
* @param array|Traversable ...$collections
* @return Collection
*/
function intersect($collection, ...$collections)
{
$valuesToCompare = toArray(values(concat(...$collections)));
$generatorFactory = function () use ($collection, $valuesToCompare) {
foreach ($collection as $key => $value) {
if (in_array($value, $valuesToCompare)) {
yield $key => $value;
}
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection where keys and values are flipped.
*
* @param array|Traversable $collection
* @return Collection
*/
function flip($collection)
{
$generatorFactory = function () use ($collection) {
foreach ($collection as $key => $value) {
yield $value => $key;
}
};
return new Collection($generatorFactory);
}
/**
* Checks for the existence of an item with key $key in $collection.
*
* @param array|Traversable $collection
* @param mixed $key
* @return bool
*/
function has($collection, $key)
{
try {
get($collection, $key);
return true;
} catch (ItemNotFound $e) {
return false;
}
}
/**
* Returns a lazy collection of non-lazy collections of items from nth position from each passed collection. Stops when
* any of the collections don't have an item at the nth position.
*
* @param array|Traversable[] ...$collections
* @return Collection
*/
function zip(...$collections)
{
$normalizedCollections = [];
foreach ($collections as $collection) {
$traversable = new Collection($collection);
$traversable->rewind();
$normalizedCollections[] = $traversable;
}
$generatorFactory = function () use ($normalizedCollections) {
while (true) {
$isMissingItems = false;
$zippedItem = new Collection([]);
foreach ($normalizedCollections as $collection) {
if (!$collection->valid()) {
$isMissingItems = true;
break;
}
$zippedItem = append($zippedItem, $collection->current(), $collection->key());
$collection->next();
}
if (!$isMissingItems) {
yield $zippedItem;
} else {
break;
}
}
};
return new Collection($generatorFactory);
}
/**
* Returns a lazy collection of data extracted from $collection items by dot separated key path. Supports the *
* wildcard. If a key contains \ or * it must be escaped using \ character.
*
* @param array|Traversable $collection
* @param mixed $keyPath
* @return Collection
*/
function extract($collection, $keyPath)
{
preg_match_all('/(.*[^\\\])(?:\.|$)/U', $keyPath, $matches);
$pathParts = $matches[1];
$extractor = function ($coll) use ($pathParts) {
foreach ($pathParts as $pathPart) {
$coll = flatten(filter($coll, '\DusanKasan\Knapsack\isCollection'), 1);
if ($pathPart != '*') {
$pathPart = str_replace(['\.', '\*'], ['.', '*'], $pathPart);
$coll = values(only($coll, [$pathPart]));
}
}
return $coll;
};
$generatorFactory = function () use ($collection, $extractor) {
foreach ($collection as $item) {
foreach ($extractor([$item]) as $extracted) {
yield $extracted;
}
}
};
return new Collection($generatorFactory);
}
/**
* Checks whether $collection has exactly $size items.
*
* @param array|Traversable $collection
* @param int $size
* @return bool
*/
function sizeIs($collection, $size)
{
$itemsTempCount = 0;
foreach ($collection as $key => $value) {
$itemsTempCount++;
if ($itemsTempCount > $size) {
return false;
}
}
return $itemsTempCount == $size;
}
/**
* Checks whether $collection has less than $size items.
*
* @param array|Traversable $collection
* @param int $size
* @return bool
*/
function sizeIsLessThan($collection, $size)
{
$itemsTempCount = 0;
foreach ($collection as $key => $value) {
$itemsTempCount++;
if ($itemsTempCount > $size) {
return false;
}
}
return $itemsTempCount < $size;
}
/**
* Checks whether $collection has more than $size items.
*
* @param array|Traversable $collection
* @param int $size
* @return bool
*/
function sizeIsGreaterThan($collection, $size)
{
$itemsTempCount = 0;
foreach ($collection as $key => $value) {
$itemsTempCount++;
if ($itemsTempCount > $size) {
return true;
}
}
return $itemsTempCount > $size;
}
/**
* Checks whether $collection has between $fromSize to $toSize items. $toSize can be
* smaller than $fromSize.
*
* @param array|Traversable $collection
* @param int $fromSize
* @param int $toSize
* @return bool
*/
function sizeIsBetween($collection, $fromSize, $toSize)
{
if ($fromSize > $toSize) {
$tmp = $toSize;
$toSize = $fromSize;
$fromSize = $tmp;
}
$itemsTempCount = 0;
foreach ($collection as $key => $value) {
$itemsTempCount++;
if ($itemsTempCount > $toSize) {
return false;
}
}
return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
}
/**
* Returns a sum of all values in the $collection.
*
* @param array|Traversable $collection
* @return int|float
*/
function sum($collection)
{
$result = 0;
foreach ($collection as $value) {
$result += $value;
}
return $result;
}
/**
* Returns average of values from $collection.
*
* @param array|Traversable $collection
* @return int|float
*/
function average($collection)
{
$sum = 0;
$count = 0;
foreach ($collection as $value) {
$sum += $value;
$count++;
}
return $count ? $sum/$count : 0;
}
/**
* Returns maximal value from $collection.
*
* @param array|Traversable $collection
* @return mixed
*/
function max($collection)
{
$result = null;
foreach ($collection as $value) {
$result = $value > $result ? $value : $result;
}
return $result;
}
/**
* Returns minimal value from $collection.
*
* @param array|Traversable $collection
* @return mixed
*/
function min($collection)
{
$result = null;
$hasItem = false;
foreach ($collection as $value) {
if (!$hasItem) {
$hasItem = true;
$result = $value;
}
$result = $value < $result ? $value : $result;
}
return $result;
}
/**
* Returns a string by concatenating the $collection values into a string.
*
* @param array|Traversable $collection
* @return string
*/
function toString($collection)
{
$result = '';
foreach ($collection as $value) {
$result .= (string) $value;
}
return $result;
}
/**
* Returns a lazy collection with items from $collection, but items with keys that are found in keys of $replacementMap
* are replaced by their values.
*
* @param array|Traversable $collection
* @param array|Traversable $replacementMap
* @return Collection
*/
function replaceByKeys($collection, $replacementMap)
{
$generatorFactory = function () use ($collection, $replacementMap) {
foreach ($collection as $key => $value) {
$newValue = getOrDefault($replacementMap, $key, $value);
yield $key => $newValue;
}
};
return new Collection($generatorFactory);
}
src/Collection.php 0000666 00000007240 13436746323 0010161 0 ustar 00 inputFactory = $input;
$this->input = $input();
} elseif (is_array($input)) {
$input = new RecursiveArrayIterator($input);
$this->input = $input;
} elseif ($input instanceof IteratorAggregate) {
$input = $input->getIterator();
$this->input = $input;
} elseif ($input instanceof Iterator) {
$this->input = $input;
} else {
throw new InvalidArgument;
}
}
/**
* Static alias of normal constructor.
*
* @param array|Traversable $input
* @return Collection
*/
public static function from($input)
{
return new self($input);
}
/**
* Returns lazy collection of values, where first value is $input and all subsequent values are computed by applying
* $function to the last value in the collection. By default this produces an infinite collection. However you can
* end the collection by throwing a NoMoreItems exception.
*
* @param mixed $input
* @param callable $function
* @return Collection
*/
public static function iterate($input, callable $function)
{
return iterate($input, $function);
}
/**
* Returns a lazy collection of $value repeated $times times. If $times is not provided the collection is infinite.
*
* @param mixed $value
* @param int $times
* @return Collection
*/
public static function repeat($value, $times = -1)
{
return repeat($value, $times);
}
/**
* Returns a lazy collection of numbers starting at $start, incremented by $step until $end is reached.
*
* @param int $start
* @param int|null $end
* @param int $step
* @return Collection
*/
public static function range($start = 0, $end = null, $step = 1)
{
return \DusanKasan\Knapsack\range($start, $end, $step);
}
/**
* @inheritdoc
*/
public function current()
{
return $this->input->current();
}
/**
* {@inheritdoc}
*/
public function next()
{
$this->input->next();
}
/**
* {@inheritdoc}
*/
public function key()
{
return $this->input->key();
}
/**
* {@inheritdoc}
*/
public function valid()
{
return $this->input->valid();
}
/**
* {@inheritdoc}
*/
public function rewind()
{
if ($this->inputFactory) {
$this->input = call_user_func($this->inputFactory);
}
$this->input->rewind();
}
/**
* {@inheritdoc}
*/
public function serialize()
{
return serialize(
toArray(
map(
$this->input,
function ($value, $key) {
return [$key, $value];
}
)
)
);
}
/**
* {@inheritdoc}
*/
public function unserialize($serialized)
{
$this->input = dereferenceKeyValue(unserialize($serialized));
}
}
src/CollectionTrait.php 0000666 00000061101 13436746323 0011161 0 ustar 00 getItems());
}
/**
* Returns a lazy collection of items for which $function returned true.
*
* @param callable|null $function ($value, $key)
* @return Collection
*/
public function filter(callable $function = null)
{
return filter($this->getItems(), $function);
}
/**
* Returns a lazy collection of distinct items. The comparison is the same as in in_array.
*
* @return Collection
*/
public function distinct()
{
return distinct($this->getItems());
}
/**
* Returns a lazy collection with items from all $collections passed as argument appended together
*
* @param \Traversable[]|array ...$collections
* @return Collection
*/
public function concat(...$collections)
{
return concat($this, ...$collections);
}
/**
* Returns collection where each item is changed to the output of executing $function on each key/item.
*
* @param callable $function
* @return Collection
*/
public function map(callable $function)
{
return map($this->getItems(), $function);
}
/**
* Reduces the collection to single value by iterating over the collection and calling $function while
* passing $startValue and current key/item as parameters. The output of $function is used as $startValue in
* next iteration. The output of $function on last element is the return value of this function. If
* $convertToCollection is true and the return value is a collection (array|Traversable) an instance of Collection
* is returned.
*
* @param callable $function ($tmpValue, $value, $key)
* @param mixed $startValue
* @param bool $convertToCollection
* @return mixed|Collection
*/
public function reduce(callable $function, $startValue, $convertToCollection = false)
{
$result = reduce($this->getItems(), $function, $startValue);
return (isCollection($result) && $convertToCollection) ? new Collection($result) : $result;
}
/**
* Returns a lazy collection with one or multiple levels of nesting flattened. Removes all nesting when no value
* is passed.
*
* @param int $depth How many levels should be flatten, default (-1) is infinite.
* @return Collection
*/
public function flatten($depth = -1)
{
return flatten($this->getItems(), $depth);
}
/**
* Returns a non-lazy collection sorted using $function($item1, $item2, $key1, $key2 ). $function should
* return true if first item is larger than the second and false otherwise.
*
* @param callable $function ($value1, $value2, $key1, $key2)
* @return Collection
*/
public function sort(callable $function)
{
return \DusanKasan\Knapsack\sort($this->getItems(), $function);
}
/**
* Returns lazy collection items of which are part of the original collection from item number $from to item
* number $to. The items before $from are also iterated over, just not returned.
*
* @param int $from
* @param int $to If omitted, will slice until end
* @return Collection
*/
public function slice($from, $to = -1)
{
return slice($this->getItems(), $from, $to);
}
/**
* Returns collection which items are separated into groups indexed by the return value of $function.
*
* @param callable $function ($value, $key)
* @return Collection
*/
public function groupBy(callable $function)
{
return groupBy($this->getItems(), $function);
}
/**
* Returns collection where items are separated into groups indexed by the value at given key.
*
* @param $key
* @return Collection
*/
public function groupByKey($key)
{
return groupByKey($this->getItems(), $key);
}
/**
* Returns a lazy collection in which $function is executed for each item.
*
* @param callable $function ($value, $key)
* @return Collection
*/
public function each(callable $function)
{
return \DusanKasan\Knapsack\each($this->getItems(), $function);
}
/**
* Returns the number of items in this collection.
*
* @return int
*/
public function size()
{
return size($this->getItems());
}
/**
* Returns value at the key $key. If multiple values have this key, return first. If no value has this key, throw
* ItemNotFound. If $convertToCollection is true and the return value is a collection (array|Traversable an
* instance of Collection will be returned.
*
* @param mixed $key
* @param bool $convertToCollection
* @return Collection|mixed
* @throws \DusanKasan\Knapsack\Exceptions\ItemNotFound
*/
public function get($key, $convertToCollection = false)
{
$result = get($this->getItems(), $key);
return (isCollection($result) && $convertToCollection) ? new Collection($result) : $result;
}
/**
* Returns item at the key $key. If multiple items have this key, return first. If no item has this key, return
* $ifNotFound. If no value has this key, throw ItemNotFound. If $convertToCollection is true and the return value
* is a collection (array|Traversable) an instance of Collection will be returned.
*
* @param mixed $key
* @param mixed $default
* @param bool $convertToCollection
* @return mixed|Collection
* @throws \DusanKasan\Knapsack\Exceptions\ItemNotFound
*/
public function getOrDefault($key, $default = null, $convertToCollection = false)
{
$result = getOrDefault($this->getItems(), $key, $default);
return (isCollection($result) && $convertToCollection) ? new Collection($result) : $result;
}
/**
* Returns first value matched by $function. If no value matches, return $default. If $convertToCollection is true
* and the return value is a collection (array|Traversable an instance of Collection will be returned.
*
* @param callable $function
* @param mixed|null $default
* @param bool $convertToCollection
* @return Collection|mixed
*/
public function find(callable $function, $default = null, $convertToCollection = false)
{
$result = find($this->getItems(), $function, $default);
return (isCollection($result) && $convertToCollection) ? new Collection($result) : $result;
}
/**
* Returns a non-lazy collection of items whose keys are the return values of $function and values are the number of
* items in this collection for which the $function returned this value.
*
* @param callable $function
* @return Collection
*/
public function countBy(callable $function)
{
return countBy($this->getItems(), $function);
}
/**
* Returns a lazy collection by changing keys of this collection for each item to the result of $function for
* that item.
*
* @param callable $function
* @return Collection
*/
public function indexBy(callable $function)
{
return indexBy($this->getItems(), $function);
}
/**
* Returns true if $function returns true for every item in this collection, false otherwise.
*
* @param callable $function
* @return bool
*/
public function every(callable $function)
{
return every($this->getItems(), $function);
}
/**
* Returns true if $function returns true for at least one item in this collection, false otherwise.
*
* @param callable $function
* @return bool
*/
public function some(callable $function)
{
return some($this->getItems(), $function);
}
/**
* Returns true if $value is present in the collection.
*
* @param mixed $value
* @return bool
*/
public function contains($value)
{
return contains($this->getItems(), $value);
}
/**
* Returns collection of items in this collection in reverse order.
*
* @return Collection
*/
public function reverse()
{
return reverse($this->getItems());
}
/**
* Reduce the collection to single value. Walks from right to left.
*
* @param callable $function Must take 2 arguments, intermediate value and item from the iterator. If
* $convertToCollection is true and the return value is a collection (array|Traversable) an instance of Collection
* is returned.
* @param mixed $startValue
* @param bool $convertToCollection
* @return mixed|Collection
*/
public function reduceRight(callable $function, $startValue, $convertToCollection = false)
{
$result = reduceRight($this->getItems(), $function, $startValue);
return (isCollection($result) && $convertToCollection) ? new Collection($result) : $result;
}
/**
* A form of slice that returns first $numberOfItems items.
*
* @param int $numberOfItems
* @return Collection
*/
public function take($numberOfItems)
{
return take($this->getItems(), $numberOfItems);
}
/**
* A form of slice that returns all but first $numberOfItems items.
*
* @param int $numberOfItems
* @return Collection
*/
public function drop($numberOfItems)
{
return drop($this->getItems(), $numberOfItems);
}
/**
* Returns collection of values from this collection but with keys being numerical from 0 upwards.
*
* @return Collection
*/
public function values()
{
return values($this->getItems());
}
/**
* Returns a lazy collection without elements matched by $function.
*
* @param callable $function
* @return Collection
*/
public function reject(callable $function)
{
return reject($this->getItems(), $function);
}
/**
* Returns a lazy collection of the keys of this collection.
*
* @return Collection
*/
public function keys()
{
return keys($this->getItems());
}
/**
* Returns a lazy collection of items of this collection separated by $separator
*
* @param mixed $separator
* @return Collection
*/
public function interpose($separator)
{
return interpose($this->getItems(), $separator);
}
/**
* Returns a lazy collection with last $numberOfItems items skipped. These are still iterated over, just skipped.
*
* @param int $numberOfItems
* @return Collection
*/
public function dropLast($numberOfItems = 1)
{
return dropLast($this->getItems(), $numberOfItems);
}
/**
* Returns a lazy collection of first item from first collection, first item from second, second from first and
* so on. Accepts any number of collections.
*
* @param array|\Traversable ...$collections
* @return Collection
*/
public function interleave(...$collections)
{
return interleave($this->getItems(), ...$collections);
}
/**
* Returns an infinite lazy collection of items in this collection repeated infinitely.
*
* @return Collection
*/
public function cycle()
{
return cycle($this->getItems());
}
/**
* Returns a lazy collection of items of this collection with $value added as first element. If $key is not provided
* it will be next integer index.
*
* @param mixed $value
* @param mixed|null $key
* @return Collection
*/
public function prepend($value, $key = null)
{
return prepend($this->getItems(), $value, $key);
}
/**
* Returns a lazy collection of items of this collection with $value added as last element. If $key is not provided
* it will be next integer index.
*
* @param mixed $value
* @param mixed $key
* @return Collection
*/
public function append($value, $key = null)
{
return append($this->getItems(), $value, $key);
}
/**
* Returns a lazy collection by removing items from this collection until first item for which $function returns
* false.
*
* @param callable $function
* @return Collection
*/
public function dropWhile(callable $function)
{
return dropWhile($this->getItems(), $function);
}
/**
* Returns a lazy collection which is a result of calling map($function) and then flatten(1)
*
* @param callable $function
* @return Collection
*/
public function mapcat(callable $function)
{
return mapcat($this->getItems(), $function);
}
/**
* Returns a lazy collection of items from the start of the ollection until the first item for which $function
* returns false.
*
* @param callable $function
* @return Collection
*/
public function takeWhile(callable $function)
{
return takeWhile($this->getItems(), $function);
}
/**
* Returns a collection of [take($position), drop($position)]
*
* @param int $position
* @return Collection
*/
public function splitAt($position)
{
return splitAt($this->getItems(), $position);
}
/**
* Returns a collection of [takeWhile($predicament), dropWhile($predicament]
*
* @param callable $function
* @return Collection
*/
public function splitWith(callable $function)
{
return splitWith($this->getItems(), $function);
}
/**
* Returns a lazy collection with items from this collection but values that are found in keys of $replacementMap
* are replaced by their values.
*
* @param \Traversable|array $replacementMap
* @return Collection
*/
public function replace($replacementMap)
{
return replace($this->getItems(), $replacementMap);
}
/**
* Returns a lazy collection of reduction steps.
*
* @param callable $function
* @param mixed $startValue
* @return Collection
*/
public function reductions(callable $function, $startValue)
{
return reductions($this->getItems(), $function, $startValue);
}
/**
* Returns a lazy collection of every nth item in this collection
*
* @param int $step
* @return Collection
*/
public function takeNth($step)
{
return takeNth($this->getItems(), $step);
}
/**
* Returns a non-collection of shuffled items from this collection
*
* @return Collection
*/
public function shuffle()
{
return \DusanKasan\Knapsack\shuffle($this->getItems());
}
/**
* Returns a lazy collection of collections of $numberOfItems items each, at $step step
* apart. If $step is not supplied, defaults to $numberOfItems, i.e. the partitions
* do not overlap. If a $padding collection is supplied, use its elements as
* necessary to complete last partition up to $numberOfItems items. In case there are
* not enough padding elements, return a partition with less than $numberOfItems items.
*
* @param int $numberOfItems
* @param int $step
* @param array|\Traversable $padding
* @return Collection
*/
public function partition($numberOfItems, $step = 0, $padding = [])
{
return partition($this->getItems(), $numberOfItems, $step, $padding);
}
/**
* Creates a lazy collection of collections created by partitioning this collection every time $function will
* return different result.
*
* @param callable $function
* @return Collection
*/
public function partitionBy(callable $function)
{
return partitionBy($this->getItems(), $function);
}
/**
* Returns true if this collection is empty. False otherwise.
*
* @return bool
*/
public function isEmpty()
{
return isEmpty($this->getItems());
}
/**
* Opposite of isEmpty.
*
* @return bool
*/
public function isNotEmpty()
{
return isNotEmpty($this->getItems());
}
/**
* Returns a collection where keys are distinct items from this collection and their values are number of
* occurrences of each value.
*
* @return Collection
*/
public function frequencies()
{
return frequencies($this->getItems());
}
/**
* Returns first item of this collection. If the collection is empty, throws ItemNotFound. If $convertToCollection
* is true and the return value is a collection (array|Traversable an instance of Collection is returned.
*
* @param bool $convertToCollection
* @return mixed|Collection
* @throws \DusanKasan\Knapsack\Exceptions\ItemNotFound
*/
public function first($convertToCollection = false)
{
$result = first($this->getItems());
return (isCollection($result) && $convertToCollection) ? new Collection($result) : $result;
}
/**
* Returns last item of this collection. If the collection is empty, throws ItemNotFound. If $convertToCollection
* is true and the return value is a collection (array|Traversable) it is converted to Collection.
*
* @param bool $convertToCollection
* @return mixed|Collection
* @throws \DusanKasan\Knapsack\Exceptions\ItemNotFound
*/
public function last($convertToCollection = false)
{
$result = last($this->getItems());
return (isCollection($result) && $convertToCollection) ? new Collection($result) : $result;
}
/**
* Realizes collection - turns lazy collection into non-lazy one by iterating over it and storing the key/values.
*
* @return Collection
*/
public function realize()
{
return realize($this->getItems());
}
/**
* Returns the second item in this collection or throws ItemNotFound if the collection is empty or has 1 item. If
* $convertToCollection is true and the return value is a collection (array|Traversable) it is converted to
* Collection.
*
* @param bool $convertToCollection
* @return Collection|mixed
* @throws \DusanKasan\Knapsack\Exceptions\ItemNotFound
*/
public function second($convertToCollection = false)
{
$result = second($this->getItems());
return (isCollection($result) && $convertToCollection) ? new Collection($result) : $result;
}
/**
* Combines the values of this collection as keys, with values of $collection as values. The resulting collection
* has length equal to the size of smaller collection.
*
* @param array|\Traversable $collection
* @return Collection
* @throws \DusanKasan\Knapsack\Exceptions\ItemNotFound
*/
public function combine($collection)
{
return combine($this->getItems(), $collection);
}
/**
* Returns a lazy collection without the items associated to any of the keys from $keys.
*
* @param array|\Traversable $keys
* @return Collection
*/
public function except($keys)
{
return except($this->getItems(), $keys);
}
/**
* Returns a lazy collection of items associated to any of the keys from $keys.
*
* @param array|\Traversable $keys
* @return Collection
*/
public function only($keys)
{
return only($this->getItems(), $keys);
}
/**
* Returns a lazy collection of items that are in $this but are not in any of the other arguments. Note that the
* ...$collections are iterated non-lazily.
*
* @param array|\Traversable ...$collections
* @return Collection
*/
public function diff(...$collections)
{
return diff($this->getItems(), ...$collections);
}
/**
* Returns a lazy collection where keys and values are flipped.
*
* @return Collection
*/
public function flip()
{
return flip($this->getItems());
}
/**
* Checks for the existence of an item with$key in this collection.
*
* @param mixed $key
* @return bool
*/
public function has($key)
{
return has($this->getItems(), $key);
}
/**
* Returns a lazy collection of non-lazy collections of items from nth position from this collection and each
* passed collection. Stops when any of the collections don't have an item at the nth position.
*
* @param array|\Traversable[] ...$collections
* @return Collection
*/
public function zip(...$collections)
{
array_unshift($collections, $this->getItems());
return zip(...$collections);
}
/**
* Uses a $transformer callable that takes a Collection and returns Collection on itself.
*
* @param callable $transformer Collection => Collection
* @return Collection
* @throws InvalidReturnValue
*/
public function transform(callable $transformer)
{
$items = $this->getItems();
$transformed = $transformer($items instanceof Collection ? $items : new Collection($items));
if (!($transformed instanceof Collection)) {
throw new InvalidReturnValue;
}
return $transformed;
}
/**
* Extracts data from collection items by dot separated key path. Supports the * wildcard. If a key contains \ or
* it must be escaped using \ character.
*
* @param mixed $keyPath
* @return Collection
*/
public function extract($keyPath)
{
return \DusanKasan\Knapsack\extract($this->getItems(), $keyPath);
}
/**
* Returns a lazy collection of items that are in $collection and all the other arguments, indexed by the keys from
* the first collection. Note that the ...$collections are iterated non-lazily.
*
* @param array|\Traversable[] ...$collections
* @return Collection
*/
public function intersect(...$collections)
{
return intersect($this->getItems(), ...$collections);
}
/**
* Checks whether this collection has exactly $size items.
*
* @param int $size
* @return bool
*/
public function sizeIs($size)
{
return sizeIs($this->getItems(), $size);
}
/**
* Checks whether this collection has less than $size items.
*
* @param int $size
* @return bool
*/
public function sizeIsLessThan($size)
{
return sizeIsLessThan($this->getItems(), $size);
}
/**
* Checks whether this collection has more than $size items.
*
* @param int $size
* @return bool
*/
public function sizeIsGreaterThan($size)
{
return sizeIsGreaterThan($this->getItems(), $size);
}
/**
* Checks whether this collection has between $fromSize to $toSize items. $toSize can be
* smaller than $fromSize.
*
* @param int $fromSize
* @param int $toSize
* @return bool
*/
public function sizeIsBetween($fromSize, $toSize)
{
return sizeIsBetween($this->getItems(), $fromSize, $toSize);
}
/**
* Returns a sum of all values in this collection.
*
* @return int|float
*/
public function sum()
{
return sum($this->getItems());
}
/**
* Returns average of values from this collection.
*
* @return int|float
*/
public function average()
{
return average($this->getItems());
}
/**
* Returns maximal value from this collection.
*
* @return mixed
*/
public function max()
{
return \DusanKasan\Knapsack\max($this->getItems());
}
/**
* Returns minimal value from this collection.
*
* @return mixed
*/
public function min()
{
return \DusanKasan\Knapsack\min($this->getItems());
}
/**
* Returns a string by concatenating the collection values into a string.
*
* @return string
*/
public function toString()
{
return toString($this->getItems());
}
/**
* Returns a lazy collection with items from $collection, but items with keys that are found in keys of
* $replacementMap are replaced by their values.
*
* @param array|\Traversable $replacementMap
* @return Collection
*/
public function replaceByKeys($replacementMap)
{
return replaceByKeys($this->getItems(), $replacementMap);
}
/**
* @return array|\Traversable
*/
protected function getItems()
{
return $this;
}
}
.sensiolabs.yml 0000666 00000000103 13436746323 0007520 0 ustar 00 rules:
php.interface_has_no_interface_suffix:
enabled: false
LICENSE.md 0000666 00000002066 13436746323 0006173 0 ustar 00 The MIT License (MIT)
Copyright (c) 2015 Dusan Kasan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
composer.json 0000666 00000001704 13436746323 0007307 0 ustar 00 {
"name": "dusank/knapsack",
"license": "MIT",
"type": "library",
"description": "Collection library for PHP",
"keywords": ["collections","sequences", "map", "reduce"],
"homepage": "https://github.com/DusanKasan/Knapsack",
"authors": [
{
"name": "Dusan Kasan",
"email": "dusan@kasan.sk",
"homepage": "http://kasan.sk",
"role": "Developer"
}
],
"require-dev": {
"phpspec/phpspec": "^2.0",
"henrikbjorn/phpspec-code-coverage": "^2.0",
"squizlabs/php_codesniffer": "^2.0",
"phpmd/phpmd" : "^2.0",
"ciaranmcnulty/phpspec-typehintedmethods": "^1.0",
"phpunit/phpunit": "^5.0",
"symfony/console": "^2.7"
},
"require": {
"php": ">=5.6.0"
},
"autoload": {
"files": [
"src/collection_functions.php",
"src/utility_functions.php"
],
"psr-4": {
"DusanKasan\\Knapsack\\": "src/",
"DusanKasan\\Knapsack\\Tests\\Helpers\\": "tests/helpers/"
}
}
}
phpunit.xml.dist 0000666 00000000335 13436746323 0007737 0 ustar 00
tests/scenarios
composer.lock 0000666 00000211332 13436746323 0007266 0 ustar 00 {
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "5a472b69cfe241e239c8710434928ae8",
"content-hash": "c247f88d7997ca5b4321e30b4c926e8f",
"packages": [],
"packages-dev": [
{
"name": "ciaranmcnulty/phpspec-typehintedmethods",
"version": "1.1.3",
"source": {
"type": "git",
"url": "https://github.com/ciaranmcnulty/phpspec-typehintedmethods.git",
"reference": "b2979c4f3ad45ec3ad7c6b3d7f84e912427736e1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ciaranmcnulty/phpspec-typehintedmethods/zipball/b2979c4f3ad45ec3ad7c6b3d7f84e912427736e1",
"reference": "b2979c4f3ad45ec3ad7c6b3d7f84e912427736e1",
"shasum": ""
},
"require": {
"phpspec/phpspec": "~2.0,<=2.5"
},
"require-dev": {
"behat/behat": "~3.0",
"symfony/filesystem": "~2.3"
},
"type": "library",
"autoload": {
"psr-4": {
"Cjm\\PhpSpec\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ciaran McNulty",
"email": "mail@ciaranmcnulty.com",
"homepage": "http://ciaranmcnulty.com",
"role": "Developer"
}
],
"description": "Extension for phpspec to enhance generated methods",
"homepage": "http://github.com/ciaranmcnulty/phpspec-typehintedmethods",
"keywords": [
"BDD",
"TDD",
"phpspec"
],
"time": "2016-04-19 16:37:01"
},
{
"name": "doctrine/instantiator",
"version": "1.0.5",
"source": {
"type": "git",
"url": "https://github.com/doctrine/instantiator.git",
"reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
"reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
"shasum": ""
},
"require": {
"php": ">=5.3,<8.0-DEV"
},
"require-dev": {
"athletic/athletic": "~0.1.8",
"ext-pdo": "*",
"ext-phar": "*",
"phpunit/phpunit": "~4.0",
"squizlabs/php_codesniffer": "~2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com",
"homepage": "http://ocramius.github.com/"
}
],
"description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
"homepage": "https://github.com/doctrine/instantiator",
"keywords": [
"constructor",
"instantiate"
],
"time": "2015-06-14 21:17:01"
},
{
"name": "henrikbjorn/phpspec-code-coverage",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/henrikbjorn/PhpSpecCodeCoverageExtension.git",
"reference": "528a0c69a524f8acba5f66bc59ae8dc9bc409045"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/henrikbjorn/PhpSpecCodeCoverageExtension/zipball/528a0c69a524f8acba5f66bc59ae8dc9bc409045",
"reference": "528a0c69a524f8acba5f66bc59ae8dc9bc409045",
"shasum": ""
},
"require": {
"php": "^5.3.3|^5.4|^5.5|^5.6|^7.0",
"phpspec/phpspec": "^2.0",
"phpunit/php-code-coverage": "^2.2.4|^3"
},
"require-dev": {
"bossa/phpspec2-expect": "^1.0"
},
"suggest": {
"ext-xdebug": "To allow coverage generation when not using a recent version of phpdbg"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"PhpSpec\\Extension\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Integrates CodeCoverage with PhpSpec",
"time": "2016-05-05 18:25:12"
},
{
"name": "myclabs/deep-copy",
"version": "1.5.1",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "a8773992b362b58498eed24bf85005f363c34771"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/a8773992b362b58498eed24bf85005f363c34771",
"reference": "a8773992b362b58498eed24bf85005f363c34771",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"doctrine/collections": "1.*",
"phpunit/phpunit": "~4.1"
},
"type": "library",
"autoload": {
"psr-4": {
"DeepCopy\\": "src/DeepCopy/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Create deep copies (clones) of your objects",
"homepage": "https://github.com/myclabs/DeepCopy",
"keywords": [
"clone",
"copy",
"duplicate",
"object",
"object graph"
],
"time": "2015-11-20 12:04:31"
},
{
"name": "pdepend/pdepend",
"version": "2.2.4",
"source": {
"type": "git",
"url": "https://github.com/pdepend/pdepend.git",
"reference": "b086687f3a01dc6bb92d633aef071d2c5dd0db06"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pdepend/pdepend/zipball/b086687f3a01dc6bb92d633aef071d2c5dd0db06",
"reference": "b086687f3a01dc6bb92d633aef071d2c5dd0db06",
"shasum": ""
},
"require": {
"php": ">=5.3.7",
"symfony/config": "^2.3.0|^3",
"symfony/dependency-injection": "^2.3.0|^3",
"symfony/filesystem": "^2.3.0|^3"
},
"require-dev": {
"phpunit/phpunit": "^4.4.0,<4.8",
"squizlabs/php_codesniffer": "^2.0.0"
},
"bin": [
"src/bin/pdepend"
],
"type": "library",
"autoload": {
"psr-4": {
"PDepend\\": "src/main/php/PDepend"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "Official version of pdepend to be handled with Composer",
"time": "2016-03-10 15:15:04"
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8",
"reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"suggest": {
"dflydev/markdown": "~1.0",
"erusev/parsedown": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-0": {
"phpDocumentor": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mike van Riel",
"email": "mike.vanriel@naenius.com"
}
],
"time": "2015-02-03 12:10:50"
},
{
"name": "phpmd/phpmd",
"version": "2.4.3",
"source": {
"type": "git",
"url": "https://github.com/phpmd/phpmd.git",
"reference": "2b9c2417a18696dfb578b38c116cd0ddc19b256e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpmd/phpmd/zipball/2b9c2417a18696dfb578b38c116cd0ddc19b256e",
"reference": "2b9c2417a18696dfb578b38c116cd0ddc19b256e",
"shasum": ""
},
"require": {
"pdepend/pdepend": "^2.0.4",
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0",
"squizlabs/php_codesniffer": "^2.0"
},
"bin": [
"src/bin/phpmd"
],
"type": "project",
"autoload": {
"psr-0": {
"PHPMD\\": "src/main/php"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Manuel Pichler",
"email": "github@manuel-pichler.de",
"homepage": "https://github.com/manuelpichler",
"role": "Project Founder"
},
{
"name": "Other contributors",
"homepage": "https://github.com/phpmd/phpmd/graphs/contributors",
"role": "Contributors"
},
{
"name": "Marc Würth",
"email": "ravage@bluewin.ch",
"homepage": "https://github.com/ravage84",
"role": "Project Maintainer"
}
],
"description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.",
"homepage": "http://phpmd.org/",
"keywords": [
"mess detection",
"mess detector",
"pdepend",
"phpmd",
"pmd"
],
"time": "2016-04-04 11:52:04"
},
{
"name": "phpspec/php-diff",
"version": "v1.0.2",
"source": {
"type": "git",
"url": "https://github.com/phpspec/php-diff.git",
"reference": "30e103d19519fe678ae64a60d77884ef3d71b28a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/php-diff/zipball/30e103d19519fe678ae64a60d77884ef3d71b28a",
"reference": "30e103d19519fe678ae64a60d77884ef3d71b28a",
"shasum": ""
},
"type": "library",
"autoload": {
"psr-0": {
"Diff": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Chris Boulton",
"homepage": "http://github.com/chrisboulton",
"role": "Original developer"
}
],
"description": "A comprehensive library for generating differences between two hashable objects (strings or arrays).",
"time": "2013-11-01 13:02:21"
},
{
"name": "phpspec/phpspec",
"version": "2.5.0",
"source": {
"type": "git",
"url": "https://github.com/phpspec/phpspec.git",
"reference": "385ecb015e97c13818074f1517928b24d4a26067"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/phpspec/zipball/385ecb015e97c13818074f1517928b24d4a26067",
"reference": "385ecb015e97c13818074f1517928b24d4a26067",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.1",
"ext-tokenizer": "*",
"php": ">=5.3.3",
"phpspec/php-diff": "~1.0.0",
"phpspec/prophecy": "~1.4",
"sebastian/exporter": "~1.0",
"symfony/console": "~2.3|~3.0",
"symfony/event-dispatcher": "~2.1|~3.0",
"symfony/finder": "~2.1|~3.0",
"symfony/process": "^2.6|~3.0",
"symfony/yaml": "~2.1|~3.0"
},
"require-dev": {
"behat/behat": "^3.0.11",
"bossa/phpspec2-expect": "~1.0",
"phpunit/phpunit": "~4.4",
"symfony/filesystem": "~2.1|~3.0"
},
"suggest": {
"phpspec/nyan-formatters": "~1.0 – Adds Nyan formatters"
},
"bin": [
"bin/phpspec"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.2.x-dev"
}
},
"autoload": {
"psr-0": {
"PhpSpec": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Konstantin Kudryashov",
"email": "ever.zet@gmail.com",
"homepage": "http://everzet.com"
},
{
"name": "Marcello Duarte",
"homepage": "http://marcelloduarte.net/"
}
],
"description": "Specification-oriented BDD framework for PHP 5.3+",
"homepage": "http://phpspec.net/",
"keywords": [
"BDD",
"SpecBDD",
"TDD",
"spec",
"specification",
"testing",
"tests"
],
"time": "2016-03-20 20:34:32"
},
{
"name": "phpspec/prophecy",
"version": "v1.6.0",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "3c91bdf81797d725b14cb62906f9a4ce44235972"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/3c91bdf81797d725b14cb62906f9a4ce44235972",
"reference": "3c91bdf81797d725b14cb62906f9a4ce44235972",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": "^5.3|^7.0",
"phpdocumentor/reflection-docblock": "~2.0",
"sebastian/comparator": "~1.1",
"sebastian/recursion-context": "~1.0"
},
"require-dev": {
"phpspec/phpspec": "~2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.5.x-dev"
}
},
"autoload": {
"psr-0": {
"Prophecy\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Konstantin Kudryashov",
"email": "ever.zet@gmail.com",
"homepage": "http://everzet.com"
},
{
"name": "Marcello Duarte",
"email": "marcello.duarte@gmail.com"
}
],
"description": "Highly opinionated mocking framework for PHP 5.3+",
"homepage": "https://github.com/phpspec/prophecy",
"keywords": [
"Double",
"Dummy",
"fake",
"mock",
"spy",
"stub"
],
"time": "2016-02-15 07:46:21"
},
{
"name": "phpunit/php-code-coverage",
"version": "3.3.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "44cd8e3930e431658d1a5de7d282d5cb37837fd5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/44cd8e3930e431658d1a5de7d282d5cb37837fd5",
"reference": "44cd8e3930e431658d1a5de7d282d5cb37837fd5",
"shasum": ""
},
"require": {
"php": "^5.6 || ^7.0",
"phpunit/php-file-iterator": "~1.3",
"phpunit/php-text-template": "~1.2",
"phpunit/php-token-stream": "^1.4.2",
"sebastian/code-unit-reverse-lookup": "~1.0",
"sebastian/environment": "^1.3.2",
"sebastian/version": "~1.0|~2.0"
},
"require-dev": {
"ext-xdebug": ">=2.1.4",
"phpunit/phpunit": "~5"
},
"suggest": {
"ext-dom": "*",
"ext-xdebug": ">=2.4.0",
"ext-xmlwriter": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.3.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
"homepage": "https://github.com/sebastianbergmann/php-code-coverage",
"keywords": [
"coverage",
"testing",
"xunit"
],
"time": "2016-05-27 16:24:29"
},
{
"name": "phpunit/php-file-iterator",
"version": "1.4.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
"reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "FilterIterator implementation that filters files based on a list of suffixes.",
"homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
"keywords": [
"filesystem",
"iterator"
],
"time": "2015-06-21 13:08:43"
},
{
"name": "phpunit/php-text-template",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-text-template.git",
"reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
"reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Simple template engine.",
"homepage": "https://github.com/sebastianbergmann/php-text-template/",
"keywords": [
"template"
],
"time": "2015-06-21 13:50:34"
},
{
"name": "phpunit/php-timer",
"version": "1.0.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-timer.git",
"reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260",
"reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4|~5"
},
"type": "library",
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Utility class for timing",
"homepage": "https://github.com/sebastianbergmann/php-timer/",
"keywords": [
"timer"
],
"time": "2016-05-12 18:03:57"
},
{
"name": "phpunit/php-token-stream",
"version": "1.4.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
"reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da",
"reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Wrapper around PHP's tokenizer extension.",
"homepage": "https://github.com/sebastianbergmann/php-token-stream/",
"keywords": [
"tokenizer"
],
"time": "2015-09-15 10:49:45"
},
{
"name": "phpunit/phpunit",
"version": "5.3.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "00dd95ffb48805503817ced06399017df315fe5c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/00dd95ffb48805503817ced06399017df315fe5c",
"reference": "00dd95ffb48805503817ced06399017df315fe5c",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-json": "*",
"ext-pcre": "*",
"ext-reflection": "*",
"ext-spl": "*",
"myclabs/deep-copy": "~1.3",
"php": "^5.6 || ^7.0",
"phpspec/prophecy": "^1.3.1",
"phpunit/php-code-coverage": "^3.3.0",
"phpunit/php-file-iterator": "~1.4",
"phpunit/php-text-template": "~1.2",
"phpunit/php-timer": "^1.0.6",
"phpunit/phpunit-mock-objects": "^3.1",
"sebastian/comparator": "~1.1",
"sebastian/diff": "~1.2",
"sebastian/environment": "~1.3",
"sebastian/exporter": "~1.2",
"sebastian/global-state": "~1.0",
"sebastian/object-enumerator": "~1.0",
"sebastian/resource-operations": "~1.0",
"sebastian/version": "~1.0|~2.0",
"symfony/yaml": "~2.1|~3.0"
},
"suggest": {
"phpunit/php-invoker": "~1.1"
},
"bin": [
"phpunit"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.3.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "The PHP Unit Testing framework.",
"homepage": "https://phpunit.de/",
"keywords": [
"phpunit",
"testing",
"xunit"
],
"time": "2016-05-11 13:28:45"
},
{
"name": "phpunit/phpunit-mock-objects",
"version": "3.2.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
"reference": "314f8c44019b4dfece2571b98938574e6342be59"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/314f8c44019b4dfece2571b98938574e6342be59",
"reference": "314f8c44019b4dfece2571b98938574e6342be59",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": "^5.6 || ^7.0",
"phpunit/php-text-template": "^1.2",
"sebastian/exporter": "^1.2"
},
"require-dev": {
"phpunit/phpunit": "^5.4"
},
"suggest": {
"ext-soap": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Mock Object library for PHPUnit",
"homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
"keywords": [
"mock",
"xunit"
],
"time": "2016-06-03 05:01:30"
},
{
"name": "sebastian/code-unit-reverse-lookup",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
"reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/c36f5e7cfce482fde5bf8d10d41a53591e0198fe",
"reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe",
"shasum": ""
},
"require": {
"php": ">=5.6"
},
"require-dev": {
"phpunit/phpunit": "~5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Looks up which function or method a line of code belongs to",
"homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
"time": "2016-02-13 06:45:14"
},
{
"name": "sebastian/comparator",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "937efb279bd37a375bcadf584dec0726f84dbf22"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22",
"reference": "937efb279bd37a375bcadf584dec0726f84dbf22",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"sebastian/diff": "~1.2",
"sebastian/exporter": "~1.2"
},
"require-dev": {
"phpunit/phpunit": "~4.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Volker Dusch",
"email": "github@wallbash.com"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@2bepublished.at"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Provides the functionality to compare PHP values for equality",
"homepage": "http://www.github.com/sebastianbergmann/comparator",
"keywords": [
"comparator",
"compare",
"equality"
],
"time": "2015-07-26 15:48:44"
},
{
"name": "sebastian/diff",
"version": "1.4.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "13edfd8706462032c2f52b4b862974dd46b71c9e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e",
"reference": "13edfd8706462032c2f52b4b862974dd46b71c9e",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.8"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Kore Nordmann",
"email": "mail@kore-nordmann.de"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Diff implementation",
"homepage": "https://github.com/sebastianbergmann/diff",
"keywords": [
"diff"
],
"time": "2015-12-08 07:14:41"
},
{
"name": "sebastian/environment",
"version": "1.3.7",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
"reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/4e8f0da10ac5802913afc151413bc8c53b6c2716",
"reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Provides functionality to handle HHVM/PHP environments",
"homepage": "http://www.github.com/sebastianbergmann/environment",
"keywords": [
"Xdebug",
"environment",
"hhvm"
],
"time": "2016-05-17 03:18:57"
},
{
"name": "sebastian/exporter",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "7ae5513327cb536431847bcc0c10edba2701064e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e",
"reference": "7ae5513327cb536431847bcc0c10edba2701064e",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"sebastian/recursion-context": "~1.0"
},
"require-dev": {
"phpunit/phpunit": "~4.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Volker Dusch",
"email": "github@wallbash.com"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@2bepublished.at"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Adam Harvey",
"email": "aharvey@php.net"
}
],
"description": "Provides the functionality to export PHP variables for visualization",
"homepage": "http://www.github.com/sebastianbergmann/exporter",
"keywords": [
"export",
"exporter"
],
"time": "2015-06-21 07:55:53"
},
{
"name": "sebastian/global-state",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
"reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4",
"reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.2"
},
"suggest": {
"ext-uopz": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Snapshotting of global state",
"homepage": "http://www.github.com/sebastianbergmann/global-state",
"keywords": [
"global state"
],
"time": "2015-10-12 03:26:01"
},
{
"name": "sebastian/object-enumerator",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-enumerator.git",
"reference": "d4ca2fb70344987502567bc50081c03e6192fb26"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/d4ca2fb70344987502567bc50081c03e6192fb26",
"reference": "d4ca2fb70344987502567bc50081c03e6192fb26",
"shasum": ""
},
"require": {
"php": ">=5.6",
"sebastian/recursion-context": "~1.0"
},
"require-dev": {
"phpunit/phpunit": "~5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Traverses array structures and object graphs to enumerate all referenced objects",
"homepage": "https://github.com/sebastianbergmann/object-enumerator/",
"time": "2016-01-28 13:25:10"
},
{
"name": "sebastian/recursion-context",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
"reference": "913401df809e99e4f47b27cdd781f4a258d58791"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791",
"reference": "913401df809e99e4f47b27cdd781f4a258d58791",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Adam Harvey",
"email": "aharvey@php.net"
}
],
"description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
"time": "2015-11-11 19:50:13"
},
{
"name": "sebastian/resource-operations",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/resource-operations.git",
"reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
"reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
"shasum": ""
},
"require": {
"php": ">=5.6.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Provides a list of PHP built-in functions that operate on resources",
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
"time": "2015-07-28 20:34:47"
},
{
"name": "sebastian/version",
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/version.git",
"reference": "c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5",
"reference": "c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5",
"shasum": ""
},
"require": {
"php": ">=5.6"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
"time": "2016-02-04 12:56:52"
},
{
"name": "squizlabs/php_codesniffer",
"version": "2.6.1",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
"reference": "fb72ed32f8418db5e7770be1653e62e0d6f5dd3d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/fb72ed32f8418db5e7770be1653e62e0d6f5dd3d",
"reference": "fb72ed32f8418db5e7770be1653e62e0d6f5dd3d",
"shasum": ""
},
"require": {
"ext-simplexml": "*",
"ext-tokenizer": "*",
"ext-xmlwriter": "*",
"php": ">=5.1.2"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"bin": [
"scripts/phpcs",
"scripts/phpcbf"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
}
},
"autoload": {
"classmap": [
"CodeSniffer.php",
"CodeSniffer/CLI.php",
"CodeSniffer/Exception.php",
"CodeSniffer/File.php",
"CodeSniffer/Fixer.php",
"CodeSniffer/Report.php",
"CodeSniffer/Reporting.php",
"CodeSniffer/Sniff.php",
"CodeSniffer/Tokens.php",
"CodeSniffer/Reports/",
"CodeSniffer/Tokenizers/",
"CodeSniffer/DocGenerators/",
"CodeSniffer/Standards/AbstractPatternSniff.php",
"CodeSniffer/Standards/AbstractScopeSniff.php",
"CodeSniffer/Standards/AbstractVariableSniff.php",
"CodeSniffer/Standards/IncorrectPatternException.php",
"CodeSniffer/Standards/Generic/Sniffs/",
"CodeSniffer/Standards/MySource/Sniffs/",
"CodeSniffer/Standards/PEAR/Sniffs/",
"CodeSniffer/Standards/PSR1/Sniffs/",
"CodeSniffer/Standards/PSR2/Sniffs/",
"CodeSniffer/Standards/Squiz/Sniffs/",
"CodeSniffer/Standards/Zend/Sniffs/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Greg Sherwood",
"role": "lead"
}
],
"description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
"homepage": "http://www.squizlabs.com/php-codesniffer",
"keywords": [
"phpcs",
"standards"
],
"time": "2016-05-30 22:24:32"
},
{
"name": "symfony/config",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/config.git",
"reference": "048dc47e07f92333203c3b7045868bbc864fc40e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/config/zipball/048dc47e07f92333203c3b7045868bbc864fc40e",
"reference": "048dc47e07f92333203c3b7045868bbc864fc40e",
"shasum": ""
},
"require": {
"php": ">=5.5.9",
"symfony/filesystem": "~2.8|~3.0"
},
"suggest": {
"symfony/yaml": "To use the yaml reference dumper"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Config\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Config Component",
"homepage": "https://symfony.com",
"time": "2016-05-20 11:48:17"
},
{
"name": "symfony/console",
"version": "v2.8.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "48221d3de4dc22d2cd57c97e8b9361821da86609"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/48221d3de4dc22d2cd57c97e8b9361821da86609",
"reference": "48221d3de4dc22d2cd57c97e8b9361821da86609",
"shasum": ""
},
"require": {
"php": ">=5.3.9",
"symfony/polyfill-mbstring": "~1.0"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/event-dispatcher": "~2.1|~3.0.0",
"symfony/process": "~2.1|~3.0.0"
},
"suggest": {
"psr/log": "For using the console logger",
"symfony/event-dispatcher": "",
"symfony/process": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Console\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"time": "2016-04-26 12:00:47"
},
{
"name": "symfony/dependency-injection",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/dependency-injection.git",
"reference": "383110341e8f47ae972da3a29503b099831549e1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/383110341e8f47ae972da3a29503b099831549e1",
"reference": "383110341e8f47ae972da3a29503b099831549e1",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"require-dev": {
"symfony/config": "~2.8|~3.0",
"symfony/expression-language": "~2.8|~3.0",
"symfony/yaml": "~2.8|~3.0"
},
"suggest": {
"symfony/config": "",
"symfony/expression-language": "For using expressions in service container configuration",
"symfony/proxy-manager-bridge": "Generate service proxies to lazy load them",
"symfony/yaml": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\DependencyInjection\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony DependencyInjection Component",
"homepage": "https://symfony.com",
"time": "2016-05-24 10:06:56"
},
{
"name": "symfony/event-dispatcher",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "0343b2cedd0edb26cdc791212a8eb645c406018b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0343b2cedd0edb26cdc791212a8eb645c406018b",
"reference": "0343b2cedd0edb26cdc791212a8eb645c406018b",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/config": "~2.8|~3.0",
"symfony/dependency-injection": "~2.8|~3.0",
"symfony/expression-language": "~2.8|~3.0",
"symfony/stopwatch": "~2.8|~3.0"
},
"suggest": {
"symfony/dependency-injection": "",
"symfony/http-kernel": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\EventDispatcher\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
"time": "2016-04-12 18:27:47"
},
{
"name": "symfony/filesystem",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "5751e80d6f94b7c018f338a4a7be0b700d6f3058"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/5751e80d6f94b7c018f338a4a7be0b700d6f3058",
"reference": "5751e80d6f94b7c018f338a4a7be0b700d6f3058",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Filesystem\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
"time": "2016-04-12 18:27:47"
},
{
"name": "symfony/finder",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "40d17ed287bf51a2f884c4619ce8ff2a1c5cd219"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/40d17ed287bf51a2f884c4619ce8ff2a1c5cd219",
"reference": "40d17ed287bf51a2f884c4619ce8ff2a1c5cd219",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Finder\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
"time": "2016-05-13 18:06:41"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "dff51f72b0706335131b00a7f49606168c582594"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/dff51f72b0706335131b00a7f49606168c582594",
"reference": "dff51f72b0706335131b00a7f49606168c582594",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"time": "2016-05-18 14:26:46"
},
{
"name": "symfony/process",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "1574f3451b40fa9bbae518ef71d19a56f409cac0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/1574f3451b40fa9bbae518ef71d19a56f409cac0",
"reference": "1574f3451b40fa9bbae518ef71d19a56f409cac0",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Process\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2016-04-12 19:11:33"
},
{
"name": "symfony/yaml",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "eca51b7b65eb9be6af88ad7cc91685f1556f5c9a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/eca51b7b65eb9be6af88ad7cc91685f1556f5c9a",
"reference": "eca51b7b65eb9be6af88ad7cc91685f1556f5c9a",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Yaml\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2016-05-26 21:46:24"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=5.6.0"
},
"platform-dev": []
}
CHANGELOG.md 0000666 00000007246 13436746323 0006405 0 ustar 00 # Changelog
##0.2.0
- First release
- Every operation is represented by different Collection class
##0.3.0
- **Total overhaul and move to more functional design**
- Collection class now uses Generator functions under the hood.
- The functions are also accessible allowing for functional programming.
- Performance improved 2x.
##0.4.0
- More utility functions added
##0.4.1
- Missing utility functions added to readme
##0.4.2
- Ditching PHP 5.5. support
- Variadics introduced to some functions
##0.4.3
- No longer rewinds wrapped collection in constructor
##1.0.0
- The project is ready for production use. No known bugs exist.
##2.0.0
- Project moved to new global namespace DusanKasan (whole namespace is DusanKasan\Knapsack) to avoid conflicts.
- Collection::realize was introduced to force materialization of the collection (turning lazy collection into non-lazy).
- Collection::concat and Collection::interleave are now variadic.
- **Breaking change: toArray and Collection::toArray now behave more logicaly and do not convert items recursively.**
##3.0.0
- Automatic conversion of return values to Collections is no longer happening if you do not explicitly require it. Details in documentation.
##3.1.0
- CollectionTrait has been introduced and its usage documented in readme.
- New functions added:
- second() // seems useless but really usefull :)
- combine($values) // uses current collection as keys, $values as values
- except($keys) // rejects every item with key found in $keys
- only($keys) // filters only items with key found in $keys
- difference(...$collections) // filters items that are in the original collection but not in any of $collections
- flip() // flips keys with values
- has($key) // checks for the existence of item with $key key
- A handful of bugfixes also:
- Collection constructor might have experienced conflicts between callable (in array form) and array arguments
- Pluck might have failed on heterogenous collections. Now ignores non-collection items.
##4.0.0
- GroupByKey function introduced
- Serialization support added
- Changelog added
- **Breaking change: combine now throws NonEqualCollectionLength**
##5.0.0
- Zip function added
- Extract function added
- Transform function added
- **Breaking change: combine now stops when it runs out of keys or values**
- **Breaking change: pluck removed (replaced by extract)**
##6.0.0
- Intersect function added
- Average utility function added
- Concatenate utility function added
- Reduce/reduceRight/second now have the returnAsCollection flag
- **Breaking change: getNth removed (to solve ambiguity with takeNth)**
- **Breaking change: difference renamed to diff**
##6.1.0
- Filter can be called without arguments and it will remove falsy values
##6.2.0
- sizeIsExactly function added
- sizeIsGreaterThan function added
- sizeIsLessThan function added
- sizeIsBetween function added
##7.0.0
- The functionality of sum, average, min, max and concatenate moved into collection.
- Sum collection function added
- Average collection function added
- Min collection function added
- Max collection function added
- ToString collection function added
- **Breaking change: sum utility function removed**
- **Breaking change: average utility function removed**
- **Breaking change: min utility function removed**
- **Breaking change: max utility function removed**
- **Breaking change: concatenate utility function removed**
##8.0.0
- **Breaking change: sum function will return integer by default, float if there are float type elements**
- **Breaking change: average function will not force return float and will return integer if the sum/count result is integer**
##8.1.0
- ReplaceByKeys function added
tests/helpers/PlusOneAdder.php 0000666 00000000345 13436746323 0012427 0 ustar 00 map($mapper);
foreach ($mappedCollection as $item) {
}
$collectionMapDeltas += microtime(true) - $collectionMapStart;
}
return [
'name' => 'array_map vs Collection::map on ' . NUMBER_OF_ITEMS . ' integers (addition)',
'native' => (float) $arrayMapDeltas / REPEAT_COUNT,
'collection' => (float) $collectionMapDeltas / REPEAT_COUNT
];
}
function getStringReport()
{
$arrayMapDeltas = 0.0;
$collectionMapDeltas = 0.0;
$fixtureProvider = function () {
$array = [];
for ($i = 0; $i < NUMBER_OF_ITEMS; $i++) {
$array[] = $i . 'asd';
}
return $array;
};
$mapper = function ($item) {
return $item . 'qwe';
};
for ($j = 0; $j < REPEAT_COUNT; $j++) {
$array = $fixtureProvider();
$arrayMapStart = microtime(true);
$mappedArray = array_map($mapper, $array);
foreach ($mappedArray as $item) {
}
$arrayMapDeltas += microtime(true) - $arrayMapStart;
$array = $fixtureProvider();
$collection = new Collection($array);
$collectionMapStart = microtime(true);
$mappedCollection = $collection->map($mapper);
foreach ($mappedCollection as $item) {
}
$collectionMapDeltas += microtime(true) - $collectionMapStart;
}
return [
'name' => 'array_map vs Collection::map on ' . NUMBER_OF_ITEMS . ' strings (concatenation)',
'native' => (float) $arrayMapDeltas / REPEAT_COUNT,
'collection' => (float) $collectionMapDeltas / REPEAT_COUNT
];
}
function getObjectReport()
{
$arrayMapDeltas = 0.0;
$collectionMapDeltas = 0.0;
$fixtureProvider = function () {
$array = [];
for ($i = 0; $i < NUMBER_OF_ITEMS; $i++) {
$c = new stdClass();
$c->asd = 1;
$array[] = $c;
}
return $array;
};
$mapper = function ($item) {
return $item->asd;
};
for ($j = 0; $j < REPEAT_COUNT; $j++) {
$array = $fixtureProvider();
$arrayMapStart = microtime(true);
$mappedArray = array_map($mapper, $array);
foreach ($mappedArray as $item) {
}
$arrayMapDeltas += microtime(true) - $arrayMapStart;
$array = $fixtureProvider();
$collection = new Collection($array);
$collectionMapStart = microtime(true);
$mappedCollection = $collection->map($mapper);
foreach ($mappedCollection as $item) {
}
$collectionMapDeltas += microtime(true) - $collectionMapStart;
}
return [
'name' => 'array_map vs Collection::map on ' . NUMBER_OF_ITEMS . ' objects (object to field value)',
'native' => (float) $arrayMapDeltas / REPEAT_COUNT,
'collection' => (float) $collectionMapDeltas / REPEAT_COUNT
];
}
function getComplexOperationReport()
{
$arrayMapDeltas = 0.0;
$collectionMapDeltas = 0.0;
$fixtureProvider = function () {
$array = [];
for ($i = 0; $i < NUMBER_OF_ITEMS; $i++) {
$array[] = $i;
}
return $array;
};
$mapper = function ($item) {
$result = 0;
for (; $item > 0; $item--) {
$result += $item;
}
return $result;
};
for ($j = 0; $j < REPEAT_COUNT; $j++) {
$array = $fixtureProvider();
$arrayMapStart = microtime(true);
$mappedArray = array_map($mapper, $array);
foreach ($mappedArray as $item) {
}
$arrayMapDeltas += microtime(true) - $arrayMapStart;
$array = $fixtureProvider();
$collection = new Collection($array);
$collectionMapStart = microtime(true);
$mappedCollection = $collection->map($mapper);
foreach ($mappedCollection as $item) {
}
$collectionMapDeltas += microtime(true) - $collectionMapStart;
}
return [
'name' => 'array_map vs Collection::map on ' . NUMBER_OF_ITEMS . ' integers n, counting sum(0, n) the naive way',
'native' => (float) $arrayMapDeltas / REPEAT_COUNT,
'collection' => (float) $collectionMapDeltas / REPEAT_COUNT
];
}
function getHashReport()
{
$arrayMapDeltas = 0.0;
$collectionMapDeltas = 0.0;
$fixtureProvider = function () {
$array = [];
for ($i = 0; $i < NUMBER_OF_ITEMS; $i++) {
$array[] = $i . 'asdf';
}
return $array;
};
$mapper = function ($item) {
return md5($item);
};
for ($j = 0; $j < REPEAT_COUNT; $j++) {
$array = $fixtureProvider();
$arrayMapStart = microtime(true);
$mappedArray = array_map($mapper, $array);
foreach ($mappedArray as $item) {
}
$arrayMapDeltas += microtime(true) - $arrayMapStart;
$array = $fixtureProvider();
$collection = new Collection($array);
$collectionMapStart = microtime(true);
$mappedCollection = $collection->map($mapper);
foreach ($mappedCollection as $item) {
}
$collectionMapDeltas += microtime(true) - $collectionMapStart;
}
return [
'name' => 'array_map vs Collection::map on ' . NUMBER_OF_ITEMS . ' md5 invocations',
'native' => (float) $arrayMapDeltas / REPEAT_COUNT,
'collection' => (float) $collectionMapDeltas / REPEAT_COUNT
];
}
function addReportToTable(Table $table, $reportData)
{
$row = [
$reportData['name'],
$reportData['native'] . 's',
$reportData['collection'] . 's',
((int) (($reportData['collection'] / $reportData['native']) * 100)) . '%',
];
$table->addRow($row);
}
$table = new Table(new Symfony\Component\Console\Output\ConsoleOutput());
$table->setHeaders(['operation details', 'native execution time', 'collection execution time', 'difference (percent)']);
addReportToTable($table, getIntegerReport());
addReportToTable($table, getStringReport());
addReportToTable($table, getObjectReport());
addReportToTable($table, getHashReport());
addReportToTable($table, getComplexOperationReport());
$table->render();
tests/performance/reduce_vs_array_reduce.php 0000666 00000014555 13436746323 0015455 0 ustar 00 reduce($mapper, 0);
$collectionMapDeltas += microtime(true) - $collectionMapStart;
}
return [
'name' => 'array_reduce vs Collection::reduce on ' . NUMBER_OF_ITEMS . ' integers (addition)',
'native' => (float) $arrayMapDeltas / REPEAT_COUNT,
'collection' => (float) $collectionMapDeltas / REPEAT_COUNT
];
}
function getStringReport()
{
$arrayMapDeltas = 0.0;
$collectionMapDeltas = 0.0;
$fixtureProvider = function () {
$array = [];
for ($i = 0; $i < NUMBER_OF_ITEMS; $i++) {
$array[] = $i . 'asd';
}
return $array;
};
$mapper = function ($temp, $item) {
return $temp . $item;
};
for ($j = 0; $j < REPEAT_COUNT; $j++) {
$array = $fixtureProvider();
$arrayMapStart = microtime(true);
array_reduce($array, $mapper, '');
$arrayMapDeltas += microtime(true) - $arrayMapStart;
$array = $fixtureProvider();
$collection = new Collection($array);
$collectionMapStart = microtime(true);
$collection->reduce($mapper, '');
$collectionMapDeltas += microtime(true) - $collectionMapStart;
}
return [
'name' => 'array_reduce vs Collection::reduce on ' . NUMBER_OF_ITEMS . ' strings (concatenation)',
'native' => (float) $arrayMapDeltas / REPEAT_COUNT,
'collection' => (float) $collectionMapDeltas / REPEAT_COUNT
];
}
function getObjectReport()
{
$arrayMapDeltas = 0.0;
$collectionMapDeltas = 0.0;
$fixtureProvider = function () {
$array = [];
for ($i = 0; $i < NUMBER_OF_ITEMS; $i++) {
$c = new stdClass();
$c->asd = 1;
$array[] = $c;
}
return $array;
};
$mapper = function ($temp, $item) {
return $temp + $item->asd;
};
for ($j = 0; $j < REPEAT_COUNT; $j++) {
$array = $fixtureProvider();
$arrayMapStart = microtime(true);
array_reduce($array, $mapper, 0);
$arrayMapDeltas += microtime(true) - $arrayMapStart;
$array = $fixtureProvider();
$collection = new Collection($array);
$collectionMapStart = microtime(true);
$collection->reduce($mapper, 0);
$collectionMapDeltas += microtime(true) - $collectionMapStart;
}
return [
'name' => 'array_reduce vs Collection::reduce on ' . NUMBER_OF_ITEMS . ' object (object to field value)',
'native' => (float) $arrayMapDeltas / REPEAT_COUNT,
'collection' => (float) $collectionMapDeltas / REPEAT_COUNT
];
}
function getComplexOperationReport()
{
$arrayMapDeltas = 0.0;
$collectionMapDeltas = 0.0;
$fixtureProvider = function () {
$array = [];
for ($i = 0; $i < NUMBER_OF_ITEMS; $i++) {
$array[] = $i;
}
return $array;
};
$mapper = function ($temp, $item) {
$result = 0;
for (; $item > 0; $item--) {
$result += $item;
}
return $temp + $result;
};
for ($j = 0; $j < REPEAT_COUNT; $j++) {
$array = $fixtureProvider();
$arrayMapStart = microtime(true);
array_reduce($array, $mapper, 0);
$arrayMapDeltas += microtime(true) - $arrayMapStart;
$array = $fixtureProvider();
$collection = new Collection($array);
$collectionMapStart = microtime(true);
$collection->reduce($mapper, 0);
$collectionMapDeltas += microtime(true) - $collectionMapStart;
}
return [
'name' => 'array_reduce vs Collection::reduce for ' . NUMBER_OF_ITEMS . ' integers n, counting sum(0, n) the naive way',
'native' => (float) $arrayMapDeltas / REPEAT_COUNT,
'collection' => (float) $collectionMapDeltas / REPEAT_COUNT
];
}
function getHashReport()
{
$arrayMapDeltas = 0.0;
$collectionMapDeltas = 0.0;
$fixtureProvider = function () {
$array = [];
for ($i = 0; $i < NUMBER_OF_ITEMS; $i++) {
$array[] = $i . 'asdf';
}
return $array;
};
$mapper = function ($temp, $item) {
return md5($temp . $item);
};
for ($j = 0; $j < REPEAT_COUNT; $j++) {
$array = $fixtureProvider();
$arrayMapStart = microtime(true);
array_reduce($array, $mapper, '');
$arrayMapDeltas += microtime(true) - $arrayMapStart;
$array = $fixtureProvider();
$collection = new Collection($array);
$collectionMapStart = microtime(true);
$collection->reduce($mapper, '');
$collectionMapDeltas += microtime(true) - $collectionMapStart;
}
return [
'name' => 'array_reduce vs Collection::reduce on ' . NUMBER_OF_ITEMS . ' md5 invocations',
'native' => (float) $arrayMapDeltas / REPEAT_COUNT,
'collection' => (float) $collectionMapDeltas / REPEAT_COUNT
];
}
function addReportToTable(Table $table, $reportData)
{
$row = [
$reportData['name'],
$reportData['native'] . 's',
$reportData['collection'] . 's',
((int) (($reportData['collection'] / $reportData['native']) * 100)) . '%',
];
$table->addRow($row);
}
$table = new Table(new Symfony\Component\Console\Output\ConsoleOutput());
$table->setHeaders(['operation details', 'native execution time', 'collection execution time', 'difference (percent)']);
addReportToTable($table, getIntegerReport());
addReportToTable($table, getStringReport());
addReportToTable($table, getObjectReport());
addReportToTable($table, getHashReport());
addReportToTable($table, getComplexOperationReport());
$table->render();
tests/scenarios/GroupingFlightsTest.php 0000666 00000004440 13436746323 0014401 0 ustar 00 "BOS",
"dest" => "LAX",
"date" => "2015-01-12",
"number" => "25",
"carrier" => "AA",
"delay" => 10.0,
"cancelled" => false
],
[
"origin" => "BOS",
"dest" => "LAX",
"date" => "2015-01-13",
"number" => "25",
"carrier" => "AA",
"delay" => 0.0,
"cancelled" => true
],
];
public function testIt()
{
$collection = new Collection($this->inputData);
$result = $collection
->groupBy(function ($v) {
return $v['dest'];
})
->map([$this, 'summarize'])
->map([$this, 'buildResults'])
->toArray();
$expected = [
'LAX' => [
'meanDelay' => 10,
'cancellationRate' => 0.5
]
];
$this->assertEquals($expected, $result);
}
public function summarize(Collection $flights)
{
$numCancellations = $flights
->filter(function ($f) {
return $f['cancelled'];
})
->size();
$totalDelay = $flights
->reject(function ($f) {
return $f['cancelled'];
})
->reduce(
function ($tmp, $f) {
return $tmp + $f['delay'];
},
0
);
return [
'numFlights' => $flights->size(),
'numCancellations' => $numCancellations,
'totalDelay' => $totalDelay
];
}
public function buildResults(array $airport)
{
return [
'meanDelay' => $airport['totalDelay'] / ($airport['numFlights'] - $airport['numCancellations']),
'cancellationRate' => $airport['numCancellations'] / $airport['numFlights']
];
}
}
tests/scenarios/FibonaccisSequenceTest.php 0000666 00000001135 13436746323 0015015 0 ustar 00 map('\DusanKasan\Knapsack\first')
->take(5)
->values()
->toArray();
$this->assertEquals([1, 1, 2, 3, 5], $result);
}
}
tests/scenarios/CallableFuctionNamesTest.php 0000666 00000001113 13436746323 0015273 0 ustar 00 concat([3, 4])
->sort('\DusanKasan\Knapsack\compare')
->values()
->toArray();
$expected = [1, 2, 3, 4];
$this->assertEquals($expected, $result);
}
}
tests/scenarios/MultipleOperationsTest.php 0000666 00000002146 13436746323 0015126 0 ustar 00 reject(function ($v) {
return $v > 2;
})
->filter(function ($k) {
return $k > 5;
})
->distinct()
->concat([1, 2])
->map(function ($i) {
return [$i, $i + 1];
})
->flatten()
->sort(function ($a, $b) {
return $a > $b;
})
->slice(2, 5)
->groupBy(function ($v) {
return $v % 2 == 0 ? 'even' : 'odd';
})
->get('even')
->toArray();
$this->assertEquals([2], $result);
}
}
tests/scenarios/CustomPassthroughFunctionTest.php 0000666 00000003366 13436746323 0016504 0 ustar 00 [
'Jane',
'Bob',
'Mary',
],
'emails' => [
'jane@example.com',
'bob@example.com',
'mary@example.com',
],
'occupations' => [
'Doctor',
'Plumber',
'Dentist',
],
];
//Must take and return a Collection
$transpose = function (Collection $collections) {
$transposed = array_map(
function (...$items) {
return $items;
},
...$collections->values()->toArray()
);
return Collection::from($transposed);
};
$result = Collection::from($formData)
->transform($transpose)
->toArray();
$expected = [
[
'Jane',
'jane@example.com',
'Doctor'
],
[
'Bob',
'bob@example.com',
'Plumber'
],
[
'Mary',
'mary@example.com',
'Dentist'
]
];
$this->assertEquals($expected, $result);
}
}
tests/spec/Exceptions/InvalidArgumentSpec.php 0000666 00000000622 13436746323 0015415 0 ustar 00 shouldHaveType(RuntimeException::class);
}
}
tests/spec/Exceptions/InvalidReturnValueSpec.php 0000666 00000000477 13436746323 0016117 0 ustar 00 shouldHaveType(RuntimeException::class);
}
}
tests/spec/Exceptions/ItemNotFoundSpec.php 0000666 00000000552 13436746323 0014701 0 ustar 00 shouldHaveType(RuntimeException::class);
}
}
tests/spec/Exceptions/NoMoreItemsSpec.php 0000666 00000000606 13436746323 0014527 0 ustar 00 shouldHaveType(RuntimeException::class);
}
}
tests/spec/CollectionSpec.php 0000666 00000100310 13436746323 0012271 0 ustar 00 beConstructedWith([1, 2, 3]);
$this->shouldHaveType(Collection::class);
$this->shouldHaveType(Iterator::class);
}
function it_can_be_instantiated_from_iterator()
{
$iterator = new ArrayIterator([1, 2]);
$this->beConstructedWith($iterator);
$this->toArray()->shouldReturn([1, 2]);
}
function it_can_be_instantiated_from_iterator_aggregate(IteratorAggregate $iteratorAggregate)
{
$iterator = new ArrayIterator([1, 2]);
$iteratorAggregate->getIterator()->willReturn($iterator);
$this->beConstructedWith($iteratorAggregate);
$this->toArray()->shouldReturn([1, 2]);
}
function it_can_be_instantiated_from_array()
{
$this->beConstructedWith([1, 2, 3]);
$this->toArray()->shouldReturn([1, 2, 3]);
}
function it_will_throw_when_passed_something_other_than_array_or_traversable()
{
$this->beConstructedWith(1);
$this->shouldThrow(InvalidArgument::class)->duringInstantiation();
}
function it_can_be_created_statically()
{
$this->beConstructedThrough('from', [[1, 2]]);
$this->toArray()->shouldReturn([1, 2]);
}
function it_can_be_created_to_iterate_over_function_infinitely()
{
$this->beConstructedThrough('iterate', [1, function($i) {return $i+1;}]);
$this->take(2)->toArray()->shouldReturn([1, 2]);
}
function it_can_be_created_to_iterate_over_function_non_infinitely()
{
$this->beConstructedThrough(
'iterate',
[
1,
function($i) {
if ($i > 3) {
throw new NoMoreItems;
}
return $i+1;
}
]
);
$this->toArray()->shouldReturn([1, 2, 3, 4]);
}
function it_can_be_created_to_repeat_a_value_infinite_times()
{
$this->beConstructedThrough('repeat', [1]);
$this->take(2)->toArray()->shouldReturn([1, 1]);
}
function it_can_filter()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->filter(function ($item) {
return $item > 2;
})
->toArray()
->shouldReturn([1 => 3, 2 => 3]);
$this
->filter(function ($item, $key) {
return $key > 2 && $item < 3;
})
->toArray()
->shouldReturn([3 => 2]);
}
function it_can_filter_falsy_values()
{
$this->beConstructedWith([false, null, '', 0, 0.0, []]);
$this->filter()->isEmpty()->shouldReturn(true);
}
function it_can_distinct()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->distinct()
->toArray()
->shouldReturn([1, 3, 3 => 2]);
}
function it_can_concat()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$collection = $this->concat([4, 5]);
$collection->toArray()->shouldReturn([4, 5, 3, 2]);
$collection->size()->shouldReturn(6);
}
function it_can_map()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->map(function ($item) {
return $item + 1;
})
->toArray()
->shouldReturn([2, 4, 4, 3]);
}
function it_can_reduce()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->reduce(
function ($temp, $item) {
$temp[] = $item;
return $temp;
},
['a' => [1]],
true
)
->values()
->toArray()
->shouldReturn([[1], 1, 3, 3, 2]);
$this
->reduce(
function ($temp, $item) {
return $temp + $item;
},
0
)
->shouldReturn(9);
$this
->reduce(
function ($temp, $item, $key) {
return $temp + $key + $item;
},
0
)
->shouldReturn(15);
$this
->reduce(
function (Collection $temp, $item) {
return $temp->append($item);
},
new Collection([])
)
->toArray()
->shouldReturn([1, 3, 3, 2]);
}
function it_can_flatten()
{
$this->beConstructedWith([1, [2, [3]]]);
$this->flatten()->values()->toArray()->shouldReturn([1, 2, 3]);
$this->flatten(1)->values()->toArray()->shouldReturn([1, 2, [3]]);
}
function it_can_sort()
{
$this->beConstructedWith([3, 1, 2]);
$this
->sort(function ($a, $b) {
return $a > $b;
})
->toArray()
->shouldReturn([1 => 1, 2 => 2, 0 => 3]);
$this
->sort(function ($v1, $v2, $k1, $k2) {
return $k1 < $k2 || $v1 == $v2;
})
->toArray()
->shouldReturn([2 => 2, 1 => 1, 0 => 3]);
}
function it_can_slice()
{
$this->beConstructedWith([1, 2, 3, 4, 5]);
$this
->slice(2, 4)
->toArray()
->shouldReturn([2 => 3, 3 => 4]);
$this
->slice(4)
->toArray()
->shouldReturn([4 => 5]);
}
function it_can_group_by()
{
$this->beConstructedWith([1, 2, 3, 4, 5]);
$collection = $this->groupBy(function ($i) {
return $i % 2;
});
$collection->get(0)->toArray()->shouldReturn([2, 4]);
$collection->get(1)->toArray()->shouldReturn([1, 3, 5]);
$collection = $this->groupBy(function ($k, $i) {
return ($k + $i) % 3;
});
$collection->get(0)->toArray()->shouldReturn([2, 5]);
$collection->get(1)->toArray()->shouldReturn([1, 4]);
$collection->get(2)->toArray()->shouldReturn([3]);
}
function it_can_group_by_key()
{
$this->beConstructedWith([
'some' => 'thing',
['letter' => 'A', 'type' => 'caps'],
['letter' => 'a', 'type' => 'small'],
['letter' => 'B', 'type' => 'caps'],
['letter' => 'Z']
]);
$collection = $this->groupByKey('type');
$collection->get('small')->toArray()->shouldReturn([
['letter' => 'a', 'type' => 'small']
]);
$collection->get('caps')->toArray()->shouldReturn([
['letter' => 'A', 'type' => 'caps'],
['letter' => 'B', 'type' => 'caps']
]);
$collection = $this->groupByKey('types');
$collection->shouldThrow(new ItemNotFound)->during('get', ['caps']);
}
function it_can_execute_callback_for_each_item(DOMXPath $a)
{
$a->query('asd')->shouldBeCalled();
$this->beConstructedWith([$a]);
$this
->each(function (DOMXPath $i) {
$i->query('asd');
})
->toArray()
->shouldReturn([$a]);
}
function it_can_get_size()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this->size()->shouldReturn(4);
}
function it_can_get_item_by_key()
{
$this->beConstructedWith([1, [2], 3]);
$this->get(0)->shouldReturn(1);
$this->get(1, true)->first()->shouldReturn(2);
$this->get(1)->shouldReturn([2]);
$this->shouldThrow(new ItemNotFound)->during('get', [5]);
}
function it_can_get_item_by_key_or_return_default()
{
$this->beConstructedWith([1, [2], 3]);
$this->getOrDefault(0)->shouldReturn(1);
$this->getOrDefault(1, null, true)->first()->shouldReturn(2);
$this->getOrDefault(1, null)->shouldReturn([2]);
$this->getOrDefault(5)->shouldReturn(null);
$this->getOrDefault(5, 'not found')->shouldReturn('not found');
}
function it_can_find()
{
$this->beConstructedWith([1, 3, 3, 2, [5]]);
$this
->find(function ($v) {
return $v < 3;
})
->shouldReturn(1);
$this
->find(function ($v, $k) {
return $v < 3 && $k > 1;
})
->shouldReturn(2);
$this
->find(function ($v) {
return $v < 0;
})
->shouldReturn(null);
$this
->find(
function ($v) {
return $v < 0;
},
'not found'
)
->shouldReturn('not found');
$this->find('\DusanKasan\Knapsack\isCollection', null, true)->first()->shouldReturn(5);
$this->find('\DusanKasan\Knapsack\isCollection')->shouldReturn([5]);
}
function it_can_count_by()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->countBy(function ($v) {
return $v % 2 == 0 ? 'even' : 'odd';
})
->toArray()
->shouldReturn(['odd' => 3, 'even' => 1]);
$this
->countBy(function ($k, $v) {
return ($k + $v) % 2 == 0 ? 'even' : 'odd';
})
->toArray()
->shouldReturn(['odd' => 3, 'even' => 1]);
}
function it_can_index_by()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->indexBy(function ($v) {
return $v;
})
->toArray()
->shouldReturn([1 => 1, 3 => 3, 2 => 2]);
$this
->indexBy(function ($v, $k) {
return $k . $v;
})
->toArray()
->shouldReturn(['01' => 1, '13' => 3, '23' => 3, '32' => 2]);
}
function it_can_check_if_every_item_passes_predicament_test()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->every(function ($v) {
return $v > 0;
})
->shouldReturn(true);
$this
->every(function ($v) {
return $v > 1;
})
->shouldReturn(false);
$this
->every(function ($v, $k) {
return $v > 0 && $k >= 0;
})
->shouldReturn(true);
$this
->every(function ($v, $k) {
return $v > 0 && $k > 0;
})
->shouldReturn(false);
}
function it_can_check_if_some_items_pass_predicament_test()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->some(function ($v) {
return $v < -1;
})
->shouldReturn(false);
$this
->some(function ($v, $k) {
return $v > 0 && $k < -1;
})
->shouldReturn(false);
$this
->some(function ($v) {
return $v < 2;
})
->shouldReturn(true);
$this
->some(function ($v, $k) {
return $v > 0 && $k > 0;
})
->shouldReturn(true);
}
function it_can_check_if_it_contains_a_value()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->contains(3)
->shouldReturn(true);
$this
->contains(true)
->shouldReturn(false);
}
function it_can_reverse()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$collection = $this->reverse();
$collection->rewind();
$collection->valid()->shouldReturn(true);
$collection->key()->shouldReturn(3);
$collection->current()->shouldReturn(2);
$collection->next();
$collection->valid()->shouldReturn(true);
$collection->key()->shouldReturn(2);
$collection->current()->shouldReturn(3);
$collection->next();
$collection->valid()->shouldReturn(true);
$collection->key()->shouldReturn(1);
$collection->current()->shouldReturn(3);
$collection->next();
$collection->valid()->shouldReturn(true);
$collection->key()->shouldReturn(0);
$collection->current()->shouldReturn(1);
$collection->next();
$collection->valid()->shouldReturn(false);
}
function it_can_reduce_from_right()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->reduceRight(
function ($temp, $e) {
return $temp . $e;
},
0
)
->shouldReturn('02331');
$this
->reduceRight(
function ($temp, $key, $item) {
return $temp + $key + $item;
},
0
)
->shouldReturn(15);
$this
->reduceRight(
function (Collection $temp, $item) {
return $temp->append($item);
},
new Collection([])
)
->toArray()
->shouldReturn([2, 3, 3, 1]);
}
function it_can_return_only_first_x_elements()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this->take(2)
->toArray()
->shouldReturn([1, 3]);
}
function it_can_skip_first_x_elements()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this->drop(2)
->toArray()
->shouldReturn([2 => 3, 3 => 2]);
}
function it_can_return_values()
{
$this->beConstructedWith(['a' => 1, 'b' => 2]);
$this->values()->toArray()->shouldReturn([1, 2]);
}
function it_can_reject_elements_from_collection()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->reject(function ($v) {
return $v == 3;
})
->toArray()
->shouldReturn([1, 3 => 2]);
$this
->reject(function ($v, $k) {
return $k == 2 && $v == 3;
})
->toArray()
->shouldReturn([1, 3, 3 => 2]);
}
function it_can_get_keys()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->keys()
->toArray()
->shouldReturn([0, 1, 2, 3]);
}
function it_can_interpose()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->interpose('a')
->values()
->toArray()
->shouldReturn([1, 'a', 3, 'a', 3, 'a', 2]);
}
function it_can_drop_elements_from_end_of_the_collection()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->dropLast()
->toArray()
->shouldReturn([1, 3, 3]);
$this
->dropLast(2)
->toArray()
->shouldReturn([1, 3]);
}
function it_can_interleave_elements()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->interleave(['a', 'b', 'c', 'd', 'e'])
->values()
->toArray()
->shouldReturn([1, 'a', 3, 'b', 3, 'c', 2, 'd', 'e']);
}
function it_can_repeat_items_of_collection_infinitely()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->cycle()
->take(8)
->values()
->toArray()
->shouldReturn([1, 3, 3, 2, 1, 3, 3, 2]);
}
function it_can_prepend_item()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->prepend(1)
->values()
->toArray()
->shouldReturn([1, 1, 3, 3, 2]);
}
function it_can_prepend_item_with_key()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->prepend(1, 'a')
->toArray()
->shouldReturn(['a' => 1, 0 => 1, 1 => 3, 2 => 3, 3 => 2]);
}
function it_can_append_item()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->append(1)
->values()
->toArray()
->shouldReturn([1, 3, 3, 2, 1]);
}
function it_can_append_item_with_key()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->append(1, 'a')
->toArray()
->shouldReturn([1, 3, 3, 2, 'a' => 1]);
}
function it_can_drop_items_while_predicament_is_true()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->dropWhile(function ($v) {
return $v < 3;
})
->toArray()
->shouldReturn([1 => 3, 2 => 3, 3 => 2]);
$this
->dropWhile(function ($v, $k) {
return $k < 2 && $v < 3;
})
->toArray()
->shouldReturn([1 => 3, 2 => 3, 3 => 2]);
}
function it_can_map_and_then_concatenate_a_collection()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->mapcat(function ($v) {
return [[$v]];
})
->values()
->toArray()
->shouldReturn([[1], [3], [3], [2]]);
$this
->mapcat(function ($v, $k) {
return [[$k + $v]];
})
->values()
->toArray()
->shouldReturn([[1], [4], [5], [5]]);
}
function it_can_take_items_while_predicament_is_true()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->takeWhile(function ($v) {
return $v < 3;
})
->toArray()
->shouldReturn([1]);
$this
->takeWhile(function ($v, $k) {
return $k < 2 && $v < 3;
})
->toArray()
->shouldReturn([1]);
}
function it_can_split_the_collection_at_nth_item()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this->splitAt(2)->size()->shouldBe(2);
$this->splitAt(2)->first()->toArray()->shouldBeLike([1, 3]);
$this->splitAt(2)->second()->toArray()->shouldBeLike([2 => 3, 3 => 2]);
}
function it_can_split_the_collection_with_predicament()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$s1 = $this->splitWith(function ($v) {
return $v < 3;
});
$s1->size()->shouldBe(2);
$s1->first()->toArray()->shouldBe([1]);
$s1->second()->toArray()->shouldBe([1 => 3, 2 => 3, 3 => 2]);
$s2 = $this->splitWith(function ($v, $k) {
return $v < 2 && $k < 3;
});
$s2->size()->shouldBe(2);
$s2->first()->toArray()->shouldBe([1]);
$s2->second()->toArray()->shouldBe([1 => 3, 2 => 3, 3 => 2]);
}
function it_can_replace_items_by_items_from_another_collection()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->replace([3 => 'a'])
->toArray()
->shouldReturn([1, 'a', 'a', 2]);
}
function it_can_get_reduction_steps()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->reductions(
function ($tmp, $i) {
return $tmp + $i;
},
0
)
->toArray()
->shouldReturn([0, 1, 4, 7, 9]);
}
function it_can_return_every_nth_item()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this->takeNth(2)
->toArray()
->shouldReturn([1, 2 => 3]);
}
function it_can_shuffle_itself()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->shuffle()
->reduce(
function ($tmp, $i) {
return $tmp + $i;
},
0
)
->shouldReturn(9);
}
function it_can_partition()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$s1 = $this->partition(3, 2, [0, 1]);
$s1->size()->shouldBe(2);
$s1->first()->toArray()->shouldBe([1, 3, 3]);
$s1->second()->toArray()->shouldBe([2 => 3, 3 => 2, 0 => 0]);
$s2 = $this->partition(3, 2);
$s2->size()->shouldBe(2);
$s2->first()->toArray()->shouldBe([1, 3, 3]);
$s2->second()->toArray()->shouldBe([2 => 3, 3 => 2]);
$s3 = $this->partition(3);
$s3->size()->shouldBe(2);
$s3->first()->toArray()->shouldBe([1, 3, 3]);
$s3->second()->toArray()->shouldBe([3 => 2]);
$s4 = $this->partition(1, 3);
$s4->size()->shouldBe(2);
$s4->first()->toArray()->shouldBe([1,]);
$s4->second()->toArray()->shouldBe([3 => 2]);
}
function it_can_partition_by()
{
$this->beConstructedWith([1, 3, 3, 2]);
$s1 = $this->partitionBy(function ($v) {
return $v % 3 == 0;
});
$s1->size()->shouldBe(3);
$s1->first()->toArray()->shouldBe([1]);
$s1->second()->toArray()->shouldBe([1 => 3, 2 => 3]);
$s1->values()->get(2)->toArray()->shouldBe([3 => 2]);
$s2 = $this->partitionBy(function ($v, $k) {
return $k - $v;
});
$s2->size()->shouldBe(4);
$s2->first()->toArray()->shouldBe([1]);
$s2->values()->get(1)->toArray()->shouldBe([1 => 3]);
$s2->values()->get(2)->toArray()->shouldBe([2 => 3]);
$s2->values()->get(3)->toArray()->shouldBe([3 => 2]);
}
function it_can_get_nth_value()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this->first(0)->shouldReturn(1);
$this->values()->get(3)->shouldReturn(2);
}
function it_can_create_infinite_collection_of_repeated_values()
{
$this->beConstructedThrough('repeat', [1]);
$this->take(3)->toArray()->shouldReturn([1, 1, 1]);
}
function it_can_create_finite_collection_of_repeated_values()
{
$this->beConstructedThrough('repeat', [1, 1]);
$this->toArray()->shouldReturn([1]);
}
function it_can_create_range_from_value_to_infinity()
{
$this->beConstructedThrough('range', [5]);
$this->take(2)->toArray()->shouldReturn([5, 6]);
}
function it_can_create_range_from_value_to_another_value()
{
$this->beConstructedThrough('range', [5, 6]);
$this->take(4)->toArray()->shouldReturn([5, 6]);
}
function it_can_check_if_it_is_not_empty()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this->isEmpty()->shouldReturn(false);
$this->isNotEmpty()->shouldReturn(true);
}
function it_can_check_if_it_is_empty()
{
$this->beConstructedWith([]);
$this->isEmpty()->shouldReturn(true);
$this->isNotEmpty()->shouldReturn(false);
}
function it_can_check_frequency_of_distinct_items()
{
$this->beConstructedWith([1, 3, 3, 2,]);
$this
->frequencies()
->toArray()
->shouldReturn([1 => 1, 3 => 2, 2 => 1]);
}
function it_can_get_first_item()
{
$this->beConstructedWith([1, [2], 3]);
$this->first()->shouldReturn(1);
$this->drop(1)->first()->shouldReturn([2]);
$this->drop(1)->first(true)->toArray()->shouldReturn([2]);
}
function it_will_throw_when_trying_to_get_first_item_of_empty_collection()
{
$this->beConstructedWith([]);
$this->shouldThrow(ItemNotFound::class)->during('first');
}
function it_can_get_last_item()
{
$this->beConstructedWith([1, [2], 3]);
$this->last()->shouldReturn(3);
$this->take(2)->last()->shouldReturn([2]);
$this->take(2)->last(true)->toArray()->shouldReturn([2]);
}
function it_will_throw_when_trying_to_get_last_item_of_empty_collection()
{
$this->beConstructedWith([]);
$this->shouldThrow(ItemNotFound::class)->during('last');
}
function it_can_realize_the_collection(PlusOneAdder $adder)
{
$adder->dynamicMethod(1)->willReturn(2);
$adder->dynamicMethod(2)->willReturn(3);
$this->beConstructedWith(function () use ($adder) {
yield $adder->dynamicMethod(1);
yield $adder->dynamicMethod(2);
});
$this->realize();
}
function it_can_combine_the_collection()
{
$this->beConstructedWith(['a', 'b']);
$this->combine([1, 2])->toArray()->shouldReturn(['a' => 1, 'b' => 2]);
$this->combine([1])->toArray()->shouldReturn(['a' => 1]);
$this->combine([1, 2, 3])->toArray()->shouldReturn(['a' => 1, 'b' => 2]);
}
function it_can_get_second_item()
{
$this->beConstructedWith([1, 2]);
$this->second()->shouldReturn(2);
}
function it_throws_when_trying_to_get_non_existent_second_item()
{
$this->beConstructedWith([1]);
$this->shouldThrow(ItemNotFound::class)->during('second');
}
function it_can_drop_item_by_key()
{
$this->beConstructedWith(['a' => 1, 'b' => 2]);
$this->except(['a', 'b'])->toArray()->shouldReturn([]);
}
function it_can_get_the_difference_between_collections()
{
$this->beConstructedWith([1, 2, 3, 4]);
$this->diff([1, 2])->toArray()->shouldReturn([2 => 3, 3 => 4]);
$this->diff([1, 2], [3])->toArray()->shouldReturn([3 => 4]);
}
function it_can_flip_the_collection()
{
$this->beConstructedWith(['a' => 1, 'b' => 2]);
$this->flip()->toArray()->shouldReturn([1 => 'a', 2 => 'b']);
}
function it_can_check_if_key_exits()
{
$this->beConstructedWith(['a' => 1, 'b' => 2]);
$this->has('a')->shouldReturn(true);
$this->has('x')->shouldReturn(false);
}
function it_filters_by_keys()
{
$this->beConstructedWith(['a' => 1, 'b' => 2, 'c' => 3]);
$this->only(['a', 'b'])->toArray()->shouldReturn(['a' => 1, 'b' => 2]);
$this->only(['a', 'b', 'x'])->toArray()->shouldReturn(['a' => 1, 'b' => 2]);
}
function it_can_serialize_and_unserialize()
{
$original = Collection::from([1, 2, 3])->take(2);
$this->beConstructedWith([1, 2, 3, 4]);
$this->shouldHaveType(Serializable::class);
$this->unserialize($original->serialize());
$this->toArray()->shouldReturn([1, 2]);
}
function it_can_zip()
{
$this->beConstructedWith([1, 2, 3]);
$this->zip(['a' => 1, 'b' => 2, 'c' => 4])
->map('\DusanKasan\Knapsack\toArray')
->toArray()
->shouldReturn([[1, 'a' => 1], [1 => 2, 'b' => 2], [2 => 3, 'c' => 4]]);
$this->zip([4, 5, 6], [7, 8, 9])
->map('\DusanKasan\Knapsack\values')
->map('\DusanKasan\Knapsack\toArray')
->toArray()
->shouldReturn([[1, 4, 7], [2, 5, 8], [3, 6, 9]]);
$this->zip([4, 5])
->map('\DusanKasan\Knapsack\values')
->map('\DusanKasan\Knapsack\toArray')
->toArray()
->shouldReturn([[1, 4], [2, 5]]);
}
function it_can_use_callable_as_transformer()
{
$this->beConstructedWith([1, 2, 3]);
$this
->transform(function (Collection $collection) {
return $collection->map('\DusanKasan\Knapsack\increment');
})
->toArray()
->shouldReturn([2, 3, 4]);
$this
->shouldThrow(InvalidReturnValue::class)
->during(
'transform',
[
function (Collection $collection) {
return $collection->first();
}
]
);
}
function it_can_extract_data_from_nested_collections()
{
$input = [
[
'a' => [
'b' => 1
]
],
[
'a' => [
'b' => 2
]
],
[
'*' => [
'b' => 3
]
],
[
'.' => [
'b' => 4
],
'c' => [
'b' => 5
]
]
];
$this->beConstructedWith($input);
$this->extract('')->toArray()->shouldReturn($input);
$this->extract('a.b')->toArray()->shouldReturn([1, 2]);
$this->extract('*.b')->toArray()->shouldReturn([1, 2, 3, 4, 5]);
$this->extract('\*.b')->toArray()->shouldReturn([3]);
$this->extract('\..b')->toArray()->shouldReturn([4]);
}
function it_can_get_the_intersect_of_collections()
{
$this->beConstructedWith([1, 2, 3]);
$this->intersect([1, 2])->values()->toArray()->shouldReturn([1, 2]);
$this->intersect([1], [3])->values()->toArray()->shouldReturn([1, 3]);
}
function it_can_check_if_size_is_exactly_n()
{
$this->beConstructedWith([1, 2]);
$this->sizeIs(2)->shouldReturn(true);
$this->sizeIs(3)->shouldReturn(false);
$this->sizeIs(0)->shouldReturn(false);
}
function it_can_check_if_size_is_less_than_n()
{
$this->beConstructedWith([1, 2]);
$this->sizeIsLessThan(0)->shouldReturn(false);
$this->sizeIsLessThan(2)->shouldReturn(false);
$this->sizeIsLessThan(3)->shouldReturn(true);
}
function it_can_check_if_size_is_greater_than_n()
{
$this->beConstructedWith([1, 2]);
$this->sizeIsGreaterThan(2)->shouldReturn(false);
$this->sizeIsGreaterThan(1)->shouldReturn(true);
$this->sizeIsGreaterThan(0)->shouldReturn(true);
}
function it_can_check_if_size_is_between_n_and_m()
{
$this->beConstructedWith([1, 2]);
$this->sizeIsBetween(1, 3)->shouldReturn(true);
$this->sizeIsBetween(3, 4)->shouldReturn(false);
$this->sizeIsBetween(0, 0)->shouldReturn(false);
$this->sizeIsBetween(3, 1)->shouldReturn(true);
}
function it_can_sum_the_collection()
{
$this->beConstructedWith([1, 2, 3, 4]);
$this->sum()->shouldReturn(10);
$this->append(1.5)->sum()->shouldReturn(11.5);
}
function it_can_get_average_of_the_collection()
{
$this->beConstructedWith([1, 2, 2, 3]);
$this->average()->shouldReturn(2);
$this->append(3)->average()->shouldReturn(2.2);
}
function it_will_return_zero_when_average_is_called_on_empty_collection()
{
$this->beConstructedWith([]);
$this->average()->shouldReturn(0);
}
function it_can_get_maximal_value_in_the_colleciton()
{
$this->beConstructedWith([1, 2, 3, 2]);
$this->max()->shouldReturn(3);
}
function it_will_return_null_when_max_is_called_on_empty_collection()
{
$this->beConstructedWith([]);
$this->max()->shouldReturn(null);
}
function it_can_get_min_value_in_the_colleciton()
{
$this->beConstructedWith([2, 1, 3, 2]);
$this->min()->shouldReturn(1);
}
function it_will_return_null_when_min_is_called_on_empty_collection()
{
$this->beConstructedWith([]);
$this->min()->shouldReturn(null);
}
function it_can_convert_the_collection_to_string()
{
$this->beConstructedWith([2, 'a', 3, null]);
$this->toString()->shouldReturn('2a3');
}
function it_can_replace_by_key()
{
$this->beConstructedWith(['a' => 1, 'b' => 2, 'c' => 3]);
$this->replaceByKeys(['b' => 3])->toArray()->shouldReturn(['a' => 1, 'b' => 3, 'c' => 3]);
}
function it_can_use_the_utility_methods()
{
$this->beConstructedWith([1, 3, 2]);
$this
->sort('\DusanKasan\Knapsack\compare')
->values()
->toArray()
->shouldReturn([1, 2, 3]);
$this
->map('\DusanKasan\Knapsack\compare')
->toArray()
->shouldReturn([1, 1, 0]);
$this
->map('\DusanKasan\Knapsack\decrement')
->toArray()
->shouldReturn([0, 2, 1]);
}
}
ci/phpmd.xml 0000666 00000004737 13436746323 0007023 0 ustar 00
Created with the PHP Coding Standard Generator. http://edorian.github.com/php-coding-standard-generator/
ci/phpspec-with-coverage.yml 0000666 00000000426 13436746323 0012107 0 ustar 00 extensions:
- PhpSpec\Extension\CodeCoverageExtension
code_coverage:
format:
- html
- clover
output:
html: coverage
clover: coverage.clover
suites:
default:
spec_path: tests
namespace: DusanKasan\Knapsack
psr4_prefix: DusanKasan\Knapsack
CONTRIBUTING.md 0000666 00000001563 13436746323 0007021 0 ustar 00 ## How to contribute
This project is open to contribution from anyone, as long as you cover your changes with specs and if it is a bigger change with scenario. Your pull requests will be merged after your code passe CI and manual code review.
Every change merges to master. No development is done in other branches.
## Typical contribution use case
- You need a feature that is not implemented yet
- Search for open/closed issues relating to what you need
- If you don't find anything, create new issue
- Fork this repository and create fix/feature in the fork
- Write tests/scenarios for your change
- If you changed API, document the change in README
- Create pull request, describe what you did
- Wait for CI to verify you didn't break anything
- If you did, rewrite it
- If CI passes, wait for manual review by repo's owner
- Your pull request will be merged into master
.gitignore 0000666 00000000075 13436746323 0006555 0 ustar 00 /vendor
/coverage
coverage.clover
coverage.xml
phpunit.xml