Download the PHP package ps/fluent-traversable without Composer
On this page you can find all versions of the php package ps/fluent-traversable. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download ps/fluent-traversable
More information about ps/fluent-traversable
Files in ps/fluent-traversable
Package fluent-traversable
Short Description Support for operating on collections and arrays by functional way. Inspired by guava's FluentIterable, java8 Stream framework and scala stuff
License MIT
Informations about the package fluent-traversable
Fluent Traversable
FluentTraversable is small tool that adds a bit of functional programming to php, especially for arrays and collections. This library is inspired by java8 stream framework, guava FluentIterable and Scala functional features. To fully enjoy of this library, knowledge of basic functional patterns is welcome.
Summary and key features:
- allows to working with arrays and everything that implements
Traversable
interface - based on functional programming concepts:
- shortened
Closure
s - value extractors - function composition
- support for ? Quote mark is clickable ;))
- standard functional operations on collections
- shortened
- simplifies algorithms' code. Lets see few code examples and try to rewrite that examples using loops and ifs:
- simple example
- more complex example with function composition
- another example with function composition
- makes code more declarative, readable, less complex and more maintainable
- simple, considered interface: 95% of methods with 0 or 1 argument, 5% of methods with 2 arguments, 0% of methods with 3 or more arguments
- no magic in implementation, full support for IDE code completion
- inspired by few other technologies, but fully adapted to php world. This library is not a blind copy of other tools.
- framework independent, only 1 small external dependency - php-option. This library won't download a half of internet.
Quick example
We have an array of patients and we want to know percentage of female patients grouped by blood type.
Explanation and more information about this example you get here.
ToC
- Installation
- FluentTraversable
- FluentComposer
- FluentComposer as predicate / mapping function
- Predicates
- Puppet
- Contribution
- License
Installation
Installation is very easy (thanks to composer ;)):
(add to require section in your composer.json file)
"ps/fluent-traversable": "*"
You should choose last stable version, wildcard char ("*") is only an example.
FluentTraversable
Thanks to FluentTraversable
class you can operate on arrays and collection in declarative and readable way. There is a
simple example.
We want to get emails of male authors of books that have been released before 2007.
Ok, nested loops, nested if statements... It doesn't look good. If I use php array_map and array_filter functions, result wouldn't be better, would be even worst, so I omit this example.
The same code using FluentTraversable
:
IMPORTANT
In examples
toMap
andtoArray
functions are used to convert elements to array. The difference between those two functions istoArray
re-indexes elements,toMap
preserves indexes. You should usetoArray
method when indexes in your use case are not important, otherwise you should usetoMap
.
There are no loops, if statements, it looks straightforward, flow is clear and explicit (when you now what filter
,
flatMap
, map
etc methods are doing - as I said before the basics functional programming patters are needed ;)).
IMPORTANT
What does
flatMap
do? It maps single values to collections of values and then merges all those collections into one collection. In example aboveBook
has many authors, thanks toflatMap
we are able to extract all authors to one dimensional array. When we would usemap
, on output would be array of authors' arrays.
is
class (alias to Predicates
class) is factory for closures that have one argument and evaluate it to boolean
value. There are lt
, gt
, eq
, not
etc methods. Closures in php are very lengthy, you have to write function
keyword, curly braces, return statement, semicolon etc - a lot of syntax noise. Closure is multiline (yes, I now it can
be written in single line, but it would be unreadable), so it is no very compact. To handle simple predicate cases, you
might use is
class. More about predicates you can read in Predicates section.
get::value('authors')
also is a shortcut for closures, this is semantic equivalent to:
Nested paths in predicates and get::value
function are supported, so this code works as expected:
get::value('address.city.name')
.
IMPORTANT
In the most of functions (where make it sense) to predicate/mapping function are provided two arguments: element value and index:
When you won't index to be passed as second argument, you could use
func::unary($func)
function. It is very helpful especially when you want to use php build-in function that has optional second argument with different meaning, for examplestr_split
:
FluentTraversable
has a lot of useful methods: map
, flatMap
, filter
, unique
, groupBy
, orderBy
, allMatch
,
anyMatch
, noneMatch
, firstMatch
, maxBy
, minBy
, reduce
, toArray
, toMap
and more. List, description and examples
of all those methods are available in TraversableFlow interface. Each method belongs to one of two groups:
intermediate or terminate operations. Intermediate operation does some work on input array, modifies it and returns
FluentTraversable
object for further processing, so you can chain another operation. Terminate operation does some
calculation on each element of array and returns result of this calculation. For example size
operation returns
integer that is length of input array, so you can not chain operation anymore.
Example:
There are few terminal operations that returns Option
value (if you don't know what is Option or Optional value pattern,
follow this links: php-option, Optional explanation in Java). For example firstMatch
method could find nothing,
so instead return null or adding second optional argument to provide default value, Option
object is returned. Option
object is a wrapper for value, it can contain value, but it haven't to. You should threat Option
as collection with 0 or 1
value. Option
class provides few familiar methods to FluentTraversable
, for example map
and filter
. You can get
value from Option
by getOrElse
method:
Example:
If Stephen King's book was found, "Found book: TITLE" will be printed, otherwise "Not found any book...".
Properly used, option is very powerful and it integrates with FluentTraversable
perfectly. Option::map
method is
very inconspicuous, but it is also very useful. Thanks to Option::map
you can execute piece of code when value is
available without using if
statement:
IMPORTANT
Option
in many cases is very useful and it often simplifies the code. If you do not feel how to properly use it, check "Bigger example" section in this article and all examples withOption
in this documentation.Option
hasgetOrElse
method, so you can eventually use it to grab the value or default value. However I recommend you to learn how to properly use this pattern, in literature it is also calledMaybe
orOptional
pattern.IMPORTANT
When you want to use
Option::map
function, be aware when provided mapping function returnsnull
,map
function will returnSome(null)
(notNone()
) - that could be undesirable. Example below is not correct if$patientRepo::find()
method might returnnull
:When you want to transform value wrapped by
Option
and mapping function could returnnull
you should useflatMap
andget::option()
combo. There is correct example:
get::option
is similar toget::value
, the difference is it wraps value inOption
type.
FluentComposer
FluentComposer
is a tool to compose complex operations on arrays. You can define one complex operation thanks to
composer, and apply it multiple times on any array. FluentComposer
has the same interface as FluentTraversable
(those two classes implements the same interface: TraversableFlow
).
There is an example:
Ok, we have $maxEvenPrinter
object, what's next?
As I said, FluentComposer
has almost the same methods as FluentTraversable
. The difference between those two classes
is that, FluentTraversable
needs input array when object is created and it should be used once, FluentComposer
doesn't need array when object is created and can be invoked multiple times with different input arrays. Internally
FluentComposer
uses FluentTraversable
instance ;) You should threat FluentComposer
as tool to compose functions.
FluentComposer
has three factory methods that differ in arguments that are accepted by created function:
-
FluentComposer::forArray()
- created function accepts one array/traversable argument -
FluentComposer::forVarargs()
- created function accepts variable number of arguments (varargs): FluentComposer::forValue()
- created function accepts one argument that will be threaten as only element of array. This method is similar toFluentComposer::forVarargs()
, the difference is all arguments are ignored except the first.
FluentComposer as predicate / mapping function
You can use FluentComposer
to create predicate or mapping function for FluentTraversable
, especially after
functions that transforms single value to array of values (groupBy
, partition
etc.).
Example:
We have an array of patients and we want to know percentage of female patients grouped by blood type.
IMPORTANT
Directly chaining from
FluentComposer::forArray()
(and other factory methods) is not always safe, some methods does not returnFluentComposer
, butOption
object. Methods that returnsOption
are:reduce
,firstMatch
,max
,min
,first
,last
,get
. When you after all want to chain directly fromFluentComposer::forArray()
and use terminal operation that returnsOption
, you can apply a trick:
There is also FluentComposer::forValue()
method to create function with one argument that contains single value. It might be
useful to create predicates or mapping functions for single value.
Example:
We want to find doctors that all patients are women (gynecologists?).
Predicates
Predicate is a function that evaluates single value to boolean. Predefined predicates are available in is
and Predicates
classes. Those classes are the same, is
is an alias to Predicates
, so you can choose witch one to use (is
gives more
expressiveness to code). Predicates are perfect to use in filter
, firstMatch
, partition
, allMatch
, noneMatch
,
anyMatch
methods of FluentTraversable
.
The most of predicates (for example: eq
, notEq
, gt
, qte
, identical
, notIdentical
, in
, notIn
, contains
)
have two versions:
-
unary:
predicate($valueToCompare)
- binary -
predicate($property, $valueToCompare)
Few predicates (null
, notNull
, false
, true
, blank
, notBlank
) have also two, but different versions:
-
not argument:
predicate()
- unary:
predicate($property)
There are also logical predicates (not
, allTrue
- logical and, anyTrue
- logical or), but when you need to create
complex predicate maybe the better and more readable way is just to use closure. allTrue
and anyTrue
accepts also
evaluated values, for example:
Evaluated values could be useful when filtering of some values depends on external condition and you don't want to use
separate if
statement because of readability purpose - of course if your array is really big, be aware the iteration
through all elements will be done, so be carefully and use that feature consciously.
IMPORTANT
Predicates can also be used with grouping functions. Now there is only
size::of()
function.Example:
We want to find doctors with less than 5 patients
Puppet
Puppet is a very small (less than 100 lines of code) class, but it is also very powerful. What is a Puppet? Thanks to Puppet you can "record" some behaviour and execute this behaviour multiple times on various objects.
Example:
Puppet
supports property access, array access and method calls with arguments. Originally it was created to simplify map
and
flatMap
operations in FluentTraversable
. It is is also used internally by FluentComposer
, but maybe you will find
another use case for Puppet
.
Puppet has two factory methods: record
and object
- those methods are the same, object
method was created only for
semantic purpose. You can use Puppet
to create mapping function for map
, flatMap
etc. functions, but get::value()
is recommended for this purpose.
the
class is alias to Puppet
, it only adds semantic meaning to using Puppet
in FluentTraversable
context:
->map(the::object()->getName())
is much more readable than ->map(Puppet::record()->getName())
.
Contribution
Any suggestions, PR, bug reports etc. are welcome ;)
License
MIT - details in LICENSE file