Completed
Push — master ( eb5437...e70327 )
by Daniel
03:22
created

src/Arr/Transform.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
namespace Narrowspark\Arr;
3
4
class Transform
5
{
6
    /**
7
     * Dotted array cache.
8
     *
9
     * @var array
10
     */
11
    protected $dotted = [];
12
13
    /**
14
     * A instance of Access
15
     *
16
     * @var \Narrowspark\Arr\Access
17
     */
18
    protected $access;
19
20
    /**
21
     * A instance of Access
22
     *
23
     * @var \Narrowspark\Arr\Enumerator
24
     */
25
    protected $enumerator;
26
27 50
    public function __construct()
28
    {
29 50
        $this->access = new Access();
30 50
        $this->enumerator = new Enumerator();
31 50
    }
32
33
    /**
34
     * Pop value from sub array.
35
     *
36
     * @param array  $array
37
     * @param string $key
38
     *
39
     * @return mixed
40
     */
41 1
    public static function pop(array $array, $key)
42
    {
43 1
        $keys = explode('.', $key);
44
45 1
        foreach ($keys as $key) {
46 1
            if (! isset($array[$key])) {
47 1
                return;
48
            }
49
50 1
            $array = $array[$key];
51 1
        }
52
53 1
        if (! is_array($array)) {
54 1
            return;
55
        }
56
57 1
        return array_pop($array);
58
    }
59
60
    /**
61
     * Swap two elements between positions.
62
     *
63
     * @param array  $array array to swap
64
     * @param string $swapA
65
     * @param string $swapB
66
     *
67
     * @return array|null
68
     */
69 1
    public function swap(array $array, $swapA, $swapB)
70
    {
71 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
72
73 1
        return $array;
74
    }
75
76
    /**
77
     * Create a new array consisting of every n-th element.
78
     *
79
     * @param array $array
80
     * @param int   $step
81
     * @param int   $offset
82
     *
83
     * @return array
84
     */
85 1
    public static function every($array, $step, $offset = 0)
86
    {
87 1
        $new = [];
88
89 1
        $position = 0;
90
91 1
        foreach ($array as $key => $item) {
92 1
            if ($position % $step === $offset) {
93 1
                $new[] = $item;
94 1
            }
95
96 1
            ++$position;
97 1
        }
98
99 1
        return $new;
100
    }
101
102
    /**
103
     * Indexes an array depending on the values it contains.
104
     *
105
     * @param array    $array
106
     * @param callable $callback  Function to combine values.
107
     * @param bool     $overwrite Should duplicate keys be overwritten?
108
     *
109
     * @return array Indexed values.
110
     */
111 1
    public function combine(array $array, callable $callback, $overwrite = true)
112
    {
113 1
        $combined = [];
114
115 1
        foreach ($array as $key => $value) {
116 1
            $combinator = call_user_func($callback, $value, $key);
117
118
            // fix for hhvm #1871 bug
119 1
            if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.10.0', '<=')) {
120
                $combinator->next();
121
            }
122
123 1
            $index = $combinator->key();
124
125 1
            if ($overwrite || ! isset($combined[$index])) {
126 1
                $combined[$index] = $combinator->current();
127 1
            }
128 1
        }
129
130 1
        return $combined;
131
    }
132
133
    /**
134
     * Collapse a nested array down to an array of flat key=>value pairs
135
     */
136 3
    public function collapse(array $array)
137
    {
138 3
        $newArray = [];
139
140 3
        foreach ($array as $key => $value) {
141 3
            if (is_array($value)) {
142
                // strip any manually added '.'
143 3
                if (preg_match('/\./', $key)) {
144 2
                    $key = substr($key, 0, -2);
145 2
                }
146
147 3
                $this->recurseCollapse($value, $newArray, (array) $key);
148 3
            } else {
149 1
                $newArray[$key] = $value;
150
            }
151 3
        }
152
153 3
        return $newArray;
154
    }
155
156
    /**
157
     * Divide an array into two arrays. One with keys and the other with values.
158
     *
159
     * @param array $array
160
     *
161
     * @return array[]
162
     */
163 1
    public function divide($array)
164
    {
165 1
        return [array_keys($array), array_values($array)];
166
    }
167
168
    /**
169
     * Stripe all empty items.
170
     *
171
     * @param array $array
172
     *
173
     * @return array
174
     */
175 1
    public function stripEmpty(array $array)
176
    {
177
        return array_filter($array, function ($item) {
178 1
            if (is_null($item)) {
179 1
                return false;
180
            }
181
182 1
            if (! trim($item)) {
183 1
                return false;
184
            }
185
186 1
            return true;
187 1
        });
188
    }
