PHP Map: Advanced collection methods
Tags: php, array, tutoial, collection
Published: 2020-03-11 11:29:25

The **PHP Map package** for collections contains many methods to ease your day to day work. They are **extremely helpful** in a lot of situations where you’ve written custom code up to now. This tutorial explains some of the **most useful ones** you don’t want to miss any more!

- [Documentation at Github](https://github.com/aimeos/map)
- [Composer package on Packagist](https://packagist.org/packages/aimeos/map)

## Installation

To use the PHP Map package, you have to add it to your composer based application:

`composer req aimeos/map`

Afterwards, there will be a map() function available, that creates a Map object which offers the methods explained.

## col(): Map key/value pairs

There are numerous times when you need to transform an existing array into a list of key/value pairs. Think about a list of database records for example which should be indexed by ID instead of a sequencial index. The col() method creates that without the necessity of a loop:

```
$rows = [['id' => 1, 'code' => 'a'], ['id' => 3, 'code' => 'd']];
$result = map( $rows )->col( 'code', 'id' );
 
// Result equals:
map( [1 => 'a', 3 => 'd'] );
```

This does also work with objects if they implement the magic PHP `__get()` method.

## collapse() and flat(): Transform multi-dimensional arrays

Do you remember the last time when you had a multi-dimensional array and you needed a flat array of all elements? If you know the maximum depth, you can nest several loops but if not, you need to write a recursive function, which is very cumbersome. The Map object offers the flat() method, that can to do that in one line:

```
$array = [[1, 2], [3, [4, 5]]];
$result = map( $array )->flat();
 
// Result equals:
map( [1, 2, 3, 4, 5] );
```

There’s a similar method named `collapse()`, that does almost the same but keeps the key of each value. Thus, it will overwrite elements with duplicate keys (also numeric keys):

```
$array = [['a' => 1, 'b' => 2], ['c' => 3, ['a' => 4, 'b' => 5]]];
$result = map( $array )->collapse();
 
// Result equals:
map( ['a' => 4, 'b' => 5, 'c' => 3] );
```

Both methods accept the number of levels they should collapse or flatten as parameter. That way it’s easy to keep e.g. the third level and above unflattened or uncollapsed.

## concat(), merge() and union(): Differences in combining arrays

In PHP, you can combine arrays using `array_merge()` or the `+` operator but their results are different. The PHP map package also offers the `concat()` method:

```
$one = [0 => 'a', 'b' => 2];
$two = ['b' => 3, 0 => 4];
 
$result = map( $one )->concat( $two );
//equals map( [0 => 'a', 'b' => 2, 1 => 3, 2 => 4] );
 
$result = map( $one )->merge( $two );
//equals map( [0 => 'a', 'b' => 3, 1 => 4] );
 
$result = map( $one )->union( $two );
//equals map( [0 => 'a', 'b' => 2] );
```

`concat()` combines the elements of both arrays but doesn’t care about their keys and the resulting list will contain all elements of both arrays. `merge()` which uses the `array_merge()` function overwrites existing string keys but renumbers numeric keys. `union()` is the same as using the `+` operator and doesn’t add elements whose keys does already exist.

## equals(): Test if arrays contains the same elements

To compare arrays, PHP only offers the `==` and `===` operators. They return only true if both array contains the same elements with the same keys (and the same order in case of `===`). Contrary, the `equals()` method of the Map object can also test arrays without checking the keys:

```
$one = ['a', 'b'];
$two = ['b', 'a'];
 
map( $one )->equals( $two ); // true
map( $one )->equals( $two, true ); // false
```

When passing true as second parameter, `equals()` works like the `==` operator.

## find(): Return the first matching element

Sometimes, you need the first element from an array that matches your needs. The Map object offers the find() method which allows you to pass a closure function as parameter. This anonymous function can check whose element matches your needs:

```
map( ['a', 'c', 'e'] )->find( function( $value, $key ) {
    return $value >= 'b';
} );
 
// Result equals 'c'
```

If no matching value is found, null is returned. If you pass true as second parameter, the search for the matching element will be reversed and you will get the last matching element.

## first(), last(), get() and pull(): Advanced ways to retrieve an element

Fetching the first, last or any element of a list of elements is very handy and most useful in many cases. But the PHP Map object offers more than that. What if those methods don’t return null if no element is found but throw an exception or execute a closure? Instead of:

```
if( ( $item = map( [] ) )->first() === null ) {
    throw new \Exception( 'error' );
}
 
if( ( $item = map( [] ) )->last() === null ) {
    // fetch and return record from database
}
$id = $item->getId();
```

you can simply write:

```
$id = map( [] )->first( new \Exception( 'error' ) )->getId();
 
$id = map( [] )->last( function() {
    // fetch and return record from database
} )->getId();
```

All methods (`first()`, `last()`, `get()` and `pull()`) methods accept either an exception that’s thrown or a closure that’s executed if no element is available. Thus, it saves you the annoying checks that make you code full of if/else statements.

If you pass an exception, the exception object is created whether it’s used or not. This is no problem as long as you don’t do that in a loop. Then, create one exception object before the loop and pass only the variable to the methods.

## in(): Check if several elements are in the list

To test if an value is in a list of elements with `in()` isn’t much different than using `in_array()`. But what if you need to do that several times with different elements? Then, `in()` takes away the burden of doing that within a loop:

```
map( ['a', 'b', 'c'] )->in( 'b' ); // true
map( ['a', 'b', 'c'] )->in( ['b', 'a'] ); // true
map( ['a', 'b', 'c'] )->in( ['d', 'a'] ); // false
```

The `in()` method of the Map object checks if all elements of the passed array are available in the list and only then it returns true.

## toJson(): Encode everything into JSON

When working with APIs, you almost always need to encode the result into its JSON representation. The PHP map object offers a convenient way to transform all elements to JSON:

```
map( ['a', 'b', ['c', 'd'] )->toJson();
```

Like the PHP `json_encode()` method, it accepts a parameter for passing options that are used during encoding the elements. There are two that you may find extremely useful:

```
map( ['a', 'b', ['c', 'd'] )->toJson( JSON_FORCE_OBJECT|JSON_HEX_QUOT );
```

`JSON_FORCE_OBJECT` always creates `{}`, even for empty lists to avoid being encoded as `[]`. If you add JSON output to HTML data attributes for storing something you need for dynamic Javascript features later on, `JSON_HEX_QUOT` saves you from generating invalid HTML.

## walk(): Visit each value in a multi-dimensional array

You receive a multi-dimensional array from an HTML form and want to make sure that all values are of a certain type or doesn’t contain HTML tags? Then, the `walk()` method of the PHP Map object is your friend:

```
map( ['a', 'b', ['c', '<b>d</b>'] )->walk( function( &$val ) {
    $val = strip_tags( $val );
} );
```

You can also create a mapping based on the list you pass as second parameter:

```
$mapping = [1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'];
 
map( [1, 2, [3, 4] )->walk( function( &$val, $key, $data ) {
    $val = $data[$val] ?? $val;
}, $mapping );
 
// Result equals
map( ['one', 'two', ['three', 'four']] )
```

## method(): Register your own methods
The best of all comes last: You can extend the PHP Map object by your own methods! This is extremely handy if you need the same code several times and different places. To register your own method, just do this:

```
\Aimeos\Map::method( 'strrev', function( $sep ) {
    return strrev( join( '-', $this->list ) );
} );
```

Your closure function has access to the internal array of elements using $this->list and you can read and/or modify that internal array of the Map object. You can use your new method the same way as any other method of the Map object afterwards:

```
map( ['a', 'b'] )->strrev( '-' );
// returns "b-a"
```

## Conclusion

The PHP Map object offers some great methods that can simplify your code drastically and you will never want to miss them any more. Especially the possibility to register own methods to extend the Map object dynamically gives your great power in a few lines of code.