Passed
Pull Request — master (#19)
by
unknown
02:33
created

itertools.php ➔ group_by()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 11
Ratio 100 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 2
nop 3
dl 11
loc 11
ccs 4
cts 4
cp 1
crap 3
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Boudewijn Schoon <[email protected]>
4
 * @copyright Zicht Online <http://zicht.nl>
5
 */
6
7
namespace Zicht\Itertools;
8
9
use Zicht\Itertools\conversions;
10
use Zicht\Itertools\lib\AccumulateIterator;
11
use Zicht\Itertools\lib\ChainIterator;
12
use Zicht\Itertools\lib\CountIterator;
13
use Zicht\Itertools\lib\CycleIterator;
14
use Zicht\Itertools\lib\FilterIterator;
15
use Zicht\Itertools\lib\GroupbyIterator;
16
use Zicht\Itertools\lib\IterableIterator;
17
use Zicht\Itertools\lib\MapByIterator;
18
use Zicht\Itertools\lib\MapIterator;
19
use Zicht\Itertools\lib\RepeatIterator;
20
use Zicht\Itertools\lib\ReversedIterator;
21
use Zicht\Itertools\lib\SliceIterator;
22
use Zicht\Itertools\lib\SortedIterator;
23
use Zicht\Itertools\lib\UniqueIterator;
24
use Zicht\Itertools\lib\ZipIterator;
25
use Zicht\Itertools\reductions;
26
27
/**
28
 * Transforms anything into an \Iterator or throws an \InvalidArgumentException
29
 *
30
 * @param array|string|\Iterator $iterable
31
 * @return \Iterator
32
 *
33
 * @deprecated Use conversions\mixed_to_iterator instead, will be removed in version 3.0
34
 */
35
function mixedToIterator($iterable)
36
{
37
    return conversions\mixed_to_iterator($iterable);
38
}
39
40
/**
41
 * Try to transforms something into a \Closure
42
 *
43
 * @param null|\Closure $closure
44
 * @return \Closure
45
 *
46
 * @deprecated Use conversions\mixed_to_closure instead, will be removed in version 3.0
47
 */
48
function mixedToClosure($closure)
49
{
50
    return conversions\mixed_to_closure($closure);
51
}
52
53
/**
54
 * Try to transforms something into a \Closure that gets a value from $strategy
55
 *
56
 * @param null|string|\Closure $strategy
57
 * @return \Closure
58
 *
59
 * @deprecated Use Conversions::mixedToValueGetter instead, will be removed in version 3.0
60
 */
61
function mixedToValueGetter($strategy)
62
{
63
    return conversions\mixed_to_value_getter($strategy);
64
}
65
66
/**
67
 * Try to transform something into a \Closure
68
 *
69
 * @param string|\Closure $closure
70
 * @return \Closure
71
 *
72
 * @deprecated Will be removed in version 3.0, no replacement will be needed
73
 */
74
function mixedToOperationClosure($closure)
75
{
76
    if (is_string($closure)) {
77
        $closure = reductions\getReduction($closure, $closure);
0 ignored issues
show
Deprecated Code introduced by
The function Zicht\Itertools\reductions\getReduction() 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...
78
    }
79
80
    if (!($closure instanceof \Closure)) {
81
        throw new \InvalidArgumentException('Argument $closure must be a \Closure or string (i.e. "add", "join", etc)');
82
    }
83
84
    return $closure;
85
}
86
87
/**
88
 * Make an iterator that returns accumulated sums
89
 *
90
 * If the optional $closure argument is supplied, it should be a string:
91
 * add, sub, mul, min, or max.  Or it can be a \Closure taking two
92
 * arguments that will be used to instead of addition.
93
 *
94
 * > accumulate([1,2,3,4,5])
95
 * 1 3 6 10 15
96
 *
97
 * > accumulate(['One', 'Two', 'Three'], function ($a, $b) { return $a . $b; })
98
 * 'One' 'OneTwo' 'OneTwoThree'
99
 *
100
 * @param array|string|\Iterator $iterable
101
 * @param string|\Closure $closure
102
 * @return AccumulateIterator
103
 */
104
function accumulate($iterable, $closure = 'add')
105
{
106 17
    return new AccumulateIterator(
107
        conversions\mixed_to_iterator($iterable),
108 15
        $closure instanceof \Closure ? $closure : reductions\getReduction($closure)
0 ignored issues
show
Deprecated Code introduced by
The function Zicht\Itertools\reductions\getReduction() 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...
109
    );
110
}
111
112
/**
113
 * Reduce an iterator to a single value
114
 *
115
 * > reduce([1,2,3])
116
 * 6
117
 *
118
 * > reduce([1,2,3], 'max')
119
 * 3
120
 *
121
 * > reduce([1,2,3], 'sub', 10)
122
 * 4
123
 *
124
 * > reduce([], 'min', 1)
125
 * 1
126
 *
127
 * @param array|string|\Iterator $iterable
128
 * @param string|\Closure $closure
129
 * @param mixed $initializer
130
 * @return mixed
131
 */
132
function reduce($iterable, $closure = 'add', $initializer = null)
133
{
134 58
    $closure = $closure instanceof \Closure ? $closure : reductions\get_reduction($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...
135 55
    $iterable = conversions\mixed_to_iterator($iterable);
136 52
    $iterable->rewind();
137
138 52
    if (null === $initializer) {
139 44
        if ($iterable->valid()) {
140 43
            $initializer = $iterable->current();
141 43
            $iterable->next();
142
        }
143
    }
144
145 52
    $accumulatedValue = $initializer;
146 52
    while ($iterable->valid()) {
147 48
        $accumulatedValue = $closure($accumulatedValue, $iterable->current());
148 47
        $iterable->next();
149
    }
150
151 51
    return $accumulatedValue;
152
}
153
154
/**
155
 * Make an iterator that contains all consecutive elements from all provided iterables.
156
 *
157
 * The resulting iterator contains elements from the first iterable in the parameters
158
 * until it is exhausted, then proceeds to the next iterable in the parameters, until
159
 * all the iterables are exhausted.  Used for creating consecutive
160
 * sequences as a single sequence
161
 *
162
 * > chain([1, 2, 3], [4, 5, 6])
163
 * 1 2 3 4 5 6
164
 *
165
 * > chain('ABC', 'DEF')
166
 * A B C D E F
167
 *
168
 * @return ChainIterator
169
 */
170 View Code Duplication
function chain()
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...
171
{
172
    // note, once we stop supporting php 5.5, we can rewrite the code below
173
    // to the chain(...$iterables) structure.
174
    // http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list
175
176 14
    $iterables = array_map(
177
        function ($iterable) {
178 13
            return conversions\mixed_to_iterator($iterable);
179 14
        },
180
        func_get_args()
181
    );
182 11
    $reflectorClass = new \ReflectionClass('\Zicht\Itertools\lib\ChainIterator');
183 11
    return $reflectorClass->newInstanceArgs($iterables);
184
}
185
186
/**
187
 * Make an iterator that returns evenly spaced values starting with
188
 * number $start
189
 *
190
 * > count(10)
191
 * 10 11 12 13 14 ...
192
 *
193
 * > count(2.5, 0.5)
194
 * 2.5 3.0 3.5 4.0 ...
195
 *
196
 * @param int|float $start
197
 * @param int|float $step
198
 * @return CountIterator
199
 */
200
function count($start = 0, $step = 1)
201
{
202 36
    if (!(is_int($start) || is_float($start))) {
203 3
        throw new \InvalidArgumentException('Argument $start must be an integer or float');
204
    }
205
206 33
    if (!(is_int($step) || is_float($step))) {
207 3
        throw new \InvalidArgumentException('Argument $step must be an integer or float');
208
    }
209
210 30
    return new CountIterator($start, $step);
211
}
212
213
/**
214
 * Make an iterator returning elements from the $iterable and saving a
215
 * copy of each.  When the iterable is exhausted, return elements from
216
 * the saved copy, repeating indefinitely
217
 *
218
 * > cycle('ABCD')
219
 * A B C D A B C D A B C D ...
220
 *
221
 * @param array|string|\Iterator $iterable
222
 * @return CycleIterator
223
 */
224
function cycle($iterable)
225
{
226 13
    return new CycleIterator(conversions\mixed_to_iterator($iterable));
227
}
228
229
/**
230
 * Make an iterator returning values from $iterable and keys from
231
 * $strategy
232
 *
233
 * When $strategy is a string, the key is obtained through one of
234
 * the following:
235
 * 1. $value->{$strategy}, when $value is an object and
236
 *    $strategy is an existing property,
237
 * 2. call $value->{$strategy}(), when $value is an object and
238
 *    $strategy is an existing method,
239
 * 3. $value[$strategy], when $value is an array and $strategy
240
 *    is an existing key,
241
 * 4. otherwise the key will default to null.
242
 *
243
 * Alternatively $strategy can be a closure.  In this case the
244
 * $strategy closure is called with each value in $iterable and the
245
 * key will be its return value.
246
 *
247
 * > $list = [['id'=>1, 'title'=>'one'], ['id'=>2, 'title'=>'two']]
248
 * > mapBy('id', $list)
249
 * 1=>['id'=>1, 'title'=>'one'] 2=>['id'=>2, 'title'=>'two']
250
 *
251
 * @param string|\Closure $strategy
252
 * @param array|string|\Iterator $iterable
253
 * @return MapByIterator
254
 */
255
function map_by($strategy, $iterable)
256
{
257
    // In version 3.0 mapBy and map_by will be removed
258
    // as its functionality will be merged into map.
259
260 24
    return new MapByIterator(
261
        conversions\mixed_to_value_getter($strategy),
262
        conversions\mixed_to_iterator($iterable)
263
    );
264
}
265
266
/**
267
 * Make an iterator returning values from $iterable and keys from
268
 * $strategy
269
 *
270
 * @param string|\Closure $strategy
271
 * @param array|string|\Iterator $iterable
272
 * @return MapByIterator
273
 *
274
 * @deprecated Please use group_by(...)->values() instead (when flatten true), will be removed in version 3.0
275
 */
276
function mapBy($strategy, $iterable)
277
{
278
    return map_by($strategy, $iterable);
279
}
280
281
/**
282
 * Make an iterator returning values from $iterable and keys from
283
 * $strategy
284
 *
285
 * @param string|\Closure $strategy
286
 * @param array|string|\Iterator $iterable
287
 * @return MapByIterator
288
 *
289
 * @deprecated use mapBy() in stead, will be removed in version 3.0
290
 */
291
function keyCallback($strategy, $iterable)
292
{
293
    return mapBy($strategy, $iterable);
294
}
295
296
/**
297
 * Make an iterator that applies $strategy to every entry in the iterables
298
 *
299
 * If one iterable is passed, $strategy is called for each entry in
300
 * the $iterable, where the first argument is the value and the
301
 * second argument is the key of the entry
302
 *
303
 * If more than one iterable is passed, $strategy is called with the
304
 * values and the keys from the iterables.  For example, the first
305
 * call to $strategy will be:
306
 * $strategy($value_iterable1, $value_iterable2, $key_iterable2, $key_iterable2)
307
 *
308
 * With multiple iterables, the iterator stops when the shortest
309
 * iterable is exhausted.
310
 *
311
 * > $minimal = function ($value) { return min(3, $value); };
312
 * > map($minimal, [1, 2, 3, 4]);
313
 * 3 3 3 4
314
 *
315
 * > $average = function ($value1, $value2) { return ($value1 + $value2) / 2; };
316
 * > map($average, [1, 2, 3], [4, 5, 6]);
317
 * 2.5 3.5 4.5
318
 *
319
 * @param null|string|\Closure $strategy
320
 * @param array|string|\Iterator $iterable Additional $iterable parameters may follow
321
 * @return MapIterator
322
 */
323
function map($strategy, $iterable)
0 ignored issues
show
Unused Code introduced by
The parameter $iterable is not used and could be removed.

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

Loading history...
324
{
325
    // note, once we stop supporting php 5.5, we can rewrite the code below
326
    // to the map(...$iterables) structure.
327
    // http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list
328
329 60
    $iterables = array_map(
330
        function ($iterable) {
331 60
            return conversions\mixed_to_iterator($iterable);
332 60
        },
333 60
        array_slice(func_get_args(), 1)
334
    );
335 60
    $reflectorClass = new \ReflectionClass('\Zicht\Itertools\lib\MapIterator');
336 60
    return $reflectorClass->newInstanceArgs(array_merge(array(conversions\mixed_to_value_getter($strategy)), $iterables));
337
}
338
339
/**
340
 * Select values from the iterator by applying a function to each of the iterator values, i.e., mapping it to the
341
 * value with a strategy based on the input, similar to map_key
342
 *
343
 * @param null|string|\Closure $strategy
344
 * @param array|string|\Iterator $iterable
345
 * @param bool $flatten
346
 * @return array|MapIterator
347
 *
348
 * @deprecated Please use map(...)->values() instead (when flatten true), will be removed in version 3.0
349
 */
350
function select($strategy, $iterable, $flatten = true)
351
{
352
    if (!is_bool($flatten)) {
353
        throw new \InvalidArgumentException('Argument $FLATTEN must be a boolean');
354
    }
355
356
    $ret = new MapIterator(
357
        conversions\mixed_to_value_getter($strategy),
358
        conversions\mixed_to_iterator($iterable)
359
    );
360
    if ($flatten) {
361
        return $ret->values();
362
    }
363
    return $ret;
364
}
365
366
/**
367
 * Make an iterator that returns $mixed over and over again.  Runs
368
 * indefinitely unless the $times argument is specified
369
 *
370
 * > repeat(2)
371
 * 2 2 2 2 2 ...
372
 *
373
 * > repeat(10, 3)
374
 * 10 10 10
375
 *
376
 * @param mixed $mixed
377
 * @param null|int $times
378
 * @return RepeatIterator
379
 */
380
function repeat($mixed, $times = null)
381
{
382 9
    if (!(is_null($times) || (is_int($times) && $times >= 0))) {
383 4
        throw new \InvalidArgumentException('Argument $times must be null or a positive integer');
384
    }
385
386 5
    return new RepeatIterator($mixed, $times);
387
}
388
389
/**
390
 * Make an iterator that returns consecutive groups from the
391
 * $iterable.  Generally, the $iterable needs to already be sorted on
392
 * the same key function
393
 *
394
 * When $strategy is a string, the key is obtained through one of
395
 * the following:
396
 * 1. $value->{$strategy}, when $value is an object and
397
 *    $strategy is an existing property,
398
 * 2. call $value->{$strategy}(), when $value is an object and
399
 *    $strategy is an existing method,
400
 * 3. $value[$strategy], when $value is an array and $strategy
401
 *    is an existing key,
402
 * 4. otherwise the key will default to null.
403
 *
404
 * Alternatively $strategy can be a closure.  In this case the
405
 * $strategy closure is called with each value in $iterable and the
406
 * key will be its return value.  $strategy is called with two
407
 * parameters: the value and the key of the iterable as the first and
408
 * second parameter, respectively.
409
 *
410
 * The operation of groupBy() is similar to the uniq filter in Unix.
411
 * It generates a break or new group every time the value of the key
412
 * function changes (which is why it is usually necessary to have
413
 * sorted the data using the same key function).  That behavior
414
 * differs from SQL's GROUP BY which aggregates common elements
415
 * regardless of their input order.
416
 *
417
 * > $list = [['type'=>'A', 'title'=>'one'], ['type'=>'A', 'title'=>'two'], ['type'=>'B', 'title'=>'three']]
418
 * > groupby('type', $list)
419
 * 'A'=>[['type'=>'A', 'title'=>'one'], ['type'=>'A', 'title'=>'two']] 'B'=>[['type'=>'B', 'title'=>'three']]
420
 *
421
 * @param null|string|\Closure $strategy
422
 * @param array|string|\Iterator $iterable
423
 * @param boolean $sort
424
 * @return GroupbyIterator
425
 */
426 View Code Duplication
function group_by($strategy, $iterable, $sort = true)
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...
427
{
428 18
    if (!is_bool($sort)) {
429 1
        throw new \InvalidArgumentException('Argument $sort must be a boolean');
430
    }
431
432 17
    return new GroupbyIterator(
433
        conversions\mixed_to_value_getter($strategy),
434 15
        $sort ? sorted($strategy, $iterable) : conversions\mixed_to_iterator($iterable)
435
    );
436
}
437
438
/**
439
 * Make an iterator that returns consecutive groups from the
440
 * $iterable.  Generally, the $iterable needs to already be sorted on
441
 * the same key function
442
 *
443
 * @param null|string|\Closure $strategy
444
 * @param array|string|\Iterator $iterable
445
 * @param boolean $sort
446
 * @return GroupbyIterator
447
 *
448
 * @deprecated Please use group_by(...)->values() instead (when flatten true), will be removed in version 3.0
449
 */
450
function groupBy($strategy, $iterable, $sort = true)
451
{
452
    return group_by($strategy, $iterable, $sort);
453
}
454
455
/**
456
 * Make an iterator that returns the values from $iterable sorted by
457
 * $strategy
458
 *
459
 * When determining the order of two entries the $strategy is called
460
 * twice, once for each value, and the results are used to determine
461
 * the order.  $strategy is called with two parameters: the value and
462
 * the key of the iterable as the first and second parameter, respectively.
463
 *
464
 * When $reverse is true the order of the results are reversed.
465
 *
466
 * The sorted() function is guaranteed to be stable.  A sort is stable
467
 * if it guarantees not to change the relative order of elements that
468
 * compare equal.  this is helpful for sorting in multiple passes (for
469
 * example, sort by department, then by salary grade).  This also
470
 * holds up when $reverse is true.
471
 *
472
 * > $list = [['type'=>'B', 'title'=>'second'], ['type'=>'C', 'title'=>'third'], ['type'=>'A', 'title'=>'first']]
473
 * > sorted('type', $list)
474
 * ['type'=>'A', 'title'=>'first'] ['type'=>'B', 'title'=>'second']] ['type'=>'C', 'title'=>'third']
475
 *
476
 * @param string|\Closure $strategy
477
 * @param array|string|\Iterator $iterable
478
 * @param boolean $reverse
479
 * @return SortedIterator
480
 */
481 View Code Duplication
function sorted($strategy, $iterable, $reverse = false)
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...
482
{
483 35
    if (!is_bool($reverse)) {
484 1
        throw new \InvalidArgumentException('Argument $reverse must be boolean');
485
    }
486 34
    return new SortedIterator(
487
        conversions\mixed_to_value_getter($strategy),
488
        conversions\mixed_to_iterator($iterable),
489
        $reverse
490
    );
491
}
492
493
/**
494
 * Make an iterator that returns values from $iterable where the
495
 * $strategy determines that the values are not empty
496
 *
497
 * An optional $strategy may be given, this must be either null,
498
 * a string, or a \Closure.
499
 *
500
 * Following the (optional) $strategy, one or more $iterable instances
501
 * must be given.  They must be either an array, a string, or an \Iterator.
502
 *
503
 * @return FilterIterator
504
 */
505
function filter()
506
{
507
    // note, once we stop supporting php 5.5, we can rewrite the code below
508
    // to the filter(...$iterables) structure.
509
    // http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list
510
511 10
    $args = func_get_args();
512 10 View Code Duplication
    switch (sizeof($args)) {
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...
513 10
        case 1:
514 3
            $strategy = null;
515 3
            $iterable = $args[0];
516 3
            break;
517
518 7
        case 2:
519 6
            $strategy = $args[0];
520 6
            $iterable = $args[1];
521 6
            break;
522
523
        default:
524 1
            throw new \InvalidArgumentException('filter requires either one (iterable) or two (strategy, iterable) arguments');
525
    }
526
527 9
    $strategy = conversions\mixed_to_value_getter($strategy);
528
    $isValid = function ($value, $key) use ($strategy) {
529 3
        $tempVarPhp54 = $strategy($value, $key);
530 3
        return !empty($tempVarPhp54);
531 8
    };
532
533 8
    return new FilterIterator($isValid, conversions\mixed_to_iterator($iterable));
534
}
535
536
/**
537
 * Make an iterator that returns values from $iterable where the
538
 * $strategy determines that the values are not empty
539
 *
540
 * An $strategy must be given, this must be either null, a string,
541
 * or a \Closure.
542
 *
543
 * Following the $strategy, an optional $closure may be given, this
544
 * closure is called to determine is the value (which results from
545
 * $strategy) is or is not filtered.  Note that without providing a
546
 * $closure, the function !empty(...) is used instead.
547
 *
548
 * Following the (optional) $closure, one or more $iterable instances
549
 * must be given.  They must be either an array, a string, or an \Iterator.
550
 *
551
 * @return FilterIterator
552
 *
553
 * @deprecated Use filter() instead, will be removed in version 3.0
554
 */
555
function filterBy()
556
{
557
    // note, once we stop supporting php 5.5, we can rewrite the code below
558
    // to the filterBy(...$iterables) structure.
559
    // http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list
560
561
    $args = func_get_args();
562
    switch (sizeof($args)) {
563 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...
564
            $strategy = conversions\mixed_to_value_getter($args[0]);
565
            $closure = function ($value, $key) use ($strategy) {
566
                $tempVarPhp54 = call_user_func($strategy, $value, $key);
567
                return !empty($tempVarPhp54);
568
            };
569
            $iterable = conversions\mixed_to_iterator($args[1]);
570
            break;
571
572 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...
573
            $strategy = conversions\mixed_to_value_getter($args[0]);
574
            $userClosure = $args[1];
575
            $closure = function ($value, $key) use ($strategy, $userClosure) {
576
                return call_user_func($userClosure, call_user_func($strategy, $value, $key));
577
            };
578
            $iterable = conversions\mixed_to_iterator($args[2]);
579
            break;
580
581
        default:
582
            throw new \InvalidArgumentException('filterBy requires either two (strategy, iterable) or three (strategy, closure, iterable) arguments');
583
    }
584
585
    return new FilterIterator($closure, $iterable);
586
}
587
588
/**
589
 * Returns an iterator where one or more iterables are zipped together
590
 *
591
 * This function returns a list of tuples, where the i-th tuple contains
592
 * the i-th element from each of the argument sequences or iterables.
593
 *
594
 * The returned list is truncated in length to the length of the
595
 * shortest argument sequence.
596
 *
597
 * > zip([1, 2, 3], ['a', 'b', 'c'])
598
 * [1, 'a'] [2, 'b'] [3, 'c']
599
 *
600
 * @param array|string|\Iterator $iterable Additional $iterable parameters may follow
0 ignored issues
show
Documentation introduced by
There is no parameter named $iterable. Did you maybe mean $iterableA?

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

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

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

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

Loading history...
601
 * @return ZipIterator
602
 */
603 View Code Duplication
function zip($iterableA)
0 ignored issues
show
Unused Code introduced by
The parameter $iterableA is not used and could be removed.

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

Loading history...
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...
604
{
605
    // note, once we stop supporting php 5.5, we can rewrite the code below
606
    // to the zip(...$iterables) structure.
607
    // http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list
608
609 12
    $iterables = array_map(
610
        function ($iterable) {
611 12
            return conversions\mixed_to_iterator($iterable);
612 12
        },
613
        func_get_args()
614
    );
615 9
    $reflectorClass = new \ReflectionClass('\Zicht\Itertools\lib\ZipIterator');
616 9
    return $reflectorClass->newInstanceArgs($iterables);
617
}
618
619
/**
620
 * Returns an iterable with all the elements from $iterable reversed
621
 *
622
 * @param array|string|\Iterator $iterable
623
 * @return ReversedIterator
624
 */
625
function reversed($iterable)
626
{
627 7
    return new ReversedIterator(conversions\mixed_to_iterator($iterable));
628
}
629
630
/**
631
 * Returns an iterator where the values from $strategy are unique
632
 *
633
 * An optional $strategy may be given to specify the value which is used
634
 * to determine weather the element is unique.  When no $strategy is
635
 * given, the identity function is used, i.e. the value of the element
636
 * itself is used to determine weather the element is unique.
637
 *
638
 * Following the optional $strategy, a $iterable must be given.  Otherwise,
639
 * an \InvalidArgumentException will be raised.
640
 *
641
 * > unique([1, 1, 2, 2, 3, 3])
642
 * 1 2 3
643
 *
644
 * > unique('id', [['id' => 1, 'value' => 'a'], ['id' => 1, 'value' => 'b']])
645
 * ['id' => 1, 'value' => 'a']  # one element in this list
646
 *
647
 * @return UniqueIterator
648
 */
649
function unique()
650
{
651 16
    $args = func_get_args();
652 16 View Code Duplication
    switch (sizeof($args)) {
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...
653 16
        case 1:
654 8
            $strategy = null;
655 8
            $iterable = $args[0];
656 8
            break;
657
658 8
        case 2:
659 6
            $strategy = $args[0];
660 6
            $iterable = $args[1];
661 6
            break;
662
663
        default:
664 2
            throw new \InvalidArgumentException('unique requires either one (iterable) or two (strategy, iterable) arguments');
665
    }
666
667 14
    return new UniqueIterator(
668
        conversions\mixed_to_value_getter($strategy),
669
        conversions\mixed_to_iterator($iterable)
670
    );
671
}
672
673
/**
674
 * Returns an iterator where the values from $strategy are unique
675
 *
676
 * @param null|string|\Closure $strategy
677
 * @param array|string|\Iterator $iterable
678
 * @return UniqueIterator
679
 *
680
 * @deprecated use unique($strategy, $iterable) instead, will be removed in version 3.0
681
 */
682
function uniqueBy($strategy, $iterable)
683
{
684
    return new UniqueIterator(
685
        conversions\mixed_to_value_getter($strategy),
686
        conversions\mixed_to_iterator($iterable)
687
    );
688
}
689
690
/**
691
 * Returns true when one or more element of $iterable is not empty, otherwise returns false
692
 *
693
 * An optional $strategy may be given to specify the value which is used
694
 * to determine weather the element evaluates to true.  When no $strategy is
695
 * given, the identity function is used, i.e. the value of the element
696
 * itself is used to determine weather the element evaluates to true.
697
 *
698
 * Following the optional $strategy, an $iterable may be given.  Its type may
699
 * be either array, string, or \Iterator.  When no $iterable is given, false
700
 * is returned.
701
 *
702
 * > any([0, '', false])
703
 * false
704
 *
705
 * > any([1, null, 3])
706
 * true
707
 *
708
 * @return boolean
709
 */
710 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...
711
{
712 20
    $args = func_get_args();
713 20
    switch (sizeof($args)) {
714 20
        case 1:
715 9
            $strategy = conversions\mixed_to_value_getter(null);
716 9
            $iterable = conversions\mixed_to_iterator($args[0]);
717 6
            break;
718
719 11
        case 2:
720 9
            $strategy = conversions\mixed_to_value_getter($args[0]);
721 9
            $iterable = conversions\mixed_to_iterator($args[1]);
722 9
            break;
723
724
        default:
725 2
            throw new \InvalidArgumentException('any requires either one (iterable) or two (strategy, iterable) arguments');
726
    }
727
728 15
    foreach ($iterable as $item) {
729 13
        $tempVarPhp54 = call_user_func($strategy, $item);
730 13
        if (!empty($tempVarPhp54)) {
731 13
            return true;
732
        }
733
    }
734
735 7
    return false;
736
}
737
738
/**
739
 * Returns true when all elements of $iterable are not empty, otherwise returns false
740
 *
741
 * An optional $strategy may be given to specify the value which is used
742
 * to determine weather the element evaluates to true.  When no $strategy is
743
 * given, the identity function is used, i.e. the value of the element
744
 * itself is used to determine weather the element evaluates to true.
745
 *
746
 * Following the optional $strategy, an $iterable may be given.  Its type may
747
 * be either array, string, or \Iterator.  When no $iterable is given, true
748
 * is returned.
749
 *
750
 * > all([1, 'hello world', true])
751
 * true
752
 *
753
 * > all([1, null, 3])
754
 * false
755
 *
756
 * @return boolean
757
 */
758 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...
759
{
760 20
    $args = func_get_args();
761 20
    switch (sizeof($args)) {
762 20
        case 1:
763 9
            $strategy = conversions\mixed_to_value_getter(null);
764 9
            $iterable = conversions\mixed_to_iterator($args[0]);
765 6
            break;
766
767 11
        case 2:
768 9
            $strategy = conversions\mixed_to_value_getter($args[0]);
769 9
            $iterable = conversions\mixed_to_iterator($args[1]);
770 9
            break;
771
772
        default:
773 2
            throw new \InvalidArgumentException('all requires either one (iterable) or two (strategy, iterable) arguments');
774
    }
775
776 15
    foreach ($iterable as $item) {
777 13
        $tempVarPhp54 = call_user_func($strategy, $item);
778 13
        if (empty($tempVarPhp54)) {
779 13
            return false;
780
        }
781
    }
782
783 8
    return true;
784
}
785
786
/**
787
 * Make an iterator that contains a slice of $iterable
788
 *
789
 * The parameters $start and $end determine the range that will be taken
790
 * from the $iterable.  These values may be negative, in which case they
791
 * will indicate elements in $iterable starting at the end.
792
 *
793
 * > slice(['a', 'b', 'c', 'd', 'e', 1]
794
 * 'b', 'c', 'd', 'e'
795
 *
796
 * > slice(['a', 'b', 'c', 'd', 'e', -1]
797
 * 'e'
798
 *
799
 * > slice(['a', 'b', 'c', 'd', 'e', 1, 2]
800
 * 'b'
801
 *
802
 * > slice(['a', 'b', 'c', 'd', 'e', 1, -1]
803
 * 'b', 'c', 'd'
804
 *
805
 * @param array|string|\Iterator $iterable
806
 * @param integer $start
807
 * @param null|integer $end
808
 * @return SliceIterator
809
 */
810
function slice($iterable, $start, $end = null)
811
{
812 30
    if (!is_int($start)) {
813 3
        throw new \InvalidArgumentException('Argument $start must be an integer');
814
    }
815 27
    if (!(is_null($end) || is_int($end))) {
816 2
        throw new \InvalidArgumentException('Argument $end must be an integer or null');
817
    }
818 25
    return new SliceIterator(conversions\mixed_to_iterator($iterable), $start, $end);
819
}
820
821
/**
822
 * Returns the first element of $iterable or returns $default when $iterable is empty
823
 *
824
 * > first([1, 2, 3])
825
 * 1
826
 *
827
 * > first([])
828
 * null
829
 *
830
 * @param array|string|\Iterator $iterable
831
 * @param mixed $default
832
 * @return mixed
833
 */
834
function first($iterable, $default = null)
835
{
836 10
    $item = $default;
837 10
    foreach (conversions\mixed_to_iterator($iterable) as $item) {
838 3
        break;
839
    }
840 6
    return $item;
841
}
842
843
/**
844
 * Returns the last element of $iterable or returns $default when $iterable is empty
845
 *
846
 * > last([1, 2, 3])
847
 * 3
848
 *
849
 * > last([])
850
 * null
851
 *
852
 * @param array|string|\Iterator $iterable
853
 * @param mixed $default
854
 * @return mixed
855
 */
856
function last($iterable, $default = null)
857
{
858 10
    $item = $default;
859 10
    foreach (conversions\mixed_to_iterator($iterable) as $item) {
0 ignored issues
show
Unused Code introduced by
This foreach statement is empty and can be removed.

This check looks for foreach loops that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

Consider removing the loop.

Loading history...
860
    }
861 6
    return $item;
862
}
863
864
/**
865
 * Returns a IterableIterator providing a fluent interface to itertools
866
 *
867
 * > iterable([1, 2, 3])->filter(...)->map(...)->first(...)
868
 *
869
 * @param array|string|\Iterator $iterable
870
 * @return IterableIterator
871
 */
872
function iterable($iterable)
873
{
874 108
    return new IterableIterator(conversions\mixed_to_iterator($iterable));
875
}
876