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 { |
|
|
|
|
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 { |
|
|
|
|
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 |
|
|
|
|
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 |
|
|
|
|
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
|
|
|
} |
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.