189
190
    /**
191
     * Remove all instances of $ignore found in $elements (=== is used).
192
     *
193
     * @param array $array
194
     * @param array $ignore
195
     *
196
     * @return array
197
     */
198 1
    public function without(array $array, array $ignore)
199
    {
200 1
        foreach ($array as $key => $node) {
201 1
            if (in_array($node, $ignore, true)) {
202 1
                unset($array[$key]);
203 1
            }
204 1
        }
205
206 1
        return array_values($array);
207
    }
208
209
    /**
210
     * Reindexes a list of values.
211
     *
212
     * @param array $array
213
     * @param array $map      An map of correspondances of the form
214
     *                        ['currentIndex' => 'newIndex'].
215
     * @param bool  $unmapped Whether or not to keep keys that are not
216
     *                        remapped.
217
     *
218
     * @return array
219
     */
220 1
    public function reindex(array $array, array $map, $unmapped = true)
221
    {
222
        $reindexed = $unmapped
223 1
            ? $array
224 1
            : [];
225
226 1
        foreach ($map as $from => $to) {
227 1
            if (isset($array[$from])) {
228 1
                $reindexed[$to] = $array[$from];
229 1
            }
230 1
        }
231
232 1
        return $reindexed;
233
    }
234
235
    /**
236
     * Merges two or more arrays into one recursively.
237
     *
238
     * @param array $arrays.
239
     *
240
     * @return array
241
     */
242 4
    public function merge(array $arrays)
243
    {
244 4
        $args = func_get_args();
245 4
        $array = array_shift($args);
246
247 4
        while (! empty($args)) {
248 4
            $next = array_shift($args);
249
250 4
            foreach ($next as $key => $value) {
251 4
                if (is_int($key)) {
252 3
                    if (isset($array[$key])) {
253 2
                        $array[] = $value;
254 2
                    } else {
255 1
                        $array[$key] = $value;
256
                    }
257 4
                } elseif (is_array($value) && isset($array[$key]) && is_array($array[$key])) {
258 1
                    $array[$key] = $this->merge($array[$key], $value);
259 1
                } else {
260 2
                    $array[$key] = $value;
261
                }
262 4
            }
263 4
        }
264
265 4
        return $array;
266
    }
267
268
    /**
269
     *  Makes every value that is numerically indexed a key, given $default
270
     *  as value.
271
     *
272
     *  @param array $array
273
     *  @param mixed $default
274
     *
275
     *  @return array
276
     */
277 1
    public function normalize(array $array, $default)
278
    {
279 1
        $normalized = [];
280
281 1
        foreach ($array as $key => $value) {
282 1
            if (is_numeric($key)) {
283 1
                $key = $value;
284 1
                $value = $default;
285 1
            }
286
287 1
            $normalized[$key] = $value;
288 1
        }
289
290 1
        return $normalized;
291
    }
292
293
    /**
294
     * Extend one array with another.
295
     *
296
     * @param array $arrays
297
     *
298
     * @return array
299
     */
300 3
    public function extend(array $arrays)
301
    {
302 3
        $merged = [];
303
304 3
        foreach (func_get_args() as $array) {
305 3
            foreach ($array as $key => $value) {
306 3
                if (is_array($value) && $this->access->has($merged, $key) && is_array($merged[$key])) {
307 2
                    $merged[$key] = $this->extend($merged[$key], $value);
308 2
                } else {
309 3
                    $merged[$key] = $value;
310
                }
311 3
            }
312 3
        }
313
314 3
        return $merged;
315
    }
316
317
    /**
318
     * Transforms a 1-dimensional array into a multi-dimensional one,
319
     * exploding keys according to a separator.
320
     *
321
     * @param array $array
322
     *
323
     * @return array
324
     */
325 1
    public function asHierarchy(array $array)
326
    {
327 1
        $hierarchy = [];
328
329 1
        foreach ($array as $key => $value) {
330 1
            $segments = explode('.', $key);
331 1
            $valueSegment = array_pop($segments);
332 1
            $branch = &$hierarchy;
333
334 1
            foreach ($segments as $segment) {
335 1
                if (! isset($branch[$segment])) {
336 1
                    $branch[$segment] = [];
337 1
                }
338
339 1
                $branch = &$branch[$segment];
340 1
            }
341
342 1
            $branch[$valueSegment] = $value;
343 1
        }
344
345 1
        return $hierarchy;
346
    }
347
348
    /**
349
     * Separates elements from an array into groups.
350
     * The function maps an element to the key that will be used for grouping.
351
     * If no function is passed, the element itself will be used as key.
352
     *
353
     * @param array         $array
354
     * @param callable|null $callback
355
     *
356
     * @return array
357
     */
