Completed
Push — master ( cebd38...239e2a )
by Daniel
14:58 queued 09:14
created

Transform::unDot()   C

Complexity

Conditions 7
Paths 6

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 22
ccs 16
cts 16
cp 1
rs 6.9811
cc 7
eloc 12
nc 6
nop 2
crap 7
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 44
    public function __construct()
28
    {
29 44
        $this->access = new Access();
30 44
        $this->enumerator = new Enumerator();
31 44
    }
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
            // fix for hhvm #1871 bug
118 1
            if (defined('HHVM_VERSION')) {
119
                $combinator->next();
120
            }
121
122 1
            $index = $combinator->key();
123
124 1
            if ($overwrite || ! isset($combined[$index])) {
125 1
                $combined[$index] = $combinator->current();
126 1
            }
127 1
        }
128
129 1
        return $combined;
130
    }
131
132
    /**
133
     * Collapse a nested array down to an array of flat key=>value pairs
134
     */
135 3
    public function collapse(array $array)
136
    {
137 3
        $newArray = [];
138
139 3
        foreach ($array as $key => $value) {
140 3
            if (is_array($value)) {
141
                // strip any manually added '.'
142 3
                if (preg_match('/\./', $key)) {
143 2
                    $key = substr($key, 0, -2);
144 2
                }
145
146 3
                $this->recurseCollapse($value, $newArray, (array) $key);
147 3
            } else {
148 1
                $newArray[$key] = $value;
149
            }
150 3
        }
151
152 3
        return $newArray;
153
    }
154
155
    /**
156
     * Divide an array into two arrays. One with keys and the other with values.
157
     *
158
     * @param array $array
159
     *
160
     * @return array[]
161
     */
162 1
    public function divide($array)
163
    {
164 1
        return [array_keys($array), array_values($array)];
165
    }
166
167
    /**
168
     * Stripe all empty items.
169
     *
170
     * @param array $array
171
     *
172
     * @return array
173
     */
174 1
    public function stripEmpty(array $array)
175
    {
176
        return array_filter($array, function ($item) {
177 1
            if (is_null($item)) {
178 1
                return false;
179
            }
180
181 1
            if (! trim($item)) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return (bool) trim($item);.
Loading history...
182 1
                return false;
183
            }
184
185 1
            return true;
186 1
        });
187
    }
188
189
    /**
190
     * Remove all instances of $ignore found in $elements (=== is used).
191
     *
192
     * @param array $array
193
     * @param array $ignore
194
     *
195
     * @return array
196
     */
197 1
    public function without(array $array, array $ignore)
198
    {
199 1
        foreach ($array as $key => $node) {
200 1
            if (in_array($node, $ignore, true)) {
201 1
                unset($array[$key]);
202 1
            }
203 1
        }
204
205 1
        return array_values($array);
206
    }
207
208
    /**
209
     * Reindexes a list of values.
210
     *
211
     * @param array $array
212
     * @param array $map      An map of correspondances of the form
213
     *                        ['currentIndex' => 'newIndex'].
214
     * @param bool  $unmapped Whether or not to keep keys that are not
215
     *                        remapped.
216
     *
217
     * @return array
218
     */
219 1
    public function reindex(array $array, array $map, $unmapped = true)
220
    {
221
        $reindexed = $unmapped
222 1
            ? $array
223 1
            : [];
224
225 1
        foreach ($map as $from => $to) {
226 1
            if (isset($array[$from])) {
227 1
                $reindexed[$to] = $array[$from];
228 1
            }
229 1
        }
230
231 1
        return $reindexed;
232
    }
233
234
    /**
235
     * Merges two or more arrays into one recursively.
236
     *
237
     * @param array $arrays.
0 ignored issues
show
Documentation introduced by
There is no parameter named $arrays.. Did you maybe mean $arrays?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
238
     *
239
     * @return array
240
     */
241 4
    public function merge(array $arrays)
0 ignored issues
show
Unused Code introduced by
The parameter $arrays is not used and could be removed.

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

