Structure
equip/structure is an optional library for using immutable data structures. It can be used with Equip or as a stand alone dependency.
Dictionary
is an implementation of a associative array that
stores values identified by a key. Only associative arrays can be used to
initialize the structure. Any value can be defined by a string key.
SortedDictionary
is an implementation of a associative array
that also sorts the array. When the dictionary is modified it will be sorted.
By default the asort
function is used.
OrderedList
is an implementation of a list that stores ordered
values. Only an indexed array can be used to initialize the structure. Any value
can be added. When the list is modified it will be sorted. By default the
sort
function will be used.
UnorderedList
is an implementation of a list that stores
unordered values. The same value may appear more than once. Only an indexed array
can be used to initialize the structure. Any value can be added.
Set
is an implementation of a set that stores a unique values.
The same value will not appear more than once. Only an indexed array can be used
to initialize the structure. Adding an existing value to the set will have no effect.
A value can also be inserted into a set before or after an existing value.
Common Functionality
These structures can be used directly or customized for specific scenarios. Structures
can be combined in various ways for more complex structures. All structures implement
the following interfaces: ArrayAccess
, Countable
,
Iterator
, JsonSerializable
,
and Serializable
.
All of these interfaces are required by the StructureInterface
that all structures implement. This interface provides two additional methods:
toArray
can be used to convert any structure to an array.isSimilar
can be used to check if two structures are of the same type.
Each structure also has its own distinct interface but all structures have the following methods, with variations in signature:
withValues
copy the current structure with new values.withValue
copy the current structure with one new value.withoutValue
copy the current structure with one value removed.hasValue
check if a value exists.getValue
get a single value.
This makes all structures familiar and easy to work with.
Usage
Dictionary
Dictionaries can be created from associative arrays:
use Equip\Structure\Dictionary;
$dict = new Dictionary([
'color' => 'yellow',
]);
$dict->hasValue('color'); // true
$dict->hasValue('speed'); // false
And modified with associative arrays:
$dict = $dict->withValues([
'speed' => 'fast',
]);
$dict->hasValue('color'); // false
$dict->hasValue('speed'); // true
Values are added by defining a key:
$dict = $dict->withValue('color', 'red');
$dict->hasValue('color'); // true
$dict->hasValue('speed'); // true
And removed by the same key:
$dict = $dict->withoutValue('speed');
$dict->hasValue('color'); // true
$dict->hasValue('speed'); // false
Lists
Lists can be created from non-associative arrays:
use Equip\Structure\UnorderedList;
$list = new UnorderedList([
'eggs',
]);
$list->hasValue('eggs'); // true
$list->hasValue('spinach'); // false
And modified with other indexed arrays:
$list = $list->withValues([
'spinach',
]);
$list->hasValue('eggs'); // false
$list->hasValue('spinach'); // true
Values can be added:
$list = $list->withValue('eggs');
$list->hasValue('eggs'); // true
$list->hasValue('spinach'); // true
And removed:
$list = $list->withoutValue('spinach');
$list->hasValue('eggs'); // true
$list->hasValue('spinach'); // false
Ordered List
The primary difference between an OrderedList
and UnorderedList
is that an
ordered list will always be sorted as new values are added:
use Equip\Structure\OrderedList;
$list = new OrderedList([
'rice',
'beans',
'corn',
'wheat',
'oats',
]);
print_r($list->toArray()); // beans, corn, oats, rice, wheat
A custom ordering method can be defined by extending the OrderedList
class and
changing the sortValues
method:
use Equip\Structure\OrderedList;
class UserList extends OrderedList
{
protected function sortValues()
{
// Sort by user last name, then first name
usort($this->values, static function ($a, $b) {
$sort = strcmp($a['last_name'], $b['last_name']);
if ($sort === 0) {
$sort = strcmp($a['first_name'], $b['first_name']);
}
return $sort;
});
}
}
Now you can use your custom list to automatically sort users by first and last name when populated:
$users = new UserList([
[
'first_name' => 'John',
'last_name' => 'Smith',
],
[
'first_name' => 'Margie',
'last_name' => 'Vos',
],
[
'first_name' => 'John',
'last_name' => 'Abrams',
],
[
'first_name' => 'Sally',
'last_name' => 'Smith',
],
]);
print_r($users->toArray());
Set
Sets can be created from non-associative arrays:
use Equip\Structure\Set;
$set = new UnorderedList([
'eggs',
]);
$set->hasValue('eggs'); // true
$set->hasValue('spinach'); // false
And modified with other indexed arrays:
$set = $set->withValues([
'spinach',
]);
$set->hasValue('eggs'); // false
$set->hasValue('spinach'); // true
Values can be added:
$set = $set->withValue('eggs');
$set->hasValue('eggs'); // true
$set->hasValue('spinach'); // true
And removed:
$set = $set->withoutValue('spinach');
$set->hasValue('eggs'); // true
$set->hasValue('spinach'); // false
Differences from Lists
So far, all of these examples are no different than lists. The real difference with sets comes into play when you add values that already exist in the set. This will result in the same set being returned without modification:
$set = $set->withValue('spinach');
print_r($set->toArray()); // eggs, spinach
$copy = $set->withValue('spinach');
print_r($copy->toArray()); // eggs, spinach
print_r($copy === $set); // true
Sets are particularly useful in situations where you have a number of objects and want to ensure that no duplicates are included.
Inserting Values at Specific Locations
Another difference is that sets have the ability to insert values before or after existing values. This can be useful when the order of the values matters, or when using a set as a dependency chain.
$set = $set->withValueBefore('tomato', 'spinach');
print_r($set->toArray()); // eggs, tomato, spinach
$set = $set->withValueAfter('cheese', 'eggs');
print_r($set->toArray()); // eggs, cheese, tomato, spinach
Note: When inserting before a value that does not exist, the new value will be prepended to the set. When inserting after a value that does not exist, the value will be appended to the set.