Passed
Push — master ( 75a68d...f0439a )
by
unknown
43s queued 10s
created

itertools.php ➔ collapse()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 8
ccs 3
cts 3
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Zicht Online <http://zicht.nl>
4
 */
5
6
namespace Zicht\Itertools;
7
8
use Zicht\Itertools\conversions;
9
use Zicht\Itertools\lib\AccumulateIterator;
10
use Zicht\Itertools\lib\ChainIterator;
11
use Zicht\Itertools\lib\CountIterator;
12
use Zicht\Itertools\lib\CycleIterator;
13
use Zicht\Itertools\lib\FilterIterator;
14
use Zicht\Itertools\lib\GroupbyIterator;
15
use Zicht\Itertools\lib\Interfaces\AccumulateInterface;
16
use Zicht\Itertools\lib\Interfaces\AllInterface;
17
use Zicht\Itertools\lib\Interfaces\ChainInterface;
18
use Zicht\Itertools\lib\Interfaces\CollapseInterface;
19
use Zicht\Itertools\lib\Interfaces\CycleInterface;
20
use Zicht\Itertools\lib\Interfaces\FilterInterface;
21
use Zicht\Itertools\lib\Interfaces\FiniteIterableInterface;
22
use Zicht\Itertools\lib\Interfaces\FirstInterface;
23
use Zicht\Itertools\lib\Interfaces\GroupByInterface;
24
use Zicht\Itertools\lib\Interfaces\LastInterface;
25
use Zicht\Itertools\lib\Interfaces\MapByInterface;
26
use Zicht\Itertools\lib\Interfaces\ReduceInterface;
27
use Zicht\Itertools\lib\Interfaces\ReversedInterface;
28
use Zicht\Itertools\lib\Interfaces\SliceInterface;
29
use Zicht\Itertools\lib\Interfaces\SortedInterface;
30
use Zicht\Itertools\lib\Interfaces\UniqueInterface;
31
use Zicht\Itertools\lib\Interfaces\ZipInterface;
32
use Zicht\Itertools\lib\IterableIterator;
33
use Zicht\Itertools\lib\MapByIterator;
34
use Zicht\Itertools\lib\MapIterator;
35
use Zicht\Itertools\lib\RepeatIterator;
36
use Zicht\Itertools\lib\ReversedIterator;
37
use Zicht\Itertools\lib\SliceIterator;
38
use Zicht\Itertools\lib\SortedIterator;
39
use Zicht\Itertools\lib\UniqueIterator;
40
use Zicht\Itertools\lib\ZipIterator;
41
use Zicht\Itertools\reductions;
42
43
/**
44
 * Transforms anything into an \Iterator or throws an \InvalidArgumentException
45
 *
46
 * @param array|string|\Iterator $iterable
47
 * @return \Iterator
48
 *
49
 * @deprecated Use conversions\mixed_to_iterator instead, will be removed in version 3.0
50
 */
51
function mixedToIterator($iterable) // phpcs:ignore Zicht.NamingConventions.Functions.GlobalNaming
52
{
53
    return conversions\mixed_to_iterator($iterable);
54
}
55
56
/**
57
 * Try to transforms something into a \Closure
58
 *
59
 * @param null|\Closure $closure
60
 * @return \Closure
61
 *
62
 * @deprecated Use conversions\mixed_to_closure instead, will be removed in version 3.0
63
 */
64
function mixedToClosure($closure) // phpcs:ignore Zicht.NamingConventions.Functions.GlobalNaming
65
{
66
    return conversions\mixed_to_closure($closure);
67
}
68
69
/**
70
 * Try to transforms something into a \Closure that gets a value from $strategy
71
 *
72
 * @param null|string|\Closure $strategy
73
 * @return \Closure
74
 *
75
 * @deprecated Use Conversions::mixedToValueGetter instead, will be removed in version 3.0
76
 */
77
function mixedToValueGetter($strategy) // phpcs:ignore Zicht.NamingConventions.Functions.GlobalNaming
78
{
79
    return conversions\mixed_to_value_getter($strategy);
80
}
81
82
/**
83
 * Try to transform something into a \Closure
84
 *
85
 * @param string|\Closure $closure
86
 * @return \Closure
87
 *
88
 * @deprecated Will be removed in version 3.0, no replacement will be needed
89
 */
