GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Test Failed
Pull Request — master (#21)
by Pete
03:19
created

src/collection_functions.php (1 issue)

Labels
Severity

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

Code
1
<?php
2
3
namespace DusanKasan\Knapsack;
4
5
use DusanKasan\Knapsack\Exceptions\InvalidArgument;
6
use DusanKasan\Knapsack\Exceptions\ItemNotFound;
7
use DusanKasan\Knapsack\Exceptions\NoMoreItems;
8
use Traversable;
9
10
/**
11
     * Converts $collection to array. If there are multiple items with the same key, only the last will be preserved.
12
 *
13
 * @param array|Traversable $collection
14
 * @return array
15
 */
16
function toArray($collection)
17
{
18 68
    $arr = [];
19 68
    foreach ($collection as $key => $value) {
20 68
        $arr[$key] = $value;
21 68
    }
22
23 68
    return $arr;
24
}
25
26
/**
27
 * Returns a lazy collection of distinct items in $collection.
28
 *
29
 * @param array|Traversable $collection
30
 * @return Collection
31
 */
32
function distinct($collection)
33
{
34
    $generatorFactory = function () use ($collection) {
35 1
        $distinctValues = [];
36
37 1
        foreach ($collection as $key => $value) {
38 1
            if (!in_array($value, $distinctValues)) {
39 1
                $distinctValues[] = $value;
40 1
                yield $key => $value;
41 1
            }
42 1
        }
43 1
    };
44
45 1
    return new Collection($generatorFactory);
46
}
47
48
/**
49
 * Returns number of items in $collection.
50
 *
51
 * @param array|Traversable $collection
52
 * @return int
53
 */
54
function size($collection)
55
{
56 8
    $result = 0;
57 8
    foreach ($collection as $value) {
58 8
        $result++;
59 8
    }
60
61 8
    return $result;
62
}
63
64
/**
65
 * Returns a non-lazy collection with items from $collection in reversed order.
66
 *
67
 * @param array|Traversable $collection
68
 * @return Collection
69
 */
70
function reverse($collection)
71
{
72
    $generatorFactory = function () use ($collection) {
73 4
        $array = [];
74 4
        foreach ($collection as $key => $value) {
75 3
            $array[] = [$key, $value];
76 4
        }
77
78 5
        return map(
79 4
            indexBy(
80 4
                array_reverse($array),
81
                function($item) {
82 3
                    return $item[0];
83
                }
84 4
            ),
85
            function($item) {
86 3
                return $item[1];
87
            }
88 4
        );
89 4
    };
90
91 4
    return new Collection($generatorFactory);
92
}
93
94
/**
95
 * Returns a lazy collection of values from $collection (i.e. the keys are reset).
96
 *
97
 * @param array|Traversable $collection
98
 * @return Collection
99
 */
100
function values($collection)
101
{
102
    $generatorFactory = function () use ($collection) {
103 37
        foreach ($collection as $value) {
104 35
            yield $value;
105 31
        }
106 37
    };
107
108 37
    return new Collection($generatorFactory);
109
}
110
111
/**
112
 * Returns a lazy collection of keys from $collection.
113
 *
114
 * @param array|Traversable $collection
115
 * @return Collection
116
 */
117
function keys($collection)
118
{
119
    $generatorFactory = function () use ($collection) {
120 1
        foreach ($collection as $key => $value) {
121 1
            yield $key;
122 1
        }
123 1
    };
124
125 1
    return new Collection($generatorFactory);
126
}
127
128
/**
129
 * Returns a lazy collection of items from $collection repeated infinitely.
130
 *
131
 * @param array|Traversable $collection
132
 * @return Collection
133
 */
134
function cycle($collection)
135
{
136
    $generatorFactory = function () use ($collection) {
137 1
        while (true) {
138 1
            foreach ($collection as $key => $value) {
139 1
                yield $key => $value;
140 1
            }
141 1
        }
142 1
    };
143
144 1
    return new Collection($generatorFactory);
145
}
146
147
/**
148
 * Returns a non-lazy collection of shuffled items from $collection.
149
 *
150
 * @param array|Traversable $collection
151
 * @return Collection
152
 */
153
function shuffle($collection)
154
{
155 1
    $buffer = [];
156 1
    foreach ($collection as $key => $value) {
157 1
        $buffer[] = [$key, $value];
158 1
    }
159
160 1
    \shuffle($buffer);
161
162 1
    return dereferenceKeyValue($buffer);
163
}
164
165
/**
166
 * Returns true if $collection does not contain any items.
167
 *
168
 * @param array|Traversable $collection
169
 * @return bool
170
 */
171
function isEmpty($collection)
172
{
173 3
    foreach ($collection as $value) {
174 1
        return false;
175 2
    }
176
177 2
    return true;
178
}
179
180
/**
181
 * Returns true if $collection does contain any items.
182
 *
183
 * @param array|Traversable $collection
184
 * @return bool
185
 */
186
function isNotEmpty($collection)
187
{
188 2
    return !isEmpty($collection);
189
}
190
191
/**
192
 * Returns a collection where keys are distinct values from $collection and values are number of occurrences of each
193
 * value.
194
 *
195
 * @param array|Traversable $collection
196
 * @return Collection
197
 */
198
function frequencies($collection)
199
{
200 1
    return countBy($collection, '\DusanKasan\Knapsack\identity');
201
}
202
203
/**
204
 * Returns the first item of $collection or throws ItemNotFound if #collection is empty.
205
 *
206
 * @param array|Traversable $collection
207
 * @return mixed
208
 */
209
function first($collection)
210
{
211 13
    return get(values($collection), 0);
212
}
213
214
/**
215
 * Returns the last item of $collection or throws ItemNotFound if #collection is empty.
216
 *
217
 * @param array|Traversable $collection
218
 * @return mixed
219
 */
220
function last($collection)
221
{
222 2
    return first(reverse($collection));
223
}
224
225
/**
226
 * Returns a lazy collection of items of $collection where value of each item is set to the return value of calling
227
 * $function on its value and key.
228
 *
229
 * @param array|Traversable $collection
230
 * @param callable $function ($value, $key)
231
 * @return Collection
232
 */
233 View Code Duplication
function map($collection, callable $function)
234
{
235
    $generatorFactory = function () use ($collection, $function) {
236 17
        foreach ($collection as $key => $value) {
237 16
            yield $key => $function($value, $key);
238 16
        }
239 17
    };
240
241 17
    return new Collection($generatorFactory);
242
}
243
244
/**
245
 * Returns a lazy collection of items from $collection for which $function returns true.
246
 *
247
 * @param array|Traversable $collection
248
 * @param callable|null $function ($value, $key)
249
 * @return Collection
250
 */
251
function filter($collection, callable $function = null)
252
{
253 7
    if (null === $function) {
254
        $function = function ($value) {
255 1
            return (bool)$value;
256 1
        };
257 1
    };
258
259
    $generatorFactory = function () use ($collection, $function) {
260 7
        foreach ($collection as $key => $value) {
261 7
            if ($function($value, $key)) {
262 5
                yield $key => $value;
263 5
            }
264 7
        }
265 7
    };
266
267 7
    return new Collection($generatorFactory);
268
}
269
270
/**
271
 * Returns a lazy collection with items from all $collections passed as argument appended together
272
 *
273
 * @param array|Traversable ...$collections
274
 * @return Collection
275
 */
276
function concat(...$collections)
277
{
278
    $generatorFactory = function () use ($collections) {
279 4
        foreach ($collections as $collection) {
280 4
            foreach ($collection as $key => $value) {
281 4
                yield $key => $value;
282 4
            }
283 4
        }
284 4
    };
285
286 4
    return new Collection($generatorFactory);
287
}
288
289
/**
290
 * Reduces the collection to single value by iterating over the collection and calling $reduction while
291
 * passing $startValue and current key/item as parameters. The output of $function is used as $startValue in
292
 * next iteration. The output of $function on last element is the return value of this function.
293
 *
294
 * @param array|Traversable $collection
295
 * @param callable $function ($value, $key)
296
 * @param mixed $startValue
297
 * @return mixed
298
 */
299
function reduce($collection, callable $function, $startValue)
300
{
301 4
    $tmp = duplicate($startValue);
302
303 4
    foreach ($collection as $key => $value) {
304 4
        $tmp = $function($tmp, $value, $key);
305 4
    }
306
307 4
    return $tmp;
308
}
309
310
/**
311
 * Flattens multiple levels of nesting in collection. If $levelsToFlatten is not specified, flattens all levels of
312
 * nesting.
313
 *
314
 * @param array|Traversable $collection
315
 * @param int $levelsToFlatten -1 to flatten everything
316
 * @return Collection
317
 */
318
function flatten($collection, $levelsToFlatten = -1)
319
{
320
    $generatorFactory = function () use ($collection, $levelsToFlatten) {
321 3
        $flattenNextLevel = $levelsToFlatten < 0 || $levelsToFlatten > 0;
322 3
        $childLevelsToFlatten = $levelsToFlatten > 0 ? $levelsToFlatten - 1 : $levelsToFlatten;
323
324 3
        foreach ($collection as $key => $value) {
325 3
            if ($flattenNextLevel && (is_array($value) || $value instanceof Traversable)) {
326 3
                foreach (flatten($value, $childLevelsToFlatten) as $childKey => $childValue) {
327 3
                    yield $childKey => $childValue;
328 3
                }
329 3
            } else {
330 3
                yield $key => $value;
331
            }
332 3
        }
333 3
    };
334
335 3
    return new Collection($generatorFactory);
336
}
337
338
/**
339
 * Returns a non-lazy collection sorted using $collection($item1, $item2, $key1, $key2 ). $collection should
340
 * return true if first item is larger than the second and false otherwise.
341
 *
342
 * @param array|Traversable $collection
343
 * @param callable $function ($value1, $value2, $key1, $key2)
344
 * @return Collection
345
 */
346
function sort($collection, callable $function)
347
{
348 2
    $array = iterator_to_array(
349 2
        values(
350 2
            map(
351 2
                $collection,
352
                function ($value, $key) {
353 2
                    return [$key, $value];
354
                }
355 2
            )
356 2
        )
357 2
    );
358
359 2
    uasort(
360 2
        $array,
361
        function ($a, $b) use ($function) {
362 2
            return $function($a[1], $b[1], $a[0], $b[0]);
363
        }
364 2
    );
365
366 2
    return dereferenceKeyValue($array);
367
}
368
369
/**
370
 * Returns a lazy collection that is a part of $collection starting from $from position and ending in $to position.
371
 * If $to is not provided, the returned collection is contains all items from $from until end of $collection. All items
372
 * before $from are iterated over, but not included in result.
373
 *
374
 * @param array|Traversable $collection
375
 * @param int $from
376
 * @param int $to -1 to slice until end
377
 * @return Collection
378
 */
379
function slice($collection, $from, $to = -1)
380
{
381
    $generatorFactory = function () use ($collection, $from, $to) {
382 15
        $index = 0;
383 15
        foreach ($collection as $key => $value) {
384 15
            if ($index >= $from && ($index < $to || $to == -1)) {
385 15
                yield $key => $value;
386 15
            } elseif ($index >= $to && $to >= 0) {
387 12
                break;
388
            }
389
390 15
            $index++;
391 15
        }
392 15
    };
393
394 15
    return new Collection($generatorFactory);
395
}
396
397
/**
398
 * Returns a non-lazy collection of items grouped by the result of $function.
399
 *
400
 * @param array|Traversable $collection
401
 * @param callable $function ($value, $key)
402
 * @return Collection
403
 */
404
function groupBy($collection, callable $function)
405
{
406 4
    $result = [];
407
408 4
    foreach ($collection as $key => $value) {
409 4
        $newKey = $function($value, $key);
410
411 4
        $group = isset($result[$newKey]) ? $result[$newKey] : new Collection([]);
412 4
        $result[$newKey] = $group->append($value);
413 4
    }
414
415 4
    return Collection::from($result);
416
}
417
418
419
/**
420
 * Returns a non-lazy collection of items grouped by the value at given key. Ignores non-collection items and items
421
 * without the given keys
422
 *
423
 * @param array|Traversable $collection
424
 * @param mixed $key
425
 * @return Collection
426
 */
427
function groupByKey($collection, $key)
428
{
429
    $generatorFactory = function () use ($collection, $key) {
430
431 1
        return groupBy(
432 1
            filter(
433 1
                $collection,
434
                function ($item) use ($key) {
435 1
                    return isCollection($item) && has($item, $key);
436
                }
437 1
            ),
438
            function($value) use ($key) {
439 1
                return get($value, $key);
440
            }
441 1
        );
442 1
    };
443
444 1
    return new Collection($generatorFactory);
445
}
446
/**
447
 * Executes $function for each item in $collection
448
 *
449
 * @param array|Traversable $collection
450
 * @param callable $function ($value, $key)
451
 * @return Collection
452
 */
453 View Code Duplication
function each($collection, callable $function)
454
{
455
    $generatorFactory = function () use ($collection, $function) {
456 1
        foreach ($collection as $key => $value) {
457 1
            $function($value, $key);
458
459 1
            yield $key => $value;
460 1
        }
461 1
    };
462
463 1
    return new Collection($generatorFactory);
464
}
465
466
/**
467
 * Returns an item with $key key from $collection. If that key is not present, throws ItemNotFound.
468
 *
469
 * @param array|Traversable $collection
470
 * @param mixed $key
471
 * @return mixed
472
 */
473
function get($collection, $key)
474
{
475 19
    foreach ($collection as $valueKey => $value) {
476 17
        if ($key === $valueKey) {
477 16
            return $value;
478
        }
479 15
    }
480
481 8
    throw new ItemNotFound;
482
}
483
484
/**
485
 * Returns an item with $key key from $collection. If that key is not present, returns $default.
486
 *
487
 * @param array|Traversable $collection
488
 * @param mixed $key
489
 * @param mixed $default value returned if key is not found
490
 * @return mixed
491
 */
492
function getOrDefault($collection, $key, $default)
493
{
494
    try {
495 2
        return get($collection, $key);
496 2
    } catch (ItemNotFound $e) {
497 2
        return $default;
498
    }
499
}
500
501
/**
502
 * Returns the first item from $collection for which $function returns true. If item like that is not present, returns
503
 * $default.
504
 *
505
 * @param array|Traversable $collection
506
 * @param callable $function ($value, $key)
507
 * @param mixed $default
508
 * @return mixed
509
 */
510
function find($collection, callable $function, $default = null)
511
{
512 1
    foreach ($collection as $key => $value) {
513 1
        if ($function($value, $key)) {
514 1
            return $value;
515
        }
516 1
    }
517
518 1
    return $default;
519
}
520
521
/**
522
 * Returns a lazy collection by changing keys of $collection for each item to the result of $function for
523
 * that item.
524
 *
525
 * @param array|Traversable $collection
526
 * @param callable $function ($value, $key)
527
 * @return Collection
528
 */
529 View Code Duplication
function indexBy($collection, callable $function)
530
{
531
    $generatorFactory = function () use ($collection, $function) {
532 5
        foreach ($collection as $key => $value) {
533 4
            yield $function($value, $key) => $value;
534 4
        }
535 5
    };
536
537 5
    return new Collection($generatorFactory);
538
}
539
540
/**
541
 * Returns a non-lazy collection of items whose keys are the return values of $function and values are the number of
542
 * items in this collection for which the $function returned this value.
543
 *
544
 * @param array|Traversable $collection
545
 * @param callable $function ($value, $key)
546
 * @return Collection
547
 */
548
function countBy($collection, callable $function)
549
{
550 2
    return map(
551 2
        groupBy($collection, $function),
552
        '\DusanKasan\Knapsack\size'
553 2
    );
554
}
555
556
/**
557
 * Returns true if $function returns true for every item in $collection
558
 *
559
 * @param array|Traversable $collection
560
 * @param callable $function ($value, $key)
561
 * @return bool
562
 */
563
function every($collection, callable $function)
564
{
565 7
    foreach ($collection as $key => $value) {
566 7
        if (!$function($value, $key)) {
567 4
            return false;
568
        }
569 6
    }
570
571 6
    return true;
572
}
573
574
/**
575
 * Returns true if $function returns true for at least one item in $collection.
576
 *
577
 * @param array|Traversable $collection
578
 * @param callable $function ($value, $key)
579
 * @return bool
580
 */
581
function some($collection, callable $function)
582
{
583 4
    foreach ($collection as $key => $value) {
584 4
        if ($function($value, $key)) {
585 3
            return true;
586
        }
587 2
    }
588
589 2
    return false;
590
}
591
592
/**
593
 * Returns true if $needle is found in $collection values.
594
 *
595
 * @param $collection
596
 * @param mixed $needle
597
 * @return bool
598
 */
599
function contains($collection, $needle)
600
{
601 1
    foreach ($collection as $key => $value) {
602 1
        if ($value === $needle) {
603 1
            return true;
604
        }
605 1
    }
606
607 1
    return false;
608
}
609
610
/**
611
 * Reduce that walks from right to the left.
612
 *
613
 * @param array|Traversable $collection
614
 * @param callable $function
615
 * @param mixed $startValue
616
 * @return mixed
617
 */
618
function reduceRight($collection, callable $function, $startValue)
619
{
620 1
    return reduce(reverse($collection), $function, $startValue);
621
}
622
623
/**
624
 * Returns a lazy collection of first $numberOfItems items of $collection.
625
 *
626
 * @param array|Traversable $collection
627
 * @param int $numberOfItems
628
 * @return Collection
629
 */
630
function take($collection, $numberOfItems)
631
{
632 12
    return slice($collection, 0, $numberOfItems);
633
}
634
635
/**
636
 * Returns a lazy collection of all but first $numberOfItems items of $collection.
637
 *
638
 * @param array|Traversable $collection
639
 * @param int $numberOfItems
640
 * @return Collection
641
 */
642
function drop($collection, $numberOfItems)
643
{
644 4
    return slice($collection, $numberOfItems);
645
}
646
647
/**
648
 * Returns a lazy collection of values, where first value is $value and all subsequent values are computed by applying
649
 * $function to the last value in the collection. By default this produces an infinite collection. However you can
650
 * end the collection by throwing a NoMoreItems exception.
651
 *
652
 * @param mixed $value
653
 * @param callable $function ($value, $key)
654
 * @return Collection
655
 */
656
function iterate($value, callable $function)
657
{
658 4
    $duplicated = duplicate($value);
659
    $generatorFactory = function () use ($duplicated, $function) {
660 4
        $value = $duplicated;
661
662 4
        yield $value;
663
664 4
        while (true) {
665
            try {
666 4
                $value = $function($value);
667 4
                yield $value;
668 4
            } catch (NoMoreItems $e) {
669 2
                break;
670
            }
671 4
        }
672 4
    };
673
674 4
    return new Collection($generatorFactory);
675
}
676
677
/**
678
 * Returns a lazy collection of items from $collection for which $function returned true.
679
 *
680
 * @param array|Traversable $collection
681
 * @param callable $function ($value, $key)
682
 * @return Collection
683
 */
684
function reject($collection, callable $function)
685
{
686 2
    return filter(
687 2
        $collection,
688
        function($value, $key) use ($function) {
689 2
            return !$function($value, $key);
690
        }
691 2
    );
692
}
693
694
/**
695
 * Returns a lazy collection of items in $collection without the last $numberOfItems items.
696
 *
697
 * @param array|Traversable $collection
698
 * @param int $numberOfItems
699
 * @return Collection
700
 */
701
function dropLast($collection, $numberOfItems = 1)
702
{
703
    $generatorFactory = function () use ($collection, $numberOfItems) {
704 1
        $buffer = [];
705
706 1
        foreach ($collection as $key => $value) {
707 1
            $buffer[] = [$key, $value];
708
709 1
            if (count($buffer) > $numberOfItems) {
710 1
                $val = array_shift($buffer);
711 1
                yield $val[0] => $val[1];
712 1
            }
713 1
        }
714 1
    };
715
716 1
    return new Collection($generatorFactory);
717
}
718
719
/**
720
 * Returns a lazy collection of items from $collection separated by $separator.
721
 *
722
 * @param array|Traversable $collection
723
 * @param mixed $separator
724
 * @return Collection
725
 */
726
function interpose($collection, $separator)
727
{
728
    $generatorFactory = function () use ($collection, $separator) {
729 1
        foreach (take($collection, 1) as $key => $value) {
730 1
            yield $key => $value;
731 1
        }
732
733 1
        foreach (drop($collection, 1) as $key => $value) {
734 1
            yield $separator;
735 1
            yield $key => $value;
736 1
        }
737 1
    };
738
739 1
    return new Collection($generatorFactory);
740
}
741
742
/**
743
 * Returns a lazy collection of first item from first collection, first item from second, second from first and
744
 * so on. Accepts any number of collections.
745
 *
746
 * @param array|Traversable ...$collections
747
 * @return Collection
748
 */
749
function interleave(...$collections)
750
{
751
    $generatorFactory = function () use ($collections) {
752 1
        $normalizedCollection = array_map(
753
            function ($collection) {
754 1
                $traversable = new Collection($collection);
755 1
                $traversable->rewind();
756 1
                return $traversable;
757 1
            },
758
            $collections
759 1
        );
760
761
        do {
762 1
            $valid = false;
763 1
            foreach ($normalizedCollection as $collection) {
764 1
                if ($collection->valid()) {
765 1
                    yield $collection->key() => $collection->current();
766 1
                    $collection->next();
767 1
                    $valid = true;
768 1
                }
769 1
            }
770 1
        } while ($valid);
771 1
    };
772
773 1
    return new Collection($generatorFactory);
774
}
775
776
/**
777
 * Returns a lazy collection of items in $collection with $value added as first element. If $key is not provided
778
 * it will be next integer index.
779
 *
780
 * @param array|Traversable $collection
781
 * @param mixed $value
782
 * @param mixed|null $key
783
 * @return Collection
784
 */
785 View Code Duplication
function prepend($collection, $value, $key = null)
786
{
787
    $generatorFactory = function () use ($collection, $value, $key) {
788 2
        if ($key === null) {
789 1
            yield $value;
790 1
        } else {
791 1
            yield $key => $value;
792
        }
793
794 2
        foreach ($collection as $key => $value) {
795 2
            yield $key => $value;
796 2
        }
797 2
    };
798
799 2
    return new Collection($generatorFactory);
800
}
801
802
/**
803
 * Returns a lazy collection of items in $collection with $value added as last element. If $key is not provided
804
 * it will be next integer index.
805
 *
806
 * @param array|Traversable $collection
807
 * @param mixed $value
808
 * @param mixed|null $key
809
 * @return Collection
810
 */
811 View Code Duplication
function append($collection, $value, $key = null)
812
{
813
    $generatorFactory = function () use ($collection, $value, $key) {
814 9
        foreach ($collection as $k => $v) {
815 9
            yield $k => $v;
816 9
        }
817
818 9
        if ($key === null) {
819 7
            yield $value;
820 7
        } else {
821 2
            yield $key => $value;
822
        }
823 9
    };
824
825 9
    return new Collection($generatorFactory);
826
}
827
828
/**
829
 * Returns a lazy collection by removing items from $collection until first item for which $function returns false.
830
 *
831
 * @param array|Traversable $collection
832
 * @param callable $function ($value, $key)
833
 * @return Collection
834
 */
835 View Code Duplication
function dropWhile($collection, callable $function)
836
{
837
    $generatorFactory = function () use ($collection, $function) {
838 2
        $shouldDrop = true;
839 2
        foreach ($collection as $key => $value) {
840 2
            if ($shouldDrop) {
841 2
                $shouldDrop = $function($value, $key);
842 2
            }
843
844 2
            if (!$shouldDrop) {
845 2
                yield $key => $value;
846 2
            }
847 2
        }
848 2
    };
849
850 2
    return new Collection($generatorFactory);
851
}
852
853
/**
854
 * Returns a lazy collection of items from $collection until first item for which $function returns false.
855
 *
856
 * @param array|Traversable $collection
857
 * @param callable $function ($value, $key)
858
 * @return Collection
859
 */
860 View Code Duplication
function takeWhile($collection, callable $function)
861
{
862
    $generatorFactory = function () use ($collection, $function) {
863 2
        $shouldTake = true;
864 2
        foreach ($collection as $key => $value) {
865 2
            if ($shouldTake) {
866 2
                $shouldTake = $function($value, $key);
867 2
            }
868
869 2
            if ($shouldTake) {
870 2
                yield $key => $value;
871 2
            }
872 2
        }
873 2
    };
874
875 2
    return new Collection($generatorFactory);
876
}
877
878
/**
879
 * Returns a lazy collection. A result of calling map and flatten(1)
880
 *
881
 * @param array|Traversable $collection
882
 * @param callable $function ($value, $key)
883
 * @return Collection
884
 */
885
function mapcat($collection, callable $function)
886
{
887 1
    return flatten(map($collection, $function), 1);
888
}
889
890
/**
891
 * Returns a lazy collection [take($collection, $position), drop($collection, $position)]
892
 *
893
 * @param array|Traversable $collection
894
 * @param int $position
895
 * @return Collection
896
 */
897
function splitAt($collection, $position)
898
{
899
    $generatorFactory = function () use ($collection, $position) {
900 1
        yield take($collection, $position);
901 1
        yield drop($collection, $position);
902 1
    };
903
904 1
    return new Collection($generatorFactory);
905
}
906
907
/**
908
 * Returns a lazy collection [takeWhile($collection, $function), dropWhile($collection, $function)]
909
 *
910
 * @param array|Traversable $collection
911
 * @param callable $function ($value, $key)
912
 * @return Collection
913
 */
914
function splitWith($collection, callable $function)
915
{
916
    $generatorFactory = function () use ($collection, $function) {
917 1
        yield takeWhile($collection, $function);
918 1
        yield dropWhile($collection, $function);
919 1
    };
920
921 1
    return new Collection($generatorFactory);
922
}
923
924
/**
925
 * Returns a lazy collection with items from $collection but values that are found in keys of $replacementMap
926
 * are replaced by their values.
927
 *
928
 * @param array|Traversable $collection
929
 * @param array|Traversable $replacementMap
930
 * @return Collection
931
 */
932
function replace($collection, $replacementMap)
933
{
934
    $generatorFactory = function () use ($collection, $replacementMap) {
935 1
        foreach ($collection as $key => $value) {
936 1
            $newValue = getOrDefault($replacementMap, $value, $value);
937 1
            yield $key => $newValue;
938 1
        }
939 1
    };
940
941 1
    return new Collection($generatorFactory);
942
}
943
944
/**
945
 * Returns a lazy collection of reduction steps.
946
 *
947
 * @param array|Traversable $collection
948
 * @param callable $function
949
 * @param mixed $startValue
950
 * @return Collection
951
 */
952 View Code Duplication
function reductions($collection, callable $function, $startValue)
953
{
954
    $generatorFactory = function () use ($collection, $function, $startValue) {
955 1
        $tmp = duplicate($startValue);
956
957 1
        yield $tmp;
958 1
        foreach ($collection as $key => $value) {
959 1
            $tmp = $function($tmp, $value, $key);
960 1
            yield $tmp;
961 1
        }
962 1
    };
963
964 1
    return new Collection($generatorFactory);
965
}
966
967
/**
968
 * Returns a lazy collection of every nth ($step) item in  $collection.
969
 *
970
 * @param array|Traversable $collection
971
 * @param int $step
972
 * @return Collection
973
 */
974
function takeNth($collection, $step)
975
{
976
    $generatorFactory = function () use ($collection, $step) {
977 1
        $index = 0;
978 1
        foreach ($collection as $key => $value) {
979 1
            if ($index % $step == 0) {
980 1
                yield $key => $value;
981 1
            }
982
983 1
            $index++;
984 1
        }
985 1
    };
986
987 1
    return new Collection($generatorFactory);
988
}
989
990
/**
991
 * Returns a lazy collection of collections of $numberOfItems items each, at $step step
992
 * apart. If $step is not supplied, defaults to $numberOfItems, i.e. the partitions
993
 * do not overlap. If a $padding collection is supplied, use its elements as
994
 * necessary to complete last partition up to $numberOfItems items. In case there are
995
 * not enough padding elements, return a partition with less than $numberOfItems items.
996
 *
997
 * @param array|Traversable $collection
998
 * @param $numberOfItems
999
 * @param int $step
1000
 * @param array|Traversable $padding
1001
 * @return Collection
1002
 */
1003
function partition($collection, $numberOfItems, $step = -1, $padding = [])
1004
{
1005
    $generatorFactory = function () use ($collection, $numberOfItems, $step, $padding) {
1006 1
        $buffer = [];
1007 1
        $itemsToSkip = 0;
1008 1
        $tmpStep = $step ?: $numberOfItems;
1009
1010 1
        foreach ($collection as $key => $value) {
1011 1
            if (count($buffer) == $numberOfItems) {
1012 1
                yield dereferenceKeyValue($buffer);
1013
1014 1
                $buffer = array_slice($buffer, $tmpStep);
1015 1
                $itemsToSkip =  $tmpStep - $numberOfItems;
1016 1
            }
1017
1018 1
            if ($itemsToSkip <= 0) {
1019 1
                $buffer[] = [$key, $value];
1020 1
            } else {
1021 1
                $itemsToSkip--;
1022
            }
1023 1
        }
1024
1025 1
        yield take(
1026 1
            concat(dereferenceKeyValue($buffer), $padding),
1027
            $numberOfItems
1028 1
        );
1029 1
    };
1030
1031 1
    return new Collection($generatorFactory);
1032
}
1033
1034
/**
1035
 * Returns a lazy collection created by partitioning $collection each time $function returned a different value.
1036
 *
1037
 * @param array|Traversable $collection
1038
 * @param callable $function
1039
 * @return Collection
1040
 */
1041
function partitionBy($collection, callable $function)
1042
{
1043
    $generatorFactory = function () use ($collection, $function) {
1044 1
        $result = null;
1045 1
        $buffer = [];
1046
1047 1
        foreach ($collection as $key => $value) {
1048 1
            $newResult = $function($value, $key);
1049
1050 1
            if (!empty($buffer) && $result != $newResult) {
1051 1
                yield dereferenceKeyValue($buffer);
1052 1
                $buffer = [];
1053 1
            }
1054
1055 1
            $result = $newResult;
1056 1
            $buffer[] = [$key, $value];
1057 1
        }
1058
1059 1
        if (!empty($buffer)) {
1060 1
            yield dereferenceKeyValue($buffer);
1061 1
        }
1062 1
    };
1063
1064 1
    return new Collection($generatorFactory);
1065
}
1066
1067
/**
1068
 * Returns a lazy collection of $value repeated $times times. If $times is not provided the collection is infinite.
1069
 *
1070
 * @param mixed $value
1071
 * @param int $times
1072
 * @return Collection
1073
 */
1074
function repeat($value, $times = -1)
1075
{
1076
    $generatorFactory = function () use ($value, $times) {
1077 3
        $tmpTimes = $times;
1078
1079 3
        while ($tmpTimes != 0) {
1080 3
            yield $value;
1081
1082 3
            $tmpTimes = $tmpTimes < 0 ? -1 : $tmpTimes - 1;
1083 3
        }
1084 3
    };
1085
1086 3
    return new Collection($generatorFactory);
1087
}
1088
1089
/**
1090
 * Returns a lazy collection of numbers starting at $start, incremented by $step until $end is reached.
1091
 *
1092
 * @param int $start
1093
 * @param int|null $end
1094
 * @param int $step
1095
 * @return Collection
1096
 */
1097
function range($start = 0, $end = null, $step = 1)
1098
{
1099
    $generatorFactory = function () use ($start, $end, $step) {
1100 2
        return iterate(
1101 2
            $start,
1102
            function($value) use ($step, $end) {
1103 2
                $result = $value + $step;
1104
1105 2
                if ($end !== null && $result > $end) {
1106 1
                    throw new NoMoreItems;
1107
                }
1108
1109 2
                return $result;
1110
            }
1111 2
        );
1112 2
    };
1113
1114 2
    return new Collection($generatorFactory);
1115
}
1116
1117
/**
1118
 * Returns true if $input is array or Traversable object.
1119
 *
1120
 * @param mixed $input
1121
 * @return bool
1122
 */
1123
function isCollection($input)
1124
{
1125 19
    return is_array($input) || $input instanceof Traversable;
1126
}
1127
1128
/**
1129
 * Returns duplicated/cloned $input that has no relation to the original one. Used for making sure there are no side
1130
 * effect in functions.
1131
 *
1132
 * @param $input
1133
 * @return mixed
1134
 */
1135
function duplicate($input)
1136
{
1137 9
    if (is_array($input)) {
1138 1
        return toArray(
1139 1
            map(
1140 1
                $input,
1141
                function($i) {
1142 1
                    return duplicate($i);
1143
                }
1144 1
            )
1145 1
        );
1146 9
    } elseif (is_object($input)) {
1147 2
        return clone $input;
1148
    } else {
1149 9
        return $input;
1150
    }
1151
}
1152
1153
/**
1154
 * Transforms [[$key, $value], [$key2, $value2]] into [$key => $value, $key2 => $value2]. Used as a helper
1155
 *
1156
 * @param array|Traversable $collection
1157
 * @return Collection
1158
 */
1159
function dereferenceKeyValue($collection)
1160
{
1161
    $generatorFactory = function () use ($collection) {
1162 6
        foreach ($collection as $value) {
1163 6
            yield $value[0] => $value[1];
1164 6
        }
1165 7
    };
1166
1167 7
    return new Collection($generatorFactory);
1168
}
1169
1170
/**
1171
 * Realizes collection - turns lazy collection into non-lazy one by iterating over it and storing the key/values.
1172
 *
1173
 * @param array|Traversable $collection
1174
 * @return Collection
1175
 */
1176
function realize($collection)
1177
{
1178 1
    return dereferenceKeyValue(
1179 1
        toArray(
1180 1
            map(
1181 1
                $collection,
1182
                function ($value, $key) {
1183 1
                    return [$key, $value];
1184
                }
1185 1
            )
1186 1
        )
1187 1
    );
1188
}
1189
1190
/**
1191
 * Returns the second item of $collection or throws ItemNotFound if $collection is empty or has 1 item.
1192
 *
1193
 * @param array|Traversable $collection
1194
 * @return mixed
1195
 */
1196
function second($collection)
1197
{
1198 6
    return get(values($collection), 1);
1199
}
1200
1201
1202
/**
1203
 * Combines $keys and $values into a lazy collection. The resulting collection has length equal to the size of smaller
1204
 * argument.
1205
 *
1206
 * @param array|Traversable $keys
1207
 * @param array|Traversable $values
1208
 * @return Collection
1209
 */
1210
function combine($keys, $values)
1211
{
1212
    $generatorFactory = function () use ($keys, $values) {
1213 1
        $keyCollection = new Collection($keys);
1214 1
        $valueCollection = new Collection($values);
1215 1
        $valueCollection->rewind();
1216
1217 1
        foreach ($keyCollection as $key) {
1218 1
            if (!$valueCollection->valid()) {
1219 1
                break;
1220
            }
1221
1222 1
            yield $key => $valueCollection->current();
1223 1
            $valueCollection->next();
1224 1
        }
1225 1
    };
1226
1227 1
    return new Collection($generatorFactory);
1228
}
1229
1230
/**
1231
 * Returns a lazy collection without the items associated to any of the keys from $keys.
1232
 *
1233
 * @param array|Traversable $collection
1234
 * @param array|Traversable $keys
1235
 * @return Collection
1236
 */
1237
function except($collection, $keys)
1238
{
1239 1
    $keys = toArray(values($keys));
1240
1241 1
    return reject(
1242 1
        $collection,
1243
        function ($value, $key) use ($keys) {
1244 1
            return in_array($key, $keys);
1245
        }
1246 1
    );
1247
}
1248
1249
/**
1250
 * Returns a lazy collection of items associated to any of the keys from $keys.
1251
 *
1252
 * @param array|Traversable $collection
1253
 * @param array|Traversable $keys
1254
 * @return Collection
1255
 */
1256
function only($collection, $keys)
1257
{
1258 2
    $keys = toArray(values($keys));
1259
1260 2
    return filter(
1261 2
        $collection,
1262
        function ($value, $key) use ($keys) {
1263 2
            return in_array($key, $keys);
1264
        }
1265 2
    );
1266
}
1267
1268
/**
1269
 * Returns a lazy collection of items that are in $collection but are not in any of the other arguments. Note that the
1270
 * ...$collections are iterated non-lazily.
1271
 *
1272
 * @param array|Traversable $collection
1273
 * @param array|Traversable ...$collections
1274
 * @return Collection
1275
 */
1276 View Code Duplication
function diff($collection, ...$collections)
1277
{
1278 1
    $valuesToCompare = toArray(values(concat(...$collections)));
1279
    $generatorFactory = function () use ($collection, $valuesToCompare) {
1280 1
        foreach ($collection as $key => $value) {
1281 1
            if (!in_array($value, $valuesToCompare)) {
1282 1
                yield $key => $value;
1283 1
            }
1284 1
        }
1285 1
    };
1286
1287 1
    return new Collection($generatorFactory);
1288
}
1289
1290
/**
1291
 * Returns a lazy collection of items that are in $collection and all the other arguments, indexed by the keys from the
1292
 * first collection. Note that the ...$collections are iterated non-lazily.
1293
 *
1294
 * @param array|Traversable $collection
1295
 * @param array|Traversable ...$collections
1296
 * @return Collection
1297
 */
1298 View Code Duplication
function intersect($collection, ...$collections)
1299
{
1300 1
    $valuesToCompare = toArray(values(concat(...$collections)));
1301
    $generatorFactory = function () use ($collection, $valuesToCompare) {
1302 1
        foreach ($collection as $key => $value) {
1303 1
            if (in_array($value, $valuesToCompare)) {
1304 1
                yield $key => $value;
1305 1
            }
1306 1
        }
1307 1
    };
1308
1309 1
    return new Collection($generatorFactory);
1310
}
1311
1312
/**
1313
 * Returns a lazy collection where keys and values are flipped.
1314
 *
1315
 * @param array|Traversable $collection
1316
 * @return Collection
1317
 */
1318
function flip($collection)
1319
{
1320
    $generatorFactory = function () use ($collection) {
1321 1
        foreach ($collection as $key => $value) {
1322 1
            yield $value => $key;
1323 1
        }
1324 1
    };
1325
1326 1
    return new Collection($generatorFactory);
1327
}
1328
1329
/**
1330
 * Checks for the existence of an item with key $key in $collection.
1331
 *
1332
 * @param array|Traversable $collection
1333
 * @param mixed $key
1334
 * @return bool
1335
 */
1336
function has($collection, $key)
1337
{
1338
    try {
1339 2
        get($collection, $key);
1340 2
        return true;
1341 2
    } catch (ItemNotFound $e) {
1342 2
        return false;
1343
    }
1344
}
1345
1346
/**
1347
 * Returns a lazy collection of non-lazy collections of items from nth position from each passed collection. Stops when
1348
 * any of the collections don't have an item at the nth position.
1349
 *
1350
 * @param array|Traversable[] ...$collections
1351
 * @return Collection
1352
 */
1353
function zip(...$collections)
1354
{
1355 1
    $normalizedCollections = [];
1356 1
    foreach ($collections as $collection) {
1357 1
        $traversable = new Collection($collection);
1358 1
        $traversable->rewind();
1359 1
        $normalizedCollections[] = $traversable;
1360 1
    }
1361
1362
    $generatorFactory = function () use ($normalizedCollections) {
1363 1
        while (true) {
1364 1
            $isMissingItems = false;
1365 1
            $zippedItem = new Collection([]);
1366
1367 1
            foreach ($normalizedCollections as $collection) {
1368 1
                if (!$collection->valid()) {
1369 1
                    $isMissingItems = true;
1370 1
                    break;
1371
                }
1372
1373 1
                $zippedItem = append($zippedItem, $collection->current(), $collection->key());
1374 1
                $collection->next();
1375 1
            }
1376
1377 1
            if (!$isMissingItems) {
1378 1
                yield $zippedItem;
1379 1
            } else {
1380 1
                break;
1381
            }
1382 1
        }
1383 1
    };
1384
1385 1
    return new Collection($generatorFactory);
1386
}
1387
1388
/**
1389
 * Transpose each item in a collection, interchanging the row and column indexes.
1390
 * Can only transpose multi-dimensional arrays or collections. Otherwise an InvalidArgument is raised.
1391
 *
1392
 * @param array|Collection $collection
1393
 * @return Collection
1394
 */
1395
function transpose($collection)
1396
{
1397
    if (every($collection, function ($value) {
1398 6
        return is_array($value);
1399 6
    })) {
1400 5
        return new Collection(array_map(null, ...$collection->values()));
0 ignored issues
show
It seems like $collection is not always an object, but can also be of type array. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
1401
    }
1402
1403
    if (some($collection, function ($value) {
1404 3
        return $value instanceof Collection;
1405 3
    })) {
1406
        // if the values are also collections, first convert them to arrays
1407
        $subArrays = map($collection, function ($value) {
1408 2
            if ($value instanceof Collection) {
1409 2
                return $value->toArray();
1410
            }
1411 1
            return $value;
1412 2
        });
1413
1414 2
        $transposedSubArrays = transpose($subArrays);
1415
1416
        // then convert the transposed arrays back to collections.
1417
        $subCollections = map($transposedSubArrays, function ($array) {
1418 2
            return new Collection($array);
1419 2
        });
1420
1421 2
        return new Collection($subCollections);
1422
    }
1423
1424 1
    throw new InvalidArgument('Can only transpose multi-dimensional arrays or collections of collections.');
1425
}
1426
1427
/**
1428
 * Returns a lazy collection of data extracted from $collection items by dot separated key path. Supports the *
1429
 * wildcard. If a key contains \ or * it must be escaped using \ character.
1430
 *
1431
 * @param array|Traversable $collection
1432
 * @param mixed $keyPath
1433
 * @return Collection
1434
 */
1435
function extract($collection, $keyPath)
1436
{
1437 1
    preg_match_all('/(.*[^\\\])(?:\.|$)/U', $keyPath, $matches);
1438 1
    $pathParts = $matches[1];
1439
1440
    $extractor = function ($coll) use ($pathParts) {
1441 1
        foreach ($pathParts as $pathPart) {
1442 1
            $coll = flatten(filter($coll, '\DusanKasan\Knapsack\isCollection'), 1);
1443
1444 1
            if ($pathPart != '*') {
1445 1
                $pathPart = str_replace(['\.', '\*'], ['.', '*'], $pathPart);
1446 1
                $coll = values(only($coll, [$pathPart]));
1447 1
            }
1448 1
        }
1449
1450 1
        return $coll;
1451 1
    };
1452
1453
    $generatorFactory = function () use ($collection, $extractor) {
1454 1
        foreach ($collection as $item) {
1455 1
            foreach ($extractor([$item]) as $extracted) {
1456 1
                yield $extracted;
1457 1
            }
1458 1
        }
1459 1
    };
1460
1461
1462 1
    return new Collection($generatorFactory);
1463
}
1464