Loading history...
242
    {
243 4
        $args = func_get_args();
244 4
        $array = array_shift($args);
245
246 4
        while (! empty($args)) {
247 4
            $next = array_shift($args);
248
249 4
            foreach ($next as $key => $value) {
250 4
                if (is_int($key)) {
251 3
                    if (isset($array[$key])) {
252 2
                        $array[] = $value;
253 2
                    } else {
254 1
                        $array[$key] = $value;
255
                    }
256 4
                } elseif (is_array($value) && isset($array[$key]) && is_array($array[$key])) {
257 1
                    $array[$key] = $this->merge($array[$key], $value);
258 1
                } else {
259 2
                    $array[$key] = $value;
260
                }
261 4
            }
262 4
        }
263
264 4
        return $array;
265
    }
266
267
    /**
268
     *  Makes every value that is numerically indexed a key, given $default
269
     *  as value.
270
     *
271
     *  @param array $array
272
     *  @param mixed $default
273
     *
274
     *  @return array
275
     */
276 1
    public function normalize(array $array, $default)
277
    {
278 1
        $normalized = [];
279
280 1
        foreach ($array as $key => $value) {
281 1
            if (is_numeric($key)) {
282 1
                $key = $value;
283 1
                $value = $default;
284 1
            }
285
286 1
            $normalized[$key] = $value;
287 1
        }
288
289 1
        return $normalized;
290
    }
291
292
    /**
293
     * Extend one array with another.
294
     *
295
     * @param array $arrays
296
     *
297
     * @return array
298
     */
299 3
    public function extend(array $arrays)
300
    {
301 3
        $merged = [];
302
303 3
        foreach (func_get_args() as $array) {
304 3
            foreach ($array as $key => $value) {
305 3
                if (is_array($value) && $this->access->has($merged, $key) && is_array($merged[$key])) {
306 2
                    $merged[$key] = $this->extend($merged[$key], $value);
307 2
                } else {
308 3
                    $merged[$key] = $value;
309
                }
310 3
            }
311 3
        }
312
313 3
        return $merged;
314
    }
315
316
    /**
317
     * Transforms a 1-dimensional array into a multi-dimensional one,
318
     * exploding keys according to a separator.
319
     *
320
     * @param array $array
321
     *
322
     * @return array
323
     */
324 1
    public function asHierarchy(array $array)
325
    {
326 1
        $hierarchy = [];
327
328 1
        foreach ($array as $key => $value) {
329 1
            $segments = explode('.', $key);
330 1
            $valueSegment = array_pop($segments);
331 1
            $branch = &$hierarchy;
332
333 1
            foreach ($segments as $segment) {
334 1
                if (! isset($branch[$segment])) {
335 1
                    $branch[$segment] = [];
336 1
                }
337
338 1
                $branch = &$branch[$segment];
339 1
            }
340
341 1
            $branch[$valueSegment] = $value;
342 1
        }
343
344 1
        return $hierarchy;
345
    }
346
347
    /**
348
     * Separates elements from an array into groups.
349
     * The function maps an element to the key that will be used for grouping.
350
     * If no function is passed, the element itself will be used as key.
351
     *
352
     * @param array         $array
353
     * @param callable|null $callback
354
     *
355
     * @return array
356
     */
357 1
    public function groupBy(array $array, callable $callback = null)
358
    {
359
        $callback = $callback ?: function ($value) {
360 1
            return $value;
361 1
        };
362
363 1
        return array_reduce(
364 1
            $array,
365 1
            function ($buckets, $value) use ($callback) {
366 1
                $key = call_user_func($callback, $value);
367
368 1
                if (! array_key_exists($key, $buckets)) {
369 1
                    $buckets[$key] = [];
370 1
                }
371
372 1
                $buckets[$key][] = $value;
373
374 1
                return $buckets;
375 1
            },
376 1
            []
377 1
        );
378
    }
379
380
    /**
381
     * Flatten a multi-dimensional associative array with dots.
382
     *
383
     * @param array  $array
384
     * @param string $prepend
385
     *
386
     * @return array
387
     */
388 4
    public function dot($array, $prepend = '')