90
function mixedToOperationClosure($closure) // phpcs:ignore Zicht.NamingConventions.Functions.GlobalNaming
91
{
92
    if (is_string($closure)) {
93
        $closure = reductions\get_reduction($closure, $closure);
0 ignored issues
show
Deprecated Code introduced by
The function Zicht\Itertools\reductions\get_reduction() has been deprecated with message: please use the reduction functions directly, will be removed in version 3.0

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
94
    }
95
96
    if (!($closure instanceof \Closure)) {
97
        throw new \InvalidArgumentException('Argument $closure must be a \Closure or string (i.e. "add", "join", etc)');
98
    }
99
100
    return $closure;
101
}
102
103
/**
104
 * Make an iterator that returns accumulated sums
105
 *
106
 * If the optional $closure argument is supplied, it should be a string:
107
 * add, sub, mul, min, or max.  Or it can be a \Closure taking two
108
 * arguments that will be used to instead of addition.
109
 *
110
 * > accumulate([1,2,3,4,5])
111
 * 1 3 6 10 15
112
 *
113
 * > accumulate(['One', 'Two', 'Three'], function ($a, $b) { return $a . $b; })
114
 * 'One' 'OneTwo' 'OneTwoThree'
115
 *
116
 * @param array|string|\Iterator $iterable
117
 * @param string|\Closure $closure
118
 * @return AccumulateIterator
119
 */
120
function accumulate($iterable, $closure = 'add')
121
{
122 16
    if (!($iterable instanceof AccumulateInterface)) {
123 15
        $iterable = iterable($iterable);
124
    }
125
126 14
    return $iterable->accumulate($closure);
127
}
128
129
/**
130
 * Reduce an iterator to a single value
131
 *
132
 * > reduce([1,2,3])
133
 * 6
134
 *
135
 * > reduce([1,2,3], 'max')
136
 * 3
137
 *
138
 * > reduce([1,2,3], 'sub', 10)
139
 * 4
140
 *
141
 * > reduce([], 'min', 1)
142
 * 1
143
 *
144
 * @param array|string|\Iterator $iterable
145
 * @param string|\Closure $closure
146
 * @param mixed $initializer
147
 * @return mixed
148
 */
149
function reduce($iterable, $closure = 'add', $initializer = null)
150
{
151 17
    if (!($iterable instanceof ReduceInterface)) {
152 17
        $iterable = iterable($iterable);
153
    }
154
155 14
    return $iterable->reduce($closure, $initializer);
156
}
157
158
/**
159
 * Collapse a two dimentional iterator into a one dimentional iterator
160
 *
161
 * > collapse([1, 2], [3, 4])
162
 * 1, 2, 3, 4
163
 *
164
 * @param array|string|\Iterator $iterable
165
 * @return lib\CollapseIterator
166
 */
167
function collapse($iterable)
168
{
169 6
    if (!($iterable instanceof CollapseInterface)) {
170 6
        $iterable = iterable($iterable);
171
    }
172
173 3
    return $iterable->collapse();
174
}
175
176
/**
177
 * Make an iterator that contains all consecutive elements from all provided iterables.
178
 *
179
 * The resulting iterator contains elements from the first iterable in the parameters
180
 * until it is exhausted, then proceeds to the next iterable in the parameters, until
181
 * all the iterables are exhausted.  Used for creating consecutive
182
 * sequences as a single sequence
183
 *
184
 * > chain([1, 2, 3], [4, 5, 6])
185
 * 1 2 3 4 5 6
186
 *
187
 * > chain('ABC', 'DEF')
188
 * A B C D E F
189
 *
190
 * @return ChainIterator
191
 */
192
function chain()
193
{
194
    // note, once we stop supporting php 5.5, we can rewrite the code below
195
    // to the chain(...$iterables) structure.
196
    // http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list
197
198 12
    $args = func_get_args();
199 12
    if (sizeof($args) > 0) {
200 11
        list($iterable) = array_slice($args, 0, 1);
201 11
        $iterables = array_slice($args, 1);
202
    } else {
203 1
        $iterable = null;
204 1
        $iterables = [];
205
    }
206
207 12
    if (!($iterable instanceof ChainInterface)) {
208 12
        $iterable = iterable($iterable);
209
    }
210
211 9
    return call_user_func_array([$iterable, 'chain'], $iterables);
212
}
213
214
/**
215
 * Make an iterator that returns evenly spaced values starting with
216
 * number $start
217
 *
218
 * > count(10)
219
 * 10 11 12 13 14 ...
220
 *
221
 * > count(2.5, 0.5)
222
 * 2.5 3.0 3.5 4.0 ...
223
 *
224
 * @param int|float $start
225
 * @param int|float $step
226
 * @return CountIterator
227
 */
