Completed
Push — master ( 0e17ed...515e0a )
by Daniel
03:08
created

Transform::sort()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 23
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 23
rs 8.7972
cc 4
eloc 9
nc 8
nop 4
1
<?php
2
namespace Narrowspark\Arr;
3
4
use Closure;
5
use Narrowspark\Arr\Traits\SplitPathTrait;
6
use Narrowspark\Arr\Traits\ValueTrait;
7
8
class Transform
9
{
10
    /**
11
     * Dotted array cache.
12
     *
13
     * @var array
14
     */
15
    protected $dotted = [];
16
17
    /**
18
     * A instance of Access
19
     *
20
     * @var \Narrowspark\Arr\Access
21
     */
22
    protected $access;
23
24
    /**
25
     * A instance of Access
26
     *
27
     * @var \Narrowspark\Arr\Enumerator
28
     */
29
    protected $enumerator;
30
31
    public function __construct()
32
    {
33
        $this->access     = new Access();
34
        $this->enumerator = new Enumerator();
35
    }
36
37
    /**
38
     * Pop value from sub array.
39
     *
40
     * @param array  $array
41
     * @param string $key
42
     *
43
     * @return mixed
44
     */
45
    public static function pop(array $array, $key)
46
    {
47
        $keys = explode('.', $key);
48
49
        foreach ($keys as $key) {
50
            if (!isset($array[$key])) {
51
                return;
52
            }
53
54
            $array = $array[$key];
55
        }
56
57
        if (!is_array($array)) {
58
            return;
59
        }
60
61
        return array_pop($array);
62
    }
63
64
    /**
65
     * Swap two elements between positions.
66
     *
67
     * @param array  $array array to swap
68
     * @param string $swapA
69
     * @param string $swapB
70
     *
71
     * @return array|null
72
     */
73
    public function swap(array $array, $swapA, $swapB)
74
    {
75
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
76
77
        return $array;
78
    }
79
80
    /**
81
     * Create a new array consisting of every n-th element.
82
     *
83
     * @param array $array
84
     * @param int   $step
85
     * @param int   $offset
86
     *
87
     * @return array
88
     */
89
    public static function every($array, $step, $offset = 0)
90
    {
91
        $new = [];
92
93
        $position = 0;
94
95
        foreach ($array as $key => $item) {
96
            if ($position % $step === $offset) {
97
                $new[] = $item;
98
            }
99
100
            $position++;
101
        }
102
103
        return $new;
104
    }
105
106
    /**
107
     * Indexes an array depending on the values it contains.
108
     *
109
     * @param array    $array
110
     * @param callable $callback  Function to combine values.
111
     * @param bool     $overwrite Should duplicate keys be overwritten?
112
     *
113
     * @return array Indexed values.
114
     */
115
    public function combine(array $array, callable $callback, $overwrite = true)
116
    {
117
        $combined = [];
118
119
        foreach ($array as $key => $value) {
120
            $combinator = call_user_func($callback, $value, $key);
121
            $index      = $combinator->key();
122
123
            if ($overwrite || !isset($combined[$index])) {
124
                $combined[$index] = $combinator->current();
125
            }
126
        }
127
128
        return $combined;
129
    }
130
131
    /**
132
     * Collapse a nested array down to an array of flat key=>value pairs
133
     */
134
    public function collapse(array $array)
135
    {
136
        $newArray = [];
137
138
        foreach ($array as $key => $value) {
139
            if (is_array($value)) {
140
                // strip any manually added '.'
141
                if (preg_match('/\./', $key)) {
142
                    $key = substr($key, 0, -2);
143
                }
144
145
                $this->recurseCollapse($value, $newArray, (array) $key);
146
            } else {
147
                $newArray[$key] = $value;
148
            }
149
        }
150
151
        return $newArray;
152
    }
153
154
    /**
155
     * Divide an array into two arrays. One with keys and the other with values.
156
     *
157
     * @param array $array
158
     *
159
     * @return array[]
160
     */
161
    public function divide($array)
162
    {
163
        return [array_keys($array), array_values($array)];
164
    }
165
166
    /**
167
     * Stripe all empty items.
168
     *
169
     * @param array $source
0 ignored issues
show
Bug introduced by
There is no parameter named $source. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

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

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

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

Loading history...
170
     *
171
     * @return array
172
     */
173
    public function stripEmpty(array $array)
174
    {
175
        return array_filter($array, function ($item) {
176
            if (is_null($item)) {
177
                return false;
178
            }
179
180
            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...
181
                return false;
182
            }
183
184
            return true;
185
        });
186
    }