389
    {
390 4
        $cache = serialize(['array' => $array, 'prepend' => $prepend]);
391
392 4
        if (array_key_exists($cache, $this->dotted)) {
393 1
            return $this->dotted[$cache];
394
        }
395
396 4
        $results = [];
397
398 4 View Code Duplication
        foreach ($array as $key => $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
399 4
            if (is_array($value)) {
400 2
                $results = array_merge($results, $this->dot($value, $prepend . $key . '.'));
401 2
            } else {
402 4
                $results[$prepend . $key] = $value;
403
            }
404 4
        }
405
406 4
        return $this->dotted[$cache] = $results;
407
    }
408
409
    /**
410
     * Expand a dotted array. Acts the opposite way of Arr::dot().
411
     *
412
     * @param array $array
413
     * @param bool  $recursively
414
     *
415
     * @return array
416
     */
417 4
    public static function unDot($array, $recursively = true)
418
    {
419 4
        $results = [];
420
421 4
        foreach ($array as $key => $value) {
422 3
            if (count($dottedKeys = explode('.', $key, 2)) > 1) {
423 3
                $results[$dottedKeys[0]][$dottedKeys[1]] = $value;
424 3
            } else {
425 3
                $results[$key] = $value;
426
            }
427 4
        }
428
429 4
        if ($recursively) {
430 4
            foreach ($results as $key => $value) {
431 3
                if (is_array($value) && ! empty($value)) {
432 3
                    $results[$key] = self::unDot($value, $recursively);
433 3
                }
434 4
            }
435 4
        }
436
437 4
        return $results;
438
    }
439
440
    /**
441
     * Flatten a nested array to a separated key.
442
     *
443
     * @param array       $array
444
     * @param string|null $separator
445
     * @param string      $prepend
446
     *
447
     * @return array
448
     */
449 1
    public function flatten(array $array, $separator = null, $prepend = '')
450
    {
451 1
        $flattened = [];
452
453 1 View Code Duplication
        foreach ($array as $key => $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
454 1
            if (is_array($value)) {
455 1
                $flattened = array_merge($flattened, $this->flatten($value, $separator, $prepend . $key . $separator));
456 1
            } else {
457 1
                $flattened[$prepend . $key] = $value;
458
            }
459 1
        }
460
461 1
        return $flattened;
462
    }
463
464
    /**
465
     * Expand a flattened array with dots to a multi-dimensional associative array.
466
     *
467
     * @param array  $array
468
     * @param string $prepend
469
     *
470
     * @return array
471
     */
472 4
    public function expand(array $array, $prepend = '')
473
    {
474 4
        $results = [];
475
476 4
        if ($prepend) {
477 2
            $prepend .= '.';
478 2
        }
479
480 4
        foreach ($array as $key => $value) {
481 4
            if ($prepend) {
482 2
                $pos = strpos($key, $prepend);
483
484 2
                if ($pos === 0) {
485 1
                    $key = substr($key, strlen($prepend));
486 1
                }
487 2
            }
488
489 4
            $results = $this->access->set($results, $key, $value);
490 4
        }
491
492 4
        return $results;
493
    }
494
495
    /**
496
     * Reset all numerical indexes of an array (start from zero).
497
     * Non-numerical indexes will stay untouched. Returns a new array.
498
     *
499
     * @param array      $array
500
     * @param bool|false $deep
501
     *
502
     * @return array
503
     */
504 3
    public function reset(array $array, $deep = false)
505
    {
506 3
        $target = [];
507
508 3
        foreach ($array as $key => $value) {
509 3
            if ($deep && is_array($value)) {
510 1
                $value = $this->reset($value);
511 1
            }
512
513 3
            if (is_numeric($key)) {
514 3
                $target[] = $value;
515 3
            } else {
516 3
                $target[$key] = $value;
517
            }
518 3
        }
519
520 3
        return $target;
521
    }
522
523
    /**
524
     * Extend one array with another. Non associative arrays will not be merged
525
     * but rather replaced.
526
     *
527
     * @param array $arrays
528
     *
529
     * @return array
530
     */
531 4
    public function extendDistinct(array $arrays)
0 ignored issues
show
Unused Code introduced by
The parameter $arrays is not used and could be removed.

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