228
function count($start = 0, $step = 1)
229
{
230 36
    if (!(is_int($start) || is_float($start))) {
231 3
        throw new \InvalidArgumentException('Argument $start must be an integer or float');
232
    }
233
234 33
    if (!(is_int($step) || is_float($step))) {
235 3
        throw new \InvalidArgumentException('Argument $step must be an integer or float');
236
    }
237
238 30
    return new CountIterator($start, $step);
239
}
240
241
/**
242
 * Make an iterator returning elements from the $iterable and saving a
243
 * copy of each.  When the iterable is exhausted, return elements from
244
 * the saved copy, repeating indefinitely
245
 *
246
 * > cycle('ABCD')
247
 * A B C D A B C D A B C D ...
248
 *
249
 * @param array|string|\Iterator $iterable
250
 * @return CycleIterator
251
 */
252
function cycle($iterable)
253
{
254 12
    if (!($iterable instanceof CycleInterface)) {
255 12
        $iterable = iterable($iterable);
256
    }
257
258 9
    return $iterable->cycle();
259
}
260
261
/**
262
 * Make an iterator returning values from $iterable and keys from
263
 * $strategy
264
 *
265
 * When $strategy is a string, the key is obtained through one of
266
 * the following:
267
 * 1. $value->{$strategy}, when $value is an object and
268
 *    $strategy is an existing property,
269
 * 2. call $value->{$strategy}(), when $value is an object and
270
 *    $strategy is an existing method,
271
 * 3. $value[$strategy], when $value is an array and $strategy
272
 *    is an existing key,
273
 * 4. otherwise the key will default to null.
274
 *
275
 * Alternatively $strategy can be a closure.  In this case the
276
 * $strategy closure is called with each value in $iterable and the
277
 * key will be its return value.
278
 *
279
 * > $list = [['id'=>1, 'title'=>'one'], ['id'=>2, 'title'=>'two']]
280
 * > mapBy('id', $list)
281
 * 1=>['id'=>1, 'title'=>'one'] 2=>['id'=>2, 'title'=>'two']
282
 *
283
 * @param null|string|\Closure $strategy
284
 * @param array|string|\Iterator $iterable
285
 * @return MapByIterator
286
 */
287
function map_by($strategy, $iterable)
288
{
289
    // In version 3.0 mapBy and map_by will be removed
290
    // as its functionality will be merged into map.
291
292 23
    if (!($iterable instanceof MapByInterface)) {
293 23
        $iterable = iterable($iterable);
294
    }
295
296 23
    return $iterable->mapBy($strategy);
297
}
298
299
/**
300
 * Make an iterator returning values from $iterable and keys from
301
 * $strategy
302
 *
303
 * @param null|string|\Closure $strategy
304
 * @param array|string|\Iterator $iterable
305
 * @return MapByIterator
306
 *
307
 * @deprecated Please use map_by, will be removed in version 3.0
308
 */
309
function mapBy($strategy, $iterable) // phpcs:ignore Zicht.NamingConventions.Functions.GlobalNaming
310
{
311
    return map_by($strategy, $iterable);
312
}
313
314
/**
315
 * Make an iterator returning values from $iterable and keys from
316
 * $strategy
317
 *
318
 * @param null|string|\Closure $strategy
319
 * @param array|string|\Iterator $iterable
320
 * @return MapByIterator
321
 *
322
 * @deprecated use map_by() instead, will be removed in version 3.0
323
 */