187
188
    /**
189
     * Remove all instances of $ignore found in $elements (=== is used).
190
     *
191
     * @param array $elements
0 ignored issues
show
Bug introduced by
There is no parameter named $elements. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

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

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

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

Loading history...
192
     * @param array $ignore
193
     *
194
     * @return array
195
     */
196
    public function without(array $array, array $ignore)
197
    {
198
        foreach ($array as $key => $node) {
199
            if (in_array($node, $ignore, true)) {
200
                unset($array[$key]);
201
            }
202
        }
203
204
        return array_values($array);
205
    }
206
207
    /**
208
     * Reindexes a list of values.
209
     *
210
     * @param array $array
211
     * @param array $map      An map of correspondances of the form
212
     *                        ['currentIndex' => 'newIndex'].
213
     * @param bool  $unmapped Whether or not to keep keys that are not
214
     *                        remapped.
215
     *
216
     * @return array
217
     */
218
    public function reindex(array $array, array $map, $unmapped = true)
219
    {
220
        $reindexed = $unmapped
221
            ? $array
222
            : [];
223
224
        foreach ($map as $from => $to) {
225
            if (isset($array[$from])) {
226
                $reindexed[$to] = $array[$from];
227
            }
228
        }
229
230
        return $reindexed;
231
    }
232
233
    /**
234
     * Merges two or more arrays into one recursively.
235
     *
236
     * @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...
237
     *
238
     * @return array
239
     */
240
    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...
241
    {
242
        $args  = func_get_args();
243
        $array = array_shift($args);
244
245
        while (!empty($args)) {
246
            $next = array_shift($args);
247
248
            foreach ($next as $key => $value) {
249
                if (is_int($key)) {
250
                    $array[] = $value;
251
                } elseif (is_array($value) && isset($array[$key]) && is_array($array[$key])) {
252
                    $array[$key] = $this->merge($array[$key], $value);
253
                } else {
254
                    $array[$key] = $value;
255
                }
256
            }
257
        }
258
259
        return $array;
260
    }
261
262
    /**
263
     *  Makes every value that is numerically indexed a key, given $default
264
     *  as value.
265
     *
266
     *  @param array $array
267
     *  @param mixed $default
268
     *
269
     *  @return array
270
     */
271
    public function normalize(array $array, $default)
272
    {
273
        $normalized = [];
274
275
        foreach ($array as $key => $value) {
276
            if (is_numeric($key)) {
277
                $key   = $value;
278
                $value = $default;
279
            }
280
281
            $normalized[$key] = $value;
282
        }
283
284
        return $normalized;
285
    }
286
287
    /**
288
     * Extend one array with another.
289
     *
290
     * @param array $arrays
291
     *
292
     * @return array
293
     */
294
    public function extend(array $arrays)
295
    {
296
        $merged = [];
297
298
        foreach (func_get_args() as $array) {
299
            foreach ($array as $key => $value) {
300
                if (is_array($value) && $this->access->has($merged, $key) && is_array($merged[$key])) {
301
                    $merged[$key] = $this->extend($merged[$key], $value);
302
                } else {
303
                    $merged[$key] = $value;
304
                }
305
            }
306
        }
307
308
        return $merged;
309
    }
310
311
    /**
312
     * Transforms a 1-dimensional array into a multi-dimensional one,
313
     * exploding keys according to a separator.
314
     *
315
     * @param array $array
316
     *
317
     * @return array
318
     */
319
    public function asHierarchy(array $array)
