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.
Passed
Push — master ( 4d892a...befb2e )
by Dušan
02:58
created

collection_functions.php ➔ partition()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

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