324
function keyCallback($strategy, $iterable) // phpcs:ignore Zicht.NamingConventions.Functions.GlobalNaming
325
{
326
    return map_by($strategy, $iterable);
327
}
328
329
/**
330
 * Make an iterator that applies $strategy to every entry in the iterables
331
 *
332
 * If one iterable is passed, $strategy is called for each entry in
333
 * the $iterable, where the first argument is the value and the
334
 * second argument is the key of the entry
335
 *
336
 * If more than one iterable is passed, $strategy is called with the
337
 * values and the keys from the iterables.  For example, the first
338
 * call to $strategy will be:
339
 * $strategy($value_iterable1, $value_iterable2, $key_iterable2, $key_iterable2)
340
 *
341
 * With multiple iterables, the iterator stops when the shortest
342
 * iterable is exhausted.
343
 *
344
 * > $minimal = function ($value) { return min(3, $value); };
345
 * > map($minimal, [1, 2, 3, 4]);
346
 * 3 3 3 4
347
 *
348
 * > $average = function ($value1, $value2) { return ($value1 + $value2) / 2; };
349
 * > map($average, [1, 2, 3], [4, 5, 6]);
350
 * 2.5 3.5 4.5
351
 *
352
 * @param null|string|\Closure $strategy
353
 * @param array|string|\Iterator $iterable Additional $iterable parameters may follow
354
 * @return MapIterator
355
 */
356 View Code Duplication
function map($strategy, $iterable)
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...
357
{
358
    // note, once we stop supporting php 5.5, we can rewrite the code below
359
    // to the map(...$iterables) structure.
360
    // http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list
361
362 32
    if (func_num_args() > 2) {
363 10
        $iterables = array_slice(func_get_args(), 2);
364
    } else {
365 22
        $iterables = [];
366
    }
367
368 32
    if (!($iterable instanceof ChainInterface)) {
369 31
        $iterable = iterable($iterable);
370
    }
371
372 32
    return call_user_func_array([$iterable, 'map'], array_merge([$strategy], $iterables));
373
}
374
375
/**
376
 * Select values from the iterator by applying a function to each of the iterator values, i.e., mapping it to the
377
 * value with a strategy based on the input, similar to map_key
378
 *
379
 * @param null|string|\Closure $strategy
380
 * @param array|string|\Iterator $iterable
381
 * @param bool $flatten
382
 * @return array|MapIterator
383
 *
384
 * @deprecated Please use map(...)->values() instead (when flatten true), will be removed in version 3.0
385
 */
386
function select($strategy, $iterable, $flatten = true)
387
{
388
    if (!is_bool($flatten)) {
389
        throw new \InvalidArgumentException('Argument $FLATTEN must be a boolean');
390
    }
391
392
    $ret = new MapIterator(
393
        conversions\mixed_to_value_getter($strategy),
394
        conversions\mixed_to_iterator($iterable)
395
    );
396
    if ($flatten) {
397
        return $ret->values();
398
    }
399
    return $ret;
400
}
401
402
/**
403
 * Make an iterator that returns $mixed over and over again.  Runs
404
 * indefinitely unless the $times argument is specified
405
 *
406
 * > repeat(2)
407
 * 2 2 2 2 2 ...
408
 *
409
 * > repeat(10, 3)
410
 * 10 10 10
411
 *
412
 * @param mixed $mixed
413
 * @param null|int $times
414
 * @return RepeatIterator
415
 */
416
function repeat($mixed, $times = null)
417
{
418 9
    if (!(is_null($times) || (is_int($times) && $times >= 0))) {
419 4
        throw new \InvalidArgumentException('Argument $times must be null or a positive integer');
420
    }
421
422 5
    return new RepeatIterator($mixed, $times);
423
}
424
425
/**
426
 * Make an iterator that returns consecutive groups from the
427
 * $iterable.  Generally, the $iterable needs to already be sorted on
428
 * the same key function
429
 *
430
 * When $strategy is a string, the key is obtained through one of
431
 * the following:
432
 * 1. $value->{$strategy}, when $value is an object and
433
 *    $strategy is an existing property,
434
 * 2. call $value->{$strategy}(), when $value is an object and
435
 *    $strategy is an existing method,
436
 * 3. $value[$strategy], when $value is an array and $strategy
437
 *    is an existing key,
438
 * 4. otherwise the key will default to null.
439
 *
440
 * Alternatively $strategy can be a closure.  In this case the
441
 * $strategy closure is called with each value in $iterable and the
442
 * key will be its return value.  $strategy is called with two
443
 * parameters: the value and the key of the iterable as the first and
444
 * second parameter, respectively.
445
 *
446
 * The operation of groupBy() is similar to the uniq filter in Unix.
447
 * It generates a break or new group every time the value of the key
448
 * function changes (which is why it is usually necessary to have
449
 * sorted the data using the same key function).  That behavior
450
 * differs from SQL's GROUP BY which aggregates common elements
451
 * regardless of their input order.
452
 *
453
 * > $list = [['type'=>'A', 'title'=>'one'], ['type'=>'A', 'title'=>'two'], ['type'=>'B', 'title'=>'three']]
454
 * > groupby('type', $list)
455
 * 'A'=>[['type'=>'A', 'title'=>'one'], ['type'=>'A', 'title'=>'two']] 'B'=>[['type'=>'B', 'title'=>'three']]
456
 *
457
 * @param null|string|\Closure $strategy
458
 * @param array|string|\Iterator $iterable
459
 * @param boolean $sort
460
 * @return GroupbyIterator
461
 */