358 1
    public function groupBy(array $array, callable $callback = null)
359
    {
360
        $callback = $callback ?: function ($value) {
361 1
            return $value;
362 1
        };
363
364 1
        return array_reduce(
365 1
            $array,
366 1
            function ($buckets, $value) use ($callback) {
367 1
                $key = call_user_func($callback, $value);
368
369 1
                if (! array_key_exists($key, $buckets)) {
370 1
                    $buckets[$key] = [];
371 1
                }
372
373 1
                $buckets[$key][] = $value;
374
375 1
                return $buckets;
376 1
            },
377 1
            []
378 1
        );
379
    }
380
381
    /**
382
     * Flatten a multi-dimensional associative array with dots.
383
     *
384
     * @param array  $array
385
     * @param string $prepend
386
     *
387
     * @return array
388
     */
389 4
    public function dot($array, $prepend = '')
390
    {
391 4
        $cache = serialize(['array' => $array, 'prepend' => $prepend]);
392
393 4
        if (array_key_exists($cache, $this->dotted)) {
394 1
            return $this->dotted[$cache];
395
        }
396
397 4
        $results = [];
398
399 4 View Code Duplication
        foreach ($array as $key => $value) {
400 4
            if (is_array($value)) {
401 2
                $results = array_merge($results, $this->dot($value, $prepend . $key . '.'));
402 2
            } else {
403 4
                $results[$prepend . $key] = $value;
404
            }
405 4
        }
406
407 4
        return $this->dotted[$cache] = $results;
408
    }
409
410
    /**
411
     * Expand a dotted array. Acts the opposite way of Arr::dot().
412
     *
413
     * @param array $array
414
     * @param bool  $depth
415
     *
416
     * @return array
417
     */
418 10
    public function unDot($array, $depth = INF)
419
    {
420 10
        $results = [];
421
422 10
        foreach ($array as $key => $value) {
423 9
            if (count($dottedKeys = explode('.', $key, 2)) > 1) {
424 9
                $results[$dottedKeys[0]][$dottedKeys[1]] = $value;
425 9
            } else {
426 6
                $results[$key] = $value;
427
            }
428 10
        }
429
430 10
        foreach ($results as $key => $value) {
431 9
            if (is_array($value) && ! empty($value) && $depth > 1) {
432 7
                $results[$key] = $this->unDot($value, $depth - 1);
0 ignored issues
show
$depth - 1 is of type integer|double, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
433 7
            }
434 10
        }
435
436 10
        return $results;
437
    }
438
439
    /**
440
     * Flatten a nested array to a separated key.
441
     *
442
     * @param array       $array
443
     * @param string|null $separator
444
     * @param string      $prepend
445
     *
446
     * @return array
447
     */
448 1
    public function flatten(array $array, $separator = null, $prepend = '')
449
    {
450 1
        $flattened = [];
451
452 1 View Code Duplication
        foreach ($array as $key => $value) {
453 1
            if (is_array($value)) {
454 1
                $flattened = array_merge($flattened, $this->flatten($value, $separator, $prepend . $key . $separator));
455 1
            } else {
456 1
                $flattened[$prepend . $key] = $value;
457
            }
458 1
        }
459
460 1
        return $flattened;
461
    }
462
463
    /**
464
     * Expand a flattened array with dots to a multi-dimensional associative array.
465
     *
466
     * @param array  $array
467
     * @param string $prepend
468
     *
469
     * @return array
470
     */
471 4
    public function expand(array $array, $prepend = '')
472
    {
473 4
        $results = [];
474
475 4
        if ($prepend) {
476 2
            $prepend .= '.';
477 2
        }
478
479 4
        foreach ($array as $key => $value) {
480 4
            if ($prepend) {
481 2
                $pos = strpos($key, $prepend);
482
483 2
                if ($pos === 0) {
484 1
                    $key = substr($key, strlen($prepend));
485 1
                }
486 2
            }
487
488 4
            $results = $this->access->set($results, $key, $value);
489 4
        }
490
491 4
        return $results;
492
    }
493
494
    /**
495
     * Reset all numerical indexes of an array (start from zero).
496
     * Non-numerical indexes will stay untouched. Returns a new array.
497
     *
498
     * @param array      $array
499
     * @param bool|false $deep
500
     *
501
     * @return array
502
     */
503 3
    public function reset(array $array, $deep = false)
