Skip to content

Latest commit

 

History

History
176 lines (130 loc) · 6.35 KB

arrays.md

File metadata and controls

176 lines (130 loc) · 6.35 KB

Arrays

Arrays in PHP are combination of traditional lists and dictionaries and therefore have a wide range of applications. Because they are so common, there are a lot of repeating operations for manipulation of these data structures. These operations should be abstracted away into function, so that they can be reused and the code contains only essential business logic.

Simple finding of a value by a custom rule ("find values shorter than five characters") can be cumbersome:

<?php

$haystack = ['lorem', 'ipsum', 'dolor', 'sit', 'amet'];
foreach ($haystack as $value) {
	if (strlen($value) < 5) {
		// do something with value
	}
}

In the previous example, there is logic of going through the array mixed with business logic. This can be improved by using a dedicated method for iteration. Then only the business logic remains in the callback:

<?php

use Consistence\Type\ArrayType\ArrayType;

$haystack = ['lorem', 'ipsum', 'dolor', 'sit', 'amet'];
$value = ArrayType::findValueByCallback($haystack, function(string $value) {
	return strlen($value) < 5;
});
if ($value !== null) {
	// do something with value
}

PHP has a lot of array-manipulation functions, but using them is difficult, because they do not have unified API and never throw any Exceptions, errors are reported only through return values. These functions also use non-strict comparisons while searching by default. This is all in contradiction to the basic Consistence rules.

ArrayType contains the most used operations and always offers several variants of the methods to suit your needs.

Consistent API

PHP's function argument differs from function to function, complicating both writing and readability. In Consistence's ArrayType the first parameter is always the array which will be acted upon.

Strict by default

PHP's functions use non-strict comparisons by default, which can lead to unexpected results:

<?php

$haystack = ['1', '2', '3'];
$needle = true;
var_dump(array_search($needle, $haystack)); // 0 -> found '1'

Searching for true will also find '1' because it is "trueish" by PHP's type juggling. You can always add third parameter to use strict searching with array_search, but remembering to write it everywhere is not comfortable, is error prone and less readable (when using boolean parameters it is not obvious what is the parameter from the function call):

<?php

$haystack = ['1', '2', '3'];
$needle = true;
var_dump(array_search($needle, $haystack, true)); // false -> not found

With Consistence, the comparison is always strict by default:

<?php

use Consistence\Type\ArrayType\ArrayType;

$haystack = ['1', '2', '3'];
$needle = true;
var_dump(ArrayType::findKey($haystack, $needle)); // null -> not found

Finding vs getting

Consistence follows naming pattern for searching, computing, etc. methods:

  • methods starting with get must always return the expected value and if it is not found / cannot be computed etc., or throw an Exception (this does not include "getters" on value objects)
  • methods starting with find return the expected value and if the value is not available, ten return null

Advantage of this naming pattern is that you can see from the method call if you need to handle the return value in a special way.

When working with arrays, where there is possibility, that a key or value which you are expecting is not present, you should always check if they are present before using them. This means checking this with ifs and throwing an error, which is exactly what you can achieve with getValue and getKey (or their modifications):

<?php

$haystack = [1, 2, 3];
if (!isset($haystack['foo'])) {
	throw new \Exception('Missing key foo');
}
var_dump($haystack['foo']);

can be transformed to:

<?php

use Consistence\Type\ArrayType\ArrayType;

$haystack = [1, 2, 3];
var_dump(ArrayType::getValue($haystack, 'foo')); // \Consistence\Type\ArrayType\ElementDoesNotExistException

This is again decoupling cumbersome data manipulation logic and leaving only important business logic.

KeyValuePair

Callback operations in PHP usually work only with array values, but sometimes it is needed to perform the operation using keys or both the combination of key and value. For example array_filter uses third argument to influence this by flags, but it is again not very clear interface.

Consistence brings KeyValuePair which is used in callbacks to operate on both and be clear about the arguments given. With ArrayType::mapByCallback() you can map both keys and values:

<?php

use Consistence\Type\ArrayType\ArrayType;
use Consistence\Type\ArrayType\KeyValuePair;

$haystack = [
	1 => 1,
	2 => 2,
	3 => 3,
];
var_dump(ArrayType::mapByCallback($haystack, function (KeyValuePair $pair): KeyValuePair {
	return new KeyValuePair(
		2 * $pair->getKey(),
		3 * $pair->getValue()
	);
}));
/*
[
	2 => 3,
	4 => 6,
	6 => 9,
]
*/

To avoid creating large number of objects you can use KeyValuePairMutable which will change the given instance:

<?php

use Consistence\Type\ArrayType\ArrayType;
use Consistence\Type\ArrayType\KeyValuePairMutable;

$haystack = [
	1 => 1,
	2 => 2,
	3 => 3,
];
var_dump(ArrayType::mapByCallback($haystack, function (KeyValuePairMutable $pair): KeyValuePairMutable {
	$pair->setPair(
		2 * $pair->getKey(),
		3 * $pair->getValue()
	);
	return $pair;
}));
/*
[
	2 => 3,
	4 => 6,
	6 => 9,
]
*/

KeyValuePair is also used in ArrayType::findByCallback() and ArrayType::getByCallback() which will return the pair so that you can work with it further, if you need both values in your algorithm. You can of course also use KeyValuePair as a value object in your algorithms to represent key and value in a single.