462
function group_by($strategy, $iterable, $sort = true)
463
{
464 16
    if (!($iterable instanceof GroupByInterface)) {
465 16
        $iterable = iterable($iterable);
466
    }
467
468 16
    return $iterable->groupBy($strategy, $sort);
469
}
470
471
/**
472
 * Make an iterator that returns consecutive groups from the
473
 * $iterable.  Generally, the $iterable needs to already be sorted on
474
 * the same key function
475
 *
476
 * @param null|string|\Closure $strategy
477
 * @param array|string|\Iterator $iterable
478
 * @param boolean $sort
479
 * @return GroupbyIterator
480
 *
481
 * @deprecated Please use group_by(...)->values() instead (when flatten true), will be removed in version 3.0
482
 */
483
function groupBy($strategy, $iterable, $sort = true) // phpcs:ignore Zicht.NamingConventions.Functions.GlobalNaming
484
{
485
    return group_by($strategy, $iterable, $sort);
486
}
487
488
/**
489
 * Make an iterator that returns the values from $iterable sorted by
490
 * $strategy
491
 *
492
 * When determining the order of two entries the $strategy is called
493
 * twice, once for each value, and the results are used to determine
494
 * the order.  $strategy is called with two parameters: the value and
495
 * the key of the iterable as the first and second parameter, respectively.
496
 *
497
 * When $reverse is true the order of the results are reversed.
498
 *
499
 * The sorted() function is guaranteed to be stable.  A sort is stable
500
 * if it guarantees not to change the relative order of elements that
501
 * compare equal.  this is helpful for sorting in multiple passes (for
502
 * example, sort by department, then by salary grade).  This also
503
 * holds up when $reverse is true.
504
 *
505
 * > $list = [['type'=>'B', 'title'=>'second'], ['type'=>'C', 'title'=>'third'], ['type'=>'A', 'title'=>'first']]
506
 * > sorted('type', $list)
507
 * ['type'=>'A', 'title'=>'first'] ['type'=>'B', 'title'=>'second']] ['type'=>'C', 'title'=>'third']
508
 *
509
 * @param null|string|\Closure $strategy
510
 * @param array|string|\Iterator $iterable
511
 * @param boolean $reverse
512
 * @return SortedIterator
513
 */
514
function sorted($strategy, $iterable, $reverse = false)
515
{
516 22
    if (!($iterable instanceof SortedInterface)) {
517 21
        $iterable = iterable($iterable);
518
    }
519
520 22
    return $iterable->sorted($strategy, $reverse);
521
}
522
523
/**
524
 * Make an iterator that returns values from $iterable where the
525
 * $strategy determines that the values are not empty
526
 *
527
 * An optional $strategy may be given, this must be either null,
528
 * a string, or a \Closure.
529
 *
530
 * Following the (optional) $strategy, one or more $iterable instances
531
 * must be given.  They must be either an array, a string, or an \Iterator.
532
 *
533
 * @return FilterIterator
534
 */