Loading history...
532
    {
533 4
        $merged = [];
534
535 4
        foreach (func_get_args() as $array) {
536 4
            foreach ($array as $key => $value) {
537 4
                if (is_array($value) && $this->access->has($merged, $key) && is_array($merged[$key])) {
538 3
                    if ($this->enumerator->isAssoc($value) && $this->enumerator->isAssoc($merged[$key])) {
539 2
                        $merged[$key] = $this->extendDistinct($merged[$key], $value);
540
541 2
                        continue;
542
                    }
543 2
                }
544
545 4
                $merged[$key] = $value;
546 4
            }
547 4
        }
548
549 4
        return $merged;
550
    }
551
552
    /**
553
     * Sort the array using the given callback.
554
     *
555
     * @param array    $array
556
     * @param callable $callback
557
     * @param int      $options
558
     * @param bool     $descending
559
     *
560
     * @return array
561
     */
562 1
    public function sort(array $array, callable $callback, $options = SORT_REGULAR, $descending = false)
563
    {
564 1
        $results = [];
565
566
        // First we will loop through the items and get the comparator from a callback
567
        // function which we were given. Then, we will sort the returned values and
568
        // and grab the corresponding values for the sorted keys from this array.
569 1
        foreach ($array as $key => $value) {
570 1
            $results[$key] = $callback($value, $key);
571 1
        }
572
573 1
        $descending ? arsort($results, $options)
574 1
                    : asort($results, $options);
575
576
        // Once we have sorted all of the keys in the array, we will loop through them
577
        // and grab the corresponding model so we can set the underlying items list
578
        // to the sorted version. Then we'll just return the collection instance.
579 1
        foreach (array_keys($results) as $key) {
580 1
            $results[$key] = $array[$key];
581 1
        }
582
583 1
        return $results;
584
    }
585
586
    /**
587
     * Recursively sort an array by keys and values.
588
     *
589
     * @param array $array
590
     *
591
     * @return array
592
     */
593 1
    public function sortRecursive(array $array)
594
    {
595 1
        foreach ($array as &$value) {
596 1
            if (is_array($value)) {
597 1
                $value = $this->sortRecursive($value);
598 1
            }
599 1
        }
600
601
        // sort associative array
602 1
        if ($this->enumerator->isAssoc($array)) {
603 1
            ksort($array);
604
            // sort regular array
605 1
        } else {
606 1
            sort($array);
607
        }
608
609 1
        return $array;
610
    }
611
612
    /**
613
     * Will turn each element in $arr into an array then appending
614
     * the associated indexs from the other arrays into this array as well.
615
     *
616
     * @param array $array
617
     * @param array $arrays
618
     *
619
     * @return array
620
     */
621 2
    public function zip(array $array, array $arrays)
0 ignored issues
show
Unused Code introduced by
The parameter $arrays is not used and could be removed.

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

Loading history...
622
    {
623 2
        $args = func_get_args();
624 2
        array_shift($args);
625
626 2
        foreach ($array as $key => $value) {
627 2
            $array[$key] = [$value];
628
629 2
            foreach ($args as $k => $v) {
630 2
                $array[$key][] = current($args[$k]);
631
632 2
                if (next($args[$k]) === false && $args[$k] !== [null]) {
633 1
                    $args[$k] = [null];
634 1
                }
635 2
            }
636 2
        }
637
638 2
        return $array;
639
    }
640
641
    /**
642
     * Recurse through an array, add the leaf items to the $newArray var
643
     *
644
     * @param array $subject
645
     * @param array &$newArray
646
     * @param array $stack
647
     *
648
     * @return string[]|null
649
     */
650 3
    private function recurseCollapse(array $subject, array &$newArray, $stack = [])
651
    {
652 3
        foreach ($subject as $key => $value) {
653 3
            $fstack = array_merge($stack, [$key]);
654
655 3
            if (is_array($value)) {
656 3
                $this->recurseCollapse($value, $newArray, $fstack);
657 3
            } else {
658 3
                $top = array_shift($fstack);
659 3
                $arrayPart = count($fstack) ? '.' . implode('.', $fstack) : '';
660 3
                $newArray[$top . $arrayPart] = $value;
661
            }
662 3
        }
663 3
    }
664
}
665