320
    {
321
        $hierarchy = [];
322
323
        foreach ($array as $key => $value) {
324
            $segments     = explode('.', $key);
325
            $valueSegment = array_pop($segments);
326
            $branch       = &$hierarchy;
327
328
            foreach ($segments as $segment) {
329
                if (!isset($branch[$segment])) {
330
                    $branch[$segment] = [];
331
                }
332
333
                $branch = &$branch[$segment];
334
            }
335
336
            $branch[$valueSegment] = $value;
337
        }
338
339
        return $hierarchy;
340
    }
341
342
    /**
343
     * Separates elements from an array into groups.
344
     * The function maps an element to the key that will be used for grouping.
345
     * If no function is passed, the element itself will be used as key.
346
     *
347
     * @param array         $array
348
     * @param callable|null $callback
349
     *
350
     * @return array
351
     */
352
    public function groupBy(array $array, callable $callback = null)
353
    {
354
        $callback = $callback ?: function ($value) {
355
            return $value;
356
        };
357
358
        return array_reduce(
359
            $array,
360
            function ($buckets, $value) use ($callback) {
361
                $key = call_user_func($callback, $value);
362
363
                if (!array_key_exists($key, $buckets)) {
364
                    $buckets[$key] = [];
365
                }
366
367
                $buckets[$key][] = $value;
368
369
                return $buckets;
370
            },
371
            []
372
        );
373
    }
374
375
    /**
376
     * Flatten a multi-dimensional associative array with dots.
377
     *
378
     * @param array  $array
379
     * @param string $prepend
380
     *
381
     * @return array
382
     */
383
    public function dot($array, $prepend = '')
384
    {
385
        $cache = serialize(['array' => $array, 'prepend' => $prepend]);
386
387
        if (array_key_exists($cache, $this->dotted)) {
388
            return $this->dotted[$cache];
389
        }
390
391
        $results = [];
392
393 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...
394
            if (is_array($value)) {
395
                $results = array_merge($results, $this->dot($value, $prepend . $key . '.'));
396
            } else {
397
                $results[$prepend . $key] = $value;
398
            }
399
        }
400
401
        return $this->dotted[$cache] = $results;
402
    }
403
404
    /**
405
     * Flatten a nested array to a separated key.
406
     *
407
     * @param array       $array
408
     * @param string|null $separator
409
     * @param string      $prepend
410
     *
411
     * @return array
412
     */
413
    public function flatten(array $array, $separator = null, $prepend = '')
414
    {
415
        $flattened = [];
416
417 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...
418
            if (is_array($value)) {
419
                $flattened = array_merge($flattened, $this->flatten($value, $separator, $prepend . $key . $separator));
420
            } else {
421
                $flattened[$prepend . $key] = $value;
422
            }
423
        }
424
425
        return $flattened;
426
    }
427
428
    /**
429
     * Expand a flattened array with dots to a multi-dimensional associative array.
430
     *
431
     * @param array  $array
432
     * @param string $prepend
433
     *
434
     * @return array
435
     */
436
    public function expand(array $array, $prepend = '')
437
    {
438
        $results = [];
439
440
        if ($prepend) {
441
            $prepend .= '.';
442
        }
443
444
        foreach ($array as $key => $value) {
445
            if ($prepend) {
446
                $pos = strpos($key, $prepend);
447
448
                if ($pos === 0) {
449
                    $key = substr($key, strlen($prepend));
450
                }
451
            }
452
453
            $results = $this->access->set($results, $key, $value);
454
        }
455
456
        return $results;
457
    }
458
459
    /**
460
     * Reset all numerical indexes of an array (start from zero).
461
     * Non-numerical indexes will stay untouched. Returns a new array.
462
     *
463
     * @param array      $array
464
     * @param bool|false $deep
465
     *
466
     * @return array
467
     */
468
    public function reset(array $array, $deep = false)
469
    {
470
        $target = [];
471
472
        foreach ($array as $key => $value) {
473
            if ($deep && is_array($value)) {
474
                $value = $this->reset($value);
475
            }
476
477
            if (is_numeric($key)) {
478
                $target[] = $value;
479
            } else {
480
                $target[$key] = $value;
481
            }
482
        }
483
484
        return $target;
485
    }