535 View Code Duplication
function filter()
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...
536
{
537
    // note, once we stop supporting php 5.5, we can rewrite the code below
538
    // to the filter(...$iterables) structure.
539
    // http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list
540
541 9
    $args = func_get_args();
542 9
    switch (sizeof($args)) {
543 9
        case 1:
544 3
            $strategy = null;
545 3
            $iterable = $args[0];
546 3
            break;
547
548 6
        case 2:
549 5
            $strategy = $args[0];
550 5
            $iterable = $args[1];
551 5
            break;
552
553
        default:
554 1
            throw new \InvalidArgumentException('filter requires either one (iterable) or two (strategy, iterable) arguments');
555
    }
556
557 8
    if (!($iterable instanceof FilterInterface)) {
558 7
        $iterable = iterable($iterable);
559
    }
560
561 5
    return $iterable->filter($strategy);
562
}
563
564
/**
565
 * Make an iterator that returns values from $iterable where the
566
 * $strategy determines that the values are not empty
567
 *
568
 * An $strategy must be given, this must be either null, a string,
569
 * or a \Closure.
570
 *
571
 * Following the $strategy, an optional $closure may be given, this
572
 * closure is called to determine is the value (which results from
573
 * $strategy) is or is not filtered.  Note that without providing a
574
 * $closure, the function !empty(...) is used instead.
575
 *
576
 * Following the (optional) $closure, one or more $iterable instances
577
 * must be given.  They must be either an array, a string, or an \Iterator.
578
 *
579
 * @return FilterIterator
580
 *
581
 * @deprecated Use filter() instead, will be removed in version 3.0
582
 */
583
function filterBy() // phpcs:ignore Zicht.NamingConventions.Functions.GlobalNaming
584
{
585
    // note, once we stop supporting php 5.5, we can rewrite the code below
586
    // to the filterBy(...$iterables) structure.
587
    // http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list
588
589
    $args = func_get_args();
590
    switch (sizeof($args)) {
591 View Code Duplication
        case 2:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
592
            $strategy = conversions\mixed_to_value_getter($args[0]);
593
            $closure = function ($value, $key) use ($strategy) {
594
                $tempVarPhp54 = call_user_func($strategy, $value, $key);
595
                return !empty($tempVarPhp54);
596
            };
597
            $iterable = conversions\mixed_to_iterator($args[1]);
598
            break;
599
600 View Code Duplication
        case 3:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
601
            $strategy = conversions\mixed_to_value_getter($args[0]);
602
            $userClosure = $args[1];
603
            $closure = function ($value, $key) use ($strategy, $userClosure) {
604
                return call_user_func($userClosure, call_user_func($strategy, $value, $key));
605
            };
606
            $iterable = conversions\mixed_to_iterator($args[2]);
607
            break;
608
609
        default:
610
            throw new \InvalidArgumentException('filterBy requires either two (strategy, iterable) or three (strategy, closure, iterable) arguments');
611
    }
612
613
    return new FilterIterator($closure, $iterable);
614
}
615
616
/**
617
 * Returns an iterator where one or more iterables are zipped together
618
 *
619
 * This function returns a list of tuples, where the i-th tuple contains
620
 * the i-th element from each of the argument sequences or iterables.
621
 *
622
 * The returned list is truncated in length to the length of the
623
 * shortest argument sequence.
624
 *
625
 * > zip([1, 2, 3], ['a', 'b', 'c'])
626
 * [1, 'a'] [2, 'b'] [3, 'c']
627
 *
628
 * @param array|string|\Iterator $iterable Additional $iterable parameters may follow
629
 * @return ZipIterator
630
 */
631 View Code Duplication
function zip($iterable)
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...
632
{
633
    // note, once we stop supporting php 5.5, we can rewrite the code below
634
    // to the zip(...$iterables) structure.
635
    // http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list
636
637 11
    if (func_num_args() > 1) {
638 6
        $iterables = array_slice(func_get_args(), 1);
639
    } else {
640 5
        $iterables = [];
641
    }
642
643 11
    if (!($iterable instanceof ZipInterface)) {
644 11
        $iterable = iterable($iterable);
645
    }
646
647 8
    return call_user_func_array([$iterable, 'zip'], $iterables);
648
}
649
650
/**
651
 * Returns an iterable with all the elements from $iterable reversed
652
 *
653
 * @param array|string|\Iterator $iterable
654
 * @return ReversedIterator
655
 */
656
function reversed($iterable)
657
{
658 6
    if (!($iterable instanceof ReversedInterface)) {
659 5
        $iterable = iterable($iterable);
660
    }
661
662 3
    return $iterable->reversed();
663
}
664
665
/**
666
 * Returns an iterator where the values from $strategy are unique
667
 *
668
 * An optional $strategy may be given to specify the value which is used
669
 * to determine whether the element is unique.  When no $strategy is
670
 * given, the identity function is used, i.e. the value of the element
671
 * itself is used to determine whether the element is unique.
672
 *
673
 * Following the optional $strategy, a $iterable must be given.  Otherwise,
674
 * an \InvalidArgumentException will be raised.
675
 *
676
 * > unique([1, 1, 2, 2, 3, 3])
677
 * 1 2 3
678
 *
679
 * > unique('id', [['id' => 1, 'value' => 'a'], ['id' => 1, 'value' => 'b']])
680
 * ['id' => 1, 'value' => 'a']  # one element in this list
681
 *
682
 * @return UniqueIterator
683
 */
