UnderscoreCollection::reject()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 1
eloc 2
c 2
b 0
f 1
nc 1
nop 1
dl 0
loc 5
rs 10
1
<?php
2
3
/*
4
 * This file is part of the PHP-UNDERSCORE package.
5
 *
6
 * (c) Jitendra Adhikari <[email protected]>
7
 *     <https://github.com/adhocore>
8
 *
9
 * Licensed under MIT license.
10
 */
11
12
namespace Ahc\Underscore;
13
14
class UnderscoreCollection extends UnderscoreBase
15
{
16
    /**
17
     * Apply given callback to each of the items in collection.
18
     *
19
     * @param callable $fn The callback.
20
     *
21
     * @return self
22
     */
23
    public function each(callable $fn)
24
    {
25
        foreach ($this->data as $index => $value) {
26
            $fn($value, $index);
27
        }
28
29
        return $this;
30
    }
31
32
    /**
33
     * Update the value of each items with the result of given callback.
34
     *
35
     * @param callable $fn The callback.
36
     *
37
     * @return self
38
     */
39
    public function map(callable $fn)
40
    {
41
        $data = [];
42
43
        foreach ($this->data as $index => $value) {
44
            $data[$index] = $fn($value, $index);
45
        }
46
47
        return new static($data);
48
    }
49
50
    /**
51
     * Iteratively reduce the array to a single value using a callback function.
52
     *
53
     * @param callable $fn   The callback.
54
     * @param mixed    $memo The initial value carried over to each iteration and returned finally.
55
     *
56
     * @return mixed
57
     */
58
    public function reduce(callable $fn, $memo)
59
    {
60
        return \array_reduce($this->data, $fn, $memo);
61
    }
62
63
    /**
64
     * Same as reduce but applies the callback from right most item first.
65
     *
66
     * @param callable $fn   The callback.
67
     * @param mixed    $memo The initial value carried over to each iteration and returned finally.
68
     *
69
     * @return mixed
70
     */
71
    public function reduceRight(callable $fn, $memo)
72
    {
73
        return \array_reduce(\array_reverse($this->data, true), $fn, $memo);
74
    }
75
76
    /**
77
     * Find the first item (or index) that passes given truth test.
78
     *
79
     * @param callable $fn       The truth test callback.
80
     * @param bool     $useValue Whether to return value or the index on match.
81
     *
82
     * @return mixed|null
83
     */
84
    public function find(callable $fn, $useValue = true)
85
    {
86
        foreach ($this->data as $index => $value) {
87
            if ($fn($value, $index)) {
88
                return $useValue ? $value : $index;
89
            }
90
        }
91
    }
92
93
    /**
94
     * Find and return all the items that passes given truth test.
95
     *
96
     * @param callable|string|null $fn The truth test callback.
97
     *
98
     * @return self
99
     */
100
    public function filter($fn = null)
101
    {
102
        if (null === $fn) {
103
            return new static(\array_filter($this->data));
104
        }
105
106
        $data = \array_filter($this->data, $fn, \ARRAY_FILTER_USE_BOTH);
107
108
        return new static($data);
109
    }
110
111
    /**
112
     * Find and return all the items that fails given truth test.
113
     *
114
     * @param callable $fn The truth test callback.
115
     *
116
     * @return self
117
     */
118
    public function reject(callable $fn)
119
    {
120
        $data = \array_filter($this->data, $this->negate($fn), \ARRAY_FILTER_USE_BOTH);
121
122
        return new static($data);
123
    }
124
125
    /**
126
     * Tests if all the items pass given truth test.
127
     *
128
     * @param callable $fn The truth test callback.
129
     *
130
     * @return bool
131
     */
132
    public function every(callable $fn)
133
    {
134
        return $this->match($fn, true);
135
    }
136
137
    /**
138
     * Tests if some (at least one) of the items pass given truth test.
139
     *
140
     * @param callable $fn The truth test callback.
141
     *
142
     * @return bool
143
     */
144
    public function some(callable $fn)
145
    {
146
        return $this->match($fn, false);
147
    }
148
149
    /**
150
     * Check if the items match with given truth test.
151
     *
152
     * @internal Used by every() and some().
153
     *
154
     * @param callable $fn  The truth test callback.
155
     * @param bool     $all All or one?
156
     *
157
     * @return bool
158
     */
159
    protected function match(callable $fn, $all = true)
160
    {
161
        foreach ($this->data as $index => $value) {
162
            if ($all ^ $fn($value, $index)) {
163
                return !$all;
164
            }
165
        }
166
167
        return $all;
168
    }
169
170
    /**
171
     * Check if the collection contains given item.
172
     *
173
     * @param mixed $item
174
     *
175
     * @return bool
176
     */
177
    public function contains($item)
178
    {
179
        return \in_array($item, $this->data);
180
    }
181
182
    /**
183
     * Invoke a callback using all of the items as arguments.
184
     *
185
     * @param callable $fn The callback.
186
     *
187
     * @return mixed Whatever the callback yields.
188
     */
189
    public function invoke(callable $fn)
190
    {
191
        return \call_user_func_array($fn, $this->data);
192
    }
193
194
    /**
195
     * Pluck given property from each of the items.
196
     *
197
     * @param string|int $columnKey
198
     * @param string|int $indexKey
199
     *
200
     * @return self
201
     */
202
    public function pluck($columnKey, $indexKey = null)
203
    {
204
        $data = \array_column($this->data, $columnKey, $indexKey);
205
206
        return new static($data);
207
    }
208
209
    /**
210
     * Filter only the items that contain all the given props (matching both index and value).
211
     *
212
     * @param array $props
213
     *
214
     * @return self
215
     */
216
    public function where(array $props)
217
    {
218
        return $this->filter($this->matcher($props));
219
    }
220
221
    /**
222
     * Get the first item that contains all the given props (matching both index and value).
223
     *
224
     * @param array $props
225
     *
226
     * @return mixed
227
     */
228
    public function findWhere(array $props)
229
    {
230
        return $this->find($this->matcher($props));
231
    }
232
233
    /**
234
     * Gives props matcher callback used by where() and findWhere().
235
     *
236
     * @internal
237
     *
238
     * @param array $props Key value pairs.
239
     *
240
     * @return callable
241
     */
242
    protected function matcher(array $props)
243
    {
244
        return function ($value, $index) use ($props) {
0 ignored issues
show
Unused Code introduced by
The parameter $index is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

244
        return function ($value, /** @scrutinizer ignore-unused */ $index) use ($props) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
245
            foreach ($props as $prop => $criteria) {
246
                if (\array_column([$value], $prop) != [$criteria]) {
247
                    return false;
248
                }
249
            }
250
251
            return true;
252
        };
253
    }
254
255
    /**
256
     * Find the maximum value using given callback or just items.
257
     *
258
     * @param callable|string|null $fn The callback. String is resolved to value of that index.
259
     *
260
     * @return mixed
261
     */
262
    public function max($fn = null)
263
    {
264
        return $this->maxMin($fn, true);
265
    }
266
267
    /**
268
     * Find the minimum value using given callback or just items.
269
     *
270
     * @param callable|string|null $fn The callback. String is resolved to value of that index.
271
     *
272
     * @return mixed
273
     */
274
    public function min($fn = null)
275
    {
276
        return $this->maxMin($fn, false);
277
    }
278
279
    /**
280
     * The max/min value retriever used by max() and min().
281
     *
282
     * @internal
283
     *
284
     * @param callable|string|null $fn    The reducer callback.
285
     * @param bool                 $isMax
286
     *
287
     * @return mixed
288
     */
289
    protected function maxMin($fn = null, $isMax = true)
290
    {
291
        $fn = $this->valueFn($fn);
292
293
        return $this->reduce(function ($carry, $value) use ($fn, $isMax) {
294
            $value = $fn($value);
295
296
            if (!\is_numeric($value)) {
297
                return $carry;
298
            }
299
300
            return null === $carry
301
                ? $value
302
                : ($isMax ? \max($carry, $value) : \min($carry, $value));
303
        }, null);
304
    }
305
306
    /**
307
     * Randomize the items keeping the indexes intact.
308
     *
309
     * @return self
310
     */
311
    public function shuffle()
312
    {
313
        $data = [];
314
        $keys = \array_keys($this->data);
315
316
        shuffle($keys);
317
318
        foreach ($keys as $index) {
319
            $data[$index] = $this->data[$index];
320
        }
321
322
        return new static($data);
323
    }
324
325
    /**
326
     * Get upto n items in random order.
327
     *
328
     * @param int $n Number of items to include.
329
     *
330
     * @return self
331
     */
332
    public function sample($n = 1)
333
    {
334
        $shuffled = $this->shuffle()->get();
335
336
        return new static(\array_slice($shuffled, 0, $n, true));
337
    }
338
339
    /**
340
     * Sort items by given callback and maintain indexes.
341
     *
342
     * @param callable $fn The callback. Use null to sort based only on values.
343
     *
344
     * @return self
345
     */
346
    public function sortBy($fn)
347
    {
348
        $data = $this->map($this->valueFn($fn))->get();
349
350
        \asort($data); // Keep keys.
351
352
        foreach ($data as $index => $value) {
353
            $data[$index] = $this->data[$index];
354
        }
355
356
        return new static($data);
357
    }
358
359
    /**
360
     * Group items by using the result of callback as index. The items in group will have original index intact.
361
     *
362
     * @param callable|string $fn The callback. String is resolved to value of that index.
363
     *
364
     * @return self
365
     */
366
    public function groupBy($fn)
367
    {
368
        return $this->group($fn, true);
369
    }
370
371
    /**
372
     * Reindex items by using the result of callback as new index.
373
     *
374
     * @param callable|string $fn The callback. String is resolved to value of that index.
375
     *
376
     * @return self
377
     */
378
    public function indexBy($fn)
379
    {
380
        return $this->group($fn, false);
381
    }
382
383
    /**
384
     * Count items in each group indexed by the result of callback.
385
     *
386
     * @param callable|string $fn The callback. String is resolved to value of that index.
387
     *
388
     * @return self
389
     */
390
    public function countBy($fn)
391
    {
392
        return $this->group($fn, true)->map(function ($value) {
393
            return \count($value);
394
        });
395
    }
396
397
    /**
398
     * Group/index items by using the result of given callback.
399
     *
400
     * @internal
401
     *
402
     * @param callable|string $fn
403
     * @param bool            $isGroup
404
     *
405
     * @return self
406
     */
407
    protected function group($fn, $isGroup = true)
408
    {
409
        $data = [];
410
        $fn   = $this->valueFn($fn);
411
412
        foreach ($this->data as $index => $value) {
413
            $isGroup ? $data[$fn($value, $index)][$index] = $value : $data[$fn($value, $index)] = $value;
414
        }
415
416
        return new static($data);
417
    }
418
419
    /**
420
     * Separate the items into two groups: one passing given truth test and other failing.
421
     *
422
     * @param callable|string $fn
423
     *
424
     * @return self
425
     */
426
    public function partition($fn)
427
    {
428
        $data = [[/* pass */], [/* fail */]];
429
        $fn   = $this->valueFn($fn);
430
431
        $this->each(function ($value, $index) use ($fn, &$data) {
432
            $data[$fn($value, $index) ? 0 : 1][] = $value;
433
        });
434
435
        return new static($data);
436
    }
437
}
438