Completed
Push — master ( a92ee2...268555 )
by Dmitry
04:05
created

StructureOfElementsMethods::isEqualTo()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 7
nc 3
nop 1
dl 0
loc 14
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace PHPKitchen\Platform\Struct\Mixin;
4
5
use PHPKitchen\Platform\Struct\Collection;
6
use PHPKitchen\Platform\Contract;
7
8
/**
9
 * Represents implementation of collections methods based on arrays.
10
 *
11
 * @author Dmitry Kolodko <[email protected]>
12
 * @since 1.0
13
 */
14
trait StructureOfElementsMethods {
15
    /**
16
     * @var array collection items storage.
17
     */
18
    protected $elements;
19
20
    ///region ----------------- DATA CLEANING METHODS -----------------
21
22
    /**
23
     * Clear all data from the collection.
24
     *
25
     * @since 1.0
26
     */
27
    public function clear(): void {
28
        $this->elements = [];
29
    }
30
31
    /**
32
     * Remove first occurrence of a specified element from the collection.
33
     *
34
     * @param mixed $element element to be removed.
35
     *
36
     * @since 1.0
37
     */
38
    public function remove($element): void {
39
        if (($index = $this->keyOf($element)) !== false) {
40
            $this->removeAt($index);
41
        }
42
    }
43
44
    /**
45
     * Remove all occurrences of a specified element from the collection.
46
     *
47
     * @param mixed $element element to be removed.
48
     *
49
     * @since 1.0
50
     */
51
    public function removeAll($element): void {
52
        $indexes = $this->allKeysOf($element);
53
        foreach ($indexes as $index) {
54
            $this->removeAt($index);
55
        }
56
    }
57
58
    /**
59
     * Remove element at specified index.
60
     *
61
     * @param int|string $key element index.
62
     *
63
     * @since 1.0
64
     */
65
    public function removeAt($key): void {
66
        unset($this->elements[$key]);
67
    }
68
69
    ///endregion
70
    ///
71
    ////region ----------------- DATA MANIPULATION BY CALLABLE METHODS -----------------
72
73
    /**
74
     * Apply a user function to every element of the collection.
75
     *
76
     * @param callable $do callable that takes on two parameters.
77
     * The input parameter's value being the first, and  the key/index second.
78
     * Example:
79
     * <code>
80
     * $collection->onEach(function ($element, $key) {
81
     *     $element++; // won't change original collection
82
     * });
83
     * </code>
84
     *
85
     * If callable needs to be working with the actual values of the collection,
86
     * specify the first parameter of callable as a reference. Then, any changes made
87
     * to those elements will be made in the original collection itself.
88
     * Example:
89
     * <code>
90
     * $collection = Collection::of([1, 2, 3]);
91
     * $collection->onEach(function (&$element, $key) {
92
     *     $element++;
93
     * });
94
     * // $collection is equal to Collection::of([2, 3, 4]);
95
     * </code>
96
     *
97
     * @return bool true on success or false on failure.
98
     *
99
     * @see onEachRecursive to run callbale on each element recursively.
100
     * @since 1.0
101
     */
102
    public function onEach(callable $do): bool {
0 ignored issues
show
Coding Style introduced by
function onEach() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
103
        return array_walk($this->elements, $do);
104
    }
105
106
    /**
107
     * Apply a user function recursively to every element of the collection.
108
     *
109
     * @param callable $do callable that takes on two parameters.
110
     * The input parameter's value being the first, and  the key/index second.
111
     * Example:
112
     * <code>
113
     * $collection->onEach(function ($element, $key) {
114
     *     $element++; // won't change original collection
115
     * });
116
     * </code>
117
     *
118
     * If callable needs to be working with the actual values of the collection,
119
     * specify the first parameter of callable as a reference. Then, any changes made
120
     * to those elements will be made in the original collection itself.
121
     * Example:
122
     * <code>
123
     * $collection = Collection::of([1, 2, 3]);
124
     * $collection->onEachRecursive(function (&$element) {
125
     *     $element++;
126
     * });
127
     * // $collection is equal to Collection::of([2, 3, 4]);
128
     * </code>
129
     *
130
     * @return bool true on success or false on failure.
131
     *
132
     * @since 1.0
133
     */
134
    public function onEachRecursive(callable $do): bool {
0 ignored issues
show
Coding Style introduced by
function onEachRecursive() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
135
        return array_walk_recursive($this->elements, $do);
136
    }
137
138
    /**
139
     * Iterates over each value in the collection passing them to the callback function.
140
     * If the callback function returns `true`, the current value passed to the new collection,
141
     * if `false` value will won't be added to the new collection.
142
     * Note: keys are preserved.
143
     *
144
     * Example:
145
     * <code>
146
     * $collection = Collection::of([1, 2, 3, 3]);
147
     * // remove from the collection all elements equal to 3
148
     * $filteredCollection = $collection->filter(function ($element) {
149
     *    if ($element == 3) {
150
     *        return false;
151
     *    };
152
     *    return true;
153
     * });
154
     * // $filteredCollection is equal to Collection::of([1, 2]);
155
     * </code>
156
     *
157
     * @param callable $by user function that take collection element as
158
     * and input parameter.
159
     *
160
     * @return static new collection containing all the elements of original collection
0 ignored issues
show
Documentation introduced by
Should the return type not be \self?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
161
     * after applying the callback function to each element.
162
     *
163
     * @since 1.0
164
     */
165
    public function filter(callable $by): self {
166
        return static::of(array_filter($this->elements, $by));
167
    }
168
169
    /**
170
     * Applies the callback to the elements of the collection.
171
     * The return value of a callback function
172
     * Example:
173
     * <code>
174
     * $collection = Collection::of([1, 2, 3]);
175
     * // remove from the collection all elements equal to 3
176
     * $mappedCollection = $collection->map(function ($element) {
177
     *    return $element * $element;
178
     * });
179
     * // $mappedCollection is equal to Collection::of([1, 4, 9]);
180
     * </code>
181
     *
182
     * @param callable $by callback function to run for each element.
183
     *
184
     * @return static new collection containing all the elements of original collection
0 ignored issues
show
Documentation introduced by
Should the return type not be \self?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
185
     * after applying the callback function to each element.
186
     *
187
     * @since 1.0
188
     */
189
    public function map(callable $by): self {
190
        return static::of(array_map($by, $this->elements));
191
    }
192
193
    /**
194
     * Iteratively reduce the collection to a single value using a callback function
195
     *
196
     * Example:
197
     * <code>
198
     * $collection = Collection::of([1, 2, 3]);
199
     * // remove from the collection all elements equal to 3
200
     * $reducedCollectionValue = $collection->reduce(function ($result, $element) {
201
     *    $result += $element;
202
     *
203
     *    return $result;
204
     * });
205
     * // reducedCollectionValue is equal to `6`
206
     * </code>
207
     *
208
     * @param callback $by the callback function.
209
     * @param mixed $withInitial [optional] if the optional initial is available, it will
210
     * be used at the beginning of the process, or as a final result in case
211
     * the collection is empty.
212
     * </p
213
     * @return mixed the resulting value.
214
     * @since 1.0
215
     */
216
    public function reduce(callable $by, $withInitial = null) {
217
        return array_reduce($this->elements, $by, $withInitial);
218
    }
219
220
    ///endregion
221
    ///
222
    ///region ----------------- DATA CHECK METHODS -----------------
223
224
    public function hasKey($key): bool {
225
        return array_key_exists($key, $this->elements);
226
    }
227
228
    public function has($element): bool {
229
        return in_array($element, $this->elements);
230
    }
231
232
    public function hasSet($key): bool {
233
        return isset($this->elements[$key]);
234
    }
235
236
    ///endregion
237
    ///
238
    ///region ----------------- DATA RETRIEVE METHODS -----------------
239
240
    /**
241
     * Searches the collection for a given value and returns the corresponding key if successful.
242
     *
243
     * If element is found  more than once, the first matching key is returned. To return the keys
244
     * for all matching values, use {@link allKeysOf}.
245
     *
246
     * @param mixed $element value to look for at the collection.
247
     * @param bool $strictTypeCheck determines if strict comparison (===) should be used during the search.
248
     *
249
     * @return false|int|string key of an element or `false` if element not found.
250
     * @since 1.0
251
     */
252
    public function keyOf($element, bool $strictTypeCheck = true) {
253
        // stub
254
        return array_search($element, $this->elements, $strictTypeCheck);
255
    }
256
257
    /**
258
     * Searches the collection for a given value and returns the last corresponding key if successful.
259
     *
260
     * @param mixed $element value to look for at the collection.
261
     * @param bool $strictTypeCheck determines if strict comparison (===) should be used during the search.
262
     *
263
     * @return false|int|string key of an element or `false` if element not found.
264
     * @since 1.0
265
     */
266
    public function lastKeyOf($element, bool $strictTypeCheck = true) {
267
        $indexes = array_keys($this->elements, $element, $strictTypeCheck);
268
269
        return !empty($indexes) ? end($indexes) : false;
270
    }
271
272
    /**
273
     * Searches the collection for a given value and returns the corresponding keys if successful.
274
     *
275
     * @param mixed $element value to look for at the collection.
276
     * @param bool $strictTypeCheck determines if strict comparison (===) should be used during the search.
277
     *
278
     * @return Contract\Struct\Collection collection of keys given element obtain.
279
     */
280
    public function allKeysOf($element, bool $strictTypeCheck = true): Contract\Struct\Collection {
281
        return Collection::of(array_keys($this->elements, $element, $strictTypeCheck));
282
    }
283
284
    /**
285
     * Counts all the values of collection.
286
     *
287
     * @return Collection an associative collection of values from the collection as
288
     * keys and their count as value.
289
     *
290
     * @since 1.0
291
     */
292
    public function countValuesFrequency(): Contract\Struct\Collection {
293
        return Collection::of(array_count_values($this->elements));
294
    }
295
296
    /**
297
     * Counts all the values of collection ignoring string values case.
298
     *
299
     * @return Collection an associative collection of values from the collection as
300
     * keys and their count as value.
301
     *
302
     * @since 1.0
303
     */
304
    public function countValuesFrequencyIgnoringCase(): Contract\Struct\Collection {
305
        return Collection::of(array_count_values(array_map('strtolower', $this->elements)));
306
    }
307
308
    /**
309
     * Calculate the product of values in collection.
310
     *
311
     * @return int|float the product as an integer or float.
312
     *
313
     * @since 1.0
314
     */
315
    public function calculateProduct() {
316
        return array_product($this->elements);
317
    }
318
319
    ///endregion
320
    ///
321
    ///region ----------------- DATA MANIPULATION METHODS -----------------
322
    /**
323
     * Return last element of the collection and remove the element from the
324
     * collection.
325
     *
326
     * @return mixed first element of the collection
327
     *
328
     * @since 1.0
329
     */
330
    public function pop() {
331
        return array_pop($this->elements);
332
    }
333
334
    /**
335
     * Return first element of the collection and remove the element from the
336
     * collection.
337
     *
338
     * @return mixed first element of the collection
339
     * @since 1.0
340
     */
341
    public function shift() {
342
        return array_shift($this->elements);
343
    }
344
345
    ///endregion
346
    ///
347
    ///region ----------------- VALUE OBJECT METHODS -----------------
348
349
    public function isEmpty(): bool {
350
        return empty($this->elements);
351
    }
352
353
    public function isNull(): bool {
354
        return $this->elements === null;
355
    }
356
357
    public function isEqualTo($value): bool {
358
        if (is_array($value) && $this->elements === $value) {
359
            return true;
360
        } elseif ($value instanceof Contract\Struct\Collection || $value instanceof Contract\Struct\Map) {
361
            /**
362
             * in case of collection, need to pass current collection elements to `isEqualTo` in order
363
             * to compare elements by the first rule above
364
             * @var Contract\Data\ValueObject $value
365
             */
366
            return $value->isEqualTo($this->elements);
367
        } else {
368
            return false;
369
        }
370
    }
371
    ///endregion
372
}