684 View Code Duplication
function unique()
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...
685
{
686 25
    $args = func_get_args();
687 25
    switch (sizeof($args)) {
688 25
        case 1:
689 8
            $strategy = null;
690 8
            $iterable = $args[0];
691 8
            break;
692
693 17
        case 2:
694 15
            $strategy = $args[0];
695 15
            $iterable = $args[1];
696 15
            break;
697
698
        default:
699 2
            throw new \InvalidArgumentException('unique requires either one (iterable) or two (strategy, iterable) arguments');
700
    }
701
702 23
    if (!($iterable instanceof UniqueInterface)) {
703 23
        $iterable = iterable($iterable);
704
    }
705
706 17
    return $iterable->unique($strategy);
707
}
708
709
/**
710
 * Returns an iterator where the values from $strategy are unique
711
 *
712
 * @param null|string|\Closure $strategy
713
 * @param array|string|\Iterator $iterable
714
 * @return UniqueIterator
715
 *
716
 * @deprecated use unique($strategy, $iterable) instead, will be removed in version 3.0
717
 */
718
function uniqueBy($strategy, $iterable) // phpcs:ignore Zicht.NamingConventions.Functions.GlobalNaming
719
{
720
    return unique($strategy, $iterable);
721
}
722
723
/**
724
 * Returns true when one or more element of $iterable is not empty, otherwise returns false
725
 *
726
 * An optional $strategy may be given to specify the value which is used
727
 * to determine weather the element evaluates to true.  When no $strategy is
728
 * given, the identity function is used, i.e. the value of the element
729
 * itself is used to determine weather the element evaluates to true.
730
 *
731
 * Following the optional $strategy, an $iterable may be given.  Its type may
732
 * be either array, string, or \Iterator.  When no $iterable is given, false
733
 * is returned.
734
 *
735
 * > any([0, '', false])
736
 * false
737
 *
738
 * > any([1, null, 3])
739
 * true
740
 *
741
 * @return boolean
742
 */
743 View Code Duplication
function any()
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...
744
{
745 19
    $args = func_get_args();
746 19
    switch (sizeof($args)) {
747 19
        case 1:
748 9
            $strategy = null;
749 9
            $iterable = $args[0];
750 9
            break;
751
752 10
        case 2:
753 8
            $strategy = $args[0];
754 8
            $iterable = $args[1];
755 8
            break;
756
757
        default:
758 2
            throw new \InvalidArgumentException('any requires either one (iterable) or two (strategy, iterable) arguments');
759
    }
760
761 17
    if (!($iterable instanceof AllInterface)) {
762 17
        $iterable = iterable($iterable);
763
    }
764
765 14
    return $iterable->any($strategy);
766
}
767
768
/**
769
 * Returns true when all elements of $iterable are not empty, otherwise returns false
770
 *
771
 * An optional $strategy may be given to specify the value which is used
772
 * to determine weather the element evaluates to true.  When no $strategy is
773
 * given, the identity function is used, i.e. the value of the element
774
 * itself is used to determine weather the element evaluates to true.
775
 *
776
 * Following the optional $strategy, an $iterable may be given.  Its type may
777
 * be either array, string, or \Iterator.  When no $iterable is given, true
778
 * is returned.
779
 *
780
 * > all([1, 'hello world', true])
781
 * true
782
 *
783
 * > all([1, null, 3])
784
 * false
785
 *
786
 * @return boolean
787
 */