504
    {
505 3
        $target = [];
506
507 3
        foreach ($array as $key => $value) {
508 3
            if ($deep && is_array($value)) {
509 1
                $value = $this->reset($value);
510 1
            }
511
512 3
            if (is_numeric($key)) {
513 3
                $target[] = $value;
514 3
            } else {
515 3
                $target[$key] = $value;
516
            }
517 3
        }
518
519 3
        return $target;
520
    }
521
522
    /**
523
     * Extend one array with another. Non associative arrays will not be merged
524
     * but rather replaced.
525
     *
526
     * @param array $arrays
527
     *
528
     * @return array
529
     */
530 4
    public function extendDistinct(array $arrays)
531
    {
532 4
        $merged = [];
533
534 4
        foreach (func_get_args() as $array) {
535 4
            foreach ($array as $key => $value) {
536 4
                if (is_array($value) && $this->access->has($merged, $key) && is_array($merged[$key])) {
537 3
                    if ($this->enumerator->isAssoc($value) && $this->enumerator->isAssoc($merged[$key])) {
538 2
                        $merged[$key] = $this->extendDistinct($merged[$key], $value);
539
540 2
                        continue;
541
                    }
542 2
                }
543
544 4
                $merged[$key] = $value;
545 4
            }
546 4
        }
547
548 4
        return $merged;
549
    }
550
551
    /**
552
     * Sort the array using the given callback.
553
     *
554
     * @param array    $array
555
     * @param callable $callback
556
     * @param int      $options
557
     * @param bool     $descending
558
     *
559
     * @return array
560
     */
561 1
    public function sort(array $array, callable $callback, $options = SORT_REGULAR, $descending = false)
562
    {
563 1
        $results = [];
564
565
        // First we will loop through the items and get the comparator from a callback
566
        // function which we were given. Then, we will sort the returned values and
567
        // and grab the corresponding values for the sorted keys from this array.
568 1
        foreach ($array as $key => $value) {
569 1
            $results[$key] = $callback($value, $key);
570 1
        }
571
572 1
        $descending ? arsort($results, $options)
573 1
                    : asort($results, $options);
574
575
        // Once we have sorted all of the keys in the array, we will loop through them
576
        // and grab the corresponding model so we can set the underlying items list
577
        // to the sorted version. Then we'll just return the collection instance.
578 1
        foreach (array_keys($results) as $key) {
579 1
            $results[$key] = $array[$key];
580 1
        }
581
582 1
        return $results;
583
    }
584
585
    /**
586
     * Recursively sort an array by keys and values.
587
     *
588
     * @param array $array
589
     *
590
     * @return array
591
     */
592 1
    public function sortRecursive(array $array)
593
    {
594 1
        foreach ($array as &$value) {
595 1
            if (is_array($value)) {
596 1
                $value = $this->sortRecursive($value);
597 1
            }
598 1
        }
599
600
        // sort associative array
601 1
        if ($this->enumerator->isAssoc($array)) {
602 1
            ksort($array);
603
            // sort regular array
604 1
        } else {
605 1
            sort($array);
606
        }
607
608 1
        return $array;
609
    }
610
611
    /**
612
     * Will turn each element in $arr into an array then appending
613
     * the associated indexs from the other arrays into this array as well.
614
     *
615
     * @param array $array
616
     * @param array $arrays
617
     *
618
     * @return array
619
     */
620 2
    public function zip(array $array, array $arrays)
621
    {
622 2
        $args = func_get_args();
623 2
        array_shift($args);
624
625 2
        foreach ($array as $key => $value) {
626 2
            $array[$key] = [$value];
627
628 2
            foreach ($args as $k => $v) {
629 2
                $array[$key][] = current($args[$k]);
630
631 2
                if (next($args[$k]) === false && $args[$k] !== [null]) {
632 1
                    $args[$k] = [null];
633 1
                }
634 2
            }
635 2
        }
636
637 2
        return $array;
638
    }
639
640
    /**
641
     * Recurse through an array, add the leaf items to the $newArray var
642
     *
643
     * @param array $subject
644
     * @param array &$newArray
645
     * @param array $stack
646
     *
647
     * @return string[]|null
648
     */
649 3
    private function recurseCollapse(array $subject, array &$newArray, $stack = [])
650
    {
651 3
        foreach ($subject as $key => $value) {
652 3
            $fstack = array_merge($stack, [$key]);
653
654 3
            if (is_array($value)) {
655 3
                $this->recurseCollapse($value, $newArray, $fstack);
656 3
            } else {
657 3
                $top = array_shift($fstack);
658 3
                $arrayPart = count($fstack) ? '.' . implode('.', $fstack) : '';
659 3
                $newArray[$top . $arrayPart] = $value;
660
            }
661 3
        }
662 3
    }
663
}
664