486
487
    /**
488
     * Extend one array with another. Non associative arrays will not be merged
489
     * but rather replaced.
490
     *
491
     * @param array $arrays
492
     *
493
     * @return array
494
     */
495
    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...
496
    {
497
        $merged     = [];
498
499
        foreach (func_get_args() as $array) {
500
            foreach ($array as $key => $value) {
501
                if (is_array($value) && $this->access->has($merged, $key) && is_array($merged[$key])) {
502
                    if ($this->enumerator->isAssoc($value) && $this->enumerator->isAssoc($merged[$key])) {
503
                        $merged[$key] = $this->extendDistinct($merged[$key], $value);
504
505
                        continue;
506
                    }
507
                }
508
509
                $merged[$key] = $value;
510
            }
511
        }
512
513
        return $merged;
514
    }
515
516
    /**
517
     * Sort the array using the given callback.
518
     *
519
     * @param array    $array
520
     * @param callable $callback
521
     * @param int      $options
522
     * @param bool     $descending
523
     *
524
     * @return array
525
     */
526
    public function sort(array $array, callable $callback, $options = SORT_REGULAR, $descending = false)
527
    {
528
        $results = [];
529
530
        // First we will loop through the items and get the comparator from a callback
531
        // function which we were given. Then, we will sort the returned values and
532
        // and grab the corresponding values for the sorted keys from this array.
533
        foreach ($array as $key => $value) {
534
            $results[$key] = $callback($value, $key);
535
        }
536
537
        $descending ? arsort($results, $options)
538
                    : asort($results, $options);
539
540
        // Once we have sorted all of the keys in the array, we will loop through them
541
        // and grab the corresponding model so we can set the underlying items list
542
        // to the sorted version. Then we'll just return the collection instance.
543
        foreach (array_keys($results) as $key) {
544
            $results[$key] = $array[$key];
545
        }
546
547
        return $results;
548
    }
549
550
    /**
551
     * Recursively sort an array by keys and values.
552
     *
553
     * @param array $array
554
     *
555
     * @return array
556
     */
557
    public function sortRecursive(array $array)
558
    {
559
        foreach ($array as &$value) {
560
            if (is_array($value)) {
561
                $value = $this->sortRecursive($value);
562
            }
563
        }
564
565
        // sort associative array
566
        if ($this->enumerator->isAssoc($array)) {
567
            ksort($array);
568
            // sort regular array
569
        } else {
570
            sort($array);
571
        }
572
573
        return $array;
574
    }
575
576
    /**
577
     * Will turn each element in $arr into an array then appending
578
     * the associated indexs from the other arrays into this array as well.
579
     *
580
     * @param array $array
581
     * @param array $arrays
582
     *
583
     * @return array
584
     */
585
    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...
586
    {
587
        $args = func_get_args();
588
        array_shift($args);
589
590
        foreach ($array as $key => $value) {
591
            $array[$key] = [$value];
592
593
            foreach ($args as $k => $v) {
594
                $array[$key][] = current($args[$k]);
595
596
                if (next($args[$k]) === false && $args[$k] !== [null]) {
597
                    $args[$k] = [null];
598
                }
599
            }
600
        }
601
602
        return $array;
603
    }
604
605
    /**
606
     * Recurse through an array, add the leaf items to the $newArray var
607
     *
608
     * @param array $subject
609
     * @param array &$newArray
610
     * @param array $stack
611
     *
612
     * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

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.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
613
     */
614
    private function recurseCollapse(array $subject, array &$newArray, $stack = [])
615
    {
616
        foreach ($subject as $key => $value) {
617
            $fstack = array_merge($stack, [$key]);
618
619
            if (is_array($value)) {
620
                $this->recurseCollapse($value, $newArray, $fstack);
621
            } else {
622
                $top       = array_shift($fstack);
623
                $arrayPart = count($fstack) ? '.' . implode('.', $fstack): '';
624
                $newArray[$top . $arrayPart] = $value;
625
            }
626
        }
627
    }
628
}
629