788 View Code Duplication
function all()
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...
789
{
790 19
    $args = func_get_args();
791 19
    switch (sizeof($args)) {
792 19
        case 1:
793 9
            $strategy = null;
794 9
            $iterable = $args[0];
795 9
            break;
796
797 10
        case 2:
798 8
            $strategy = $args[0];
799 8
            $iterable = $args[1];
800 8
            break;
801
802
        default:
803 2
            throw new \InvalidArgumentException('all requires either one (iterable) or two (strategy, iterable) arguments');
804
    }
805
806 17
    if (!($iterable instanceof AllInterface)) {
807 17
        $iterable = iterable($iterable);
808
    }
809
810 14
    return $iterable->all($strategy);
811
}
812
813
/**
814
 * Make an iterator that contains a slice of $iterable
815
 *
816
 * The parameters $start and $end determine the range that will be taken
817
 * from the $iterable.  These values may be negative, in which case they
818
 * will indicate elements in $iterable starting at the end.
819
 *
820
 * > slice(['a', 'b', 'c', 'd', 'e', 1]
821
 * 'b', 'c', 'd', 'e'
822
 *
823
 * > slice(['a', 'b', 'c', 'd', 'e', -1]
824
 * 'e'
825
 *
826
 * > slice(['a', 'b', 'c', 'd', 'e', 1, 2]
827
 * 'b'
828
 *
829
 * > slice(['a', 'b', 'c', 'd', 'e', 1, -1]
830
 * 'b', 'c', 'd'
831
 *
832
 * @param array|string|\Iterator $iterable
833
 * @param int $start
834
 * @param null|int $end
835
 * @return SliceIterator
836
 */
837
function slice($iterable, $start, $end = null)
838
{
839 27
    if (!($iterable instanceof SliceInterface)) {
840 27
        $iterable = iterable($iterable);
841
    }
842
843 27
    return $iterable->slice($start, $end);
844
}
845
846
/**
847
 * Returns the first element of $iterable or returns $default when $iterable is empty
848
 *
849
 * > first([1, 2, 3])
850
 * 1
851
 *
852
 * > first([])
853
 * null
854
 *
855
 * @param array|string|\Iterator $iterable
856
 * @param mixed $default
857
 * @return mixed
858
 */
859
function first($iterable, $default = null)
860
{
861 9
    if (!($iterable instanceof FirstInterface)) {
862 9
        $iterable = iterable($iterable);
863
    }
864
865 5
    return $iterable->first($default);
866
}
867
868
/**
869
 * Returns the key of the first element of $iterable or returns $default when $iterable is empty
870
 *
871
 * > first_key([1, 2, 3])
872
 * 0
873
 *
874
 * > first_key([])
875
 * null
876
 *
877
 * @param array|string|\Iterator $iterable
878
 * @param mixed $default
879
 * @return mixed
880
 */
881
function first_key($iterable, $default = null)
882
{
883 5
    if (!($iterable instanceof FirstInterface)) {
884 5
        $iterable = iterable($iterable);
885
    }
886
887 5
    return $iterable->firstKey($default);
888
}
889
890
/**
891
 * Returns the last element of $iterable or returns $default when $iterable is empty
892
 *
893
 * > last([1, 2, 3])
894
 * 3
895
 *
896
 * > last([])
897
 * null
898
 *
899
 * @param array|string|\Iterator $iterable
900
 * @param mixed $default
901
 * @return mixed
902
 */
903
function last($iterable, $default = null)
904
{
905 9
    if (!($iterable instanceof LastInterface)) {
906 9
        $iterable = iterable($iterable);
907
    }
908
909 5
    return $iterable->last($default);
910
}
911
912
/**
913
 * Returns the key of the last element of $iterable or returns $default when $iterable is empty
914
 *
915
 * > last_key([1, 2, 3])
916
 * 2
917
 *
918
 * > last_key([])
919
 * null
920
 *
921
 * @param array|string|\Iterator $iterable
922
 * @param mixed $default
923
 * @return mixed
924
 */
925
function last_key($iterable, $default = null)
926
{
927 5
    if (!($iterable instanceof LastInterface)) {
928 5
        $iterable = iterable($iterable);
929
    }
930
931 5
    return $iterable->lastKey($default);
932
}
933
934
/**
935
 * Returns an FiniteIterableInterface, providing a fluent interface to itertools
936
 *
937
 * > iterable([1, 2, 3])->filter(...)->map(...)->first(...)
938
 *
939
 * @param array|string|\Iterator $iterable
940
 * @return FiniteIterableInterface
941
 */
942
function iterable($iterable)
943
{
944 419
    if ($iterable instanceof FiniteIterableInterface) {
945 2
        return $iterable;
946
    }
947
948 419
    return new IterableIterator(conversions\mixed_to_iterator($iterable));
949
}
950