Completed
Branch master (e87af1)
by Karsten
02:43
created

Psi::toKeyValueArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 1 Features 1
Metric Value
c 1
b 1
f 1
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * File was created 06.05.2015 16:09
4
 *
5
 * @author Karsten J. Gerber <[email protected]>
6
 */
7
namespace PeekAndPoke\Component\Psi;
8
9
use PeekAndPoke\Component\Psi\Exception\PsiException;
10
use PeekAndPoke\Component\Psi\Functions\Unary\Mapper\IdentityMapper;
11
use PeekAndPoke\Component\Psi\Interfaces\Functions\BinaryFunctionInterface;
12
use PeekAndPoke\Component\Psi\Interfaces\Functions\UnaryFunctionInterface;
13
use PeekAndPoke\Component\Psi\Interfaces\Operation\TerminalOperationInterface;
14
use PeekAndPoke\Component\Psi\Interfaces\PsiFactoryInterface;
15
use PeekAndPoke\Component\Psi\Operation\FullSet\FlattenOperation;
16
use PeekAndPoke\Component\Psi\Operation\FullSet\GroupOperation;
17
use PeekAndPoke\Component\Psi\Operation\FullSet\KeyReverseSortOperation;
18
use PeekAndPoke\Component\Psi\Operation\FullSet\KeySortOperation;
19
use PeekAndPoke\Component\Psi\Operation\FullSet\ReverseOperation;
20
use PeekAndPoke\Component\Psi\Operation\FullSet\ReverseSortOperation;
21
use PeekAndPoke\Component\Psi\Operation\FullSet\SortByOperation;
22
use PeekAndPoke\Component\Psi\Operation\FullSet\SortOperation;
23
use PeekAndPoke\Component\Psi\Operation\FullSet\UniqueOperation;
24
use PeekAndPoke\Component\Psi\Operation\FullSet\UserKeySortOperation;
25
use PeekAndPoke\Component\Psi\Operation\FullSet\UserSortOperation;
26
use PeekAndPoke\Component\Psi\Operation\Intermediate\EmptyIntermediateOp;
27
use PeekAndPoke\Component\Psi\Operation\Intermediate\Functional\EachOperation;
28
use PeekAndPoke\Component\Psi\Operation\Intermediate\Functional\MapOperation;
29
use PeekAndPoke\Component\Psi\Operation\Intermediate\Predicate\AnyMatchPredicate;
30
use PeekAndPoke\Component\Psi\Operation\Intermediate\Predicate\FilterKeyPredicate;
31
use PeekAndPoke\Component\Psi\Operation\Intermediate\Predicate\FilterPredicate;
32
use PeekAndPoke\Component\Psi\Operation\Intermediate\Predicate\FilterValueKeyPredicate;
33
use PeekAndPoke\Component\Psi\Operation\Terminal\AverageOperation;
34
use PeekAndPoke\Component\Psi\Operation\Terminal\CollectOperation;
35
use PeekAndPoke\Component\Psi\Operation\Terminal\CollectToArrayOperation;
36
use PeekAndPoke\Component\Psi\Operation\Terminal\CollectToKeyValueArrayOperation;
37
use PeekAndPoke\Component\Psi\Operation\Terminal\CollectToMapOperation;
38
use PeekAndPoke\Component\Psi\Operation\Terminal\CountOperation;
39
use PeekAndPoke\Component\Psi\Operation\Terminal\GetFirstOperation;
40
use PeekAndPoke\Component\Psi\Operation\Terminal\JoinOperation;
41
use PeekAndPoke\Component\Psi\Operation\Terminal\MaxOperation;
42
use PeekAndPoke\Component\Psi\Operation\Terminal\MinOperation;
43
use PeekAndPoke\Component\Psi\Operation\Terminal\SumOperation;
44
45
/** @noinspection SingletonFactoryPatternViolationInspection */
46
/**
47
 * Psi the Php Streams Api Implementation (inspired by Java Streams Api)
48
 *
49
 * @author Karsten J. Gerber <[email protected]>
50
 */
51
class Psi
52
{
53
    /** @var array */
54
    private $inputs;
55
56
    /** @var \ArrayIterator */
57
    private $operationChain;
58
    /** @var PsiFactoryInterface */
59
    private $factory;
60
61
    private $options;
62
63
    /**
64
     * @param mixed $_ Everything that can be iterated, Provide as many params as you want (from 1 to n)
65
     *
66
     * @return Psi
67
     * @throws PsiException
68
     */
69 70
    public static function it($_ = null)
70
    {
71 70
        return new Psi(func_get_args());
72
    }
73
74
    /**
75
     * @param array $inputs
76
     */
77 70
    protected function __construct($inputs)
78
    {
79 70
        $this->operationChain = new \ArrayIterator();
80 70
        $this->factory        = new PsiFactory();
81 70
        $this->options        = new PsiOptions();
82
83 70
        $this->inputs = $inputs;
84 70
    }
85
86
    ////  CONFIGURATION METHODS  ///////////////////////////////////////////////////////////////////////////////////////
87
88
    /**
89
     * @param PsiFactoryInterface $factory
90
     *
91
     * @return $this
92
     */
93
    public function useFactory(PsiFactoryInterface $factory)
94
    {
95
        $this->factory = $factory;
96
97
        return $this;
98
    }
99
100
    /**
101
     * Configure if the keys should be preserved when we have multiple inputs.
102
     *
103
     * Depending on the inputs, preserving the keys will most likely lead to conflicts in the operation like
104
     * - sort
105
     * - unique
106
     * Thus the outcome of these operations will be unpredicted.
107
     *
108
     * By default this option is turned OFF. So for multiple inputs the keys will get lost. On the other hand all
109
     * values will survive all operations.
110
     *
111
     * @param bool $preserve
112
     *
113
     * @return $this
114
     */
115
    public function preserveKeysOfMultipleInputs($preserve = true)
116
    {
117
        $this->options->setPreserveKeysOfMultipleInputs($preserve);
118
119
        return $this;
120
    }
121
122
    ////  INTERMEDIATE OPERATIONS - working on one item at a time  /////////////////////////////////////////////////////
123
124
    /**
125
     * @param \Closure|UnaryFunctionInterface $unaryFunction
126
     *
127
     * @return $this
128
     */
129 8
    public function filter($unaryFunction)
130
    {
131 8
        $this->operationChain->append(new FilterPredicate($unaryFunction));
132
133 8
        return $this;
134
    }
135
136
    /**
137
     * @param \Closure|UnaryFunctionInterface $unaryFunction
138
     *
139
     * @return $this
140
     */
141 5
    public function filterKey($unaryFunction)
142
    {
143 5
        $this->operationChain->append(new FilterKeyPredicate($unaryFunction));
144
145 5
        return $this;
146
    }
147
148
    /**
149
     * @param \Closure|BinaryFunctionInterface $biPredicate
150
     *
151
     * @return $this
152
     */
153 5
    public function filterValueKey($biPredicate)
154
    {
155 5
        $this->operationChain->append(new FilterValueKeyPredicate($biPredicate));
156
157 5
        return $this;
158
    }
159
160
    /**
161
     * @param \Closure|UnaryFunctionInterface $unaryFunction
162
     *
163
     * @return $this
164
     */
165 7
    public function anyMatch($unaryFunction)
166
    {
167 7
        $this->operationChain->append(new AnyMatchPredicate($unaryFunction));
168
169 7
        return $this;
170
    }
171
172
    /**
173
     * @param \Closure|BinaryFunctionInterface $biFunction
174
     *
175
     * @return $this
176
     */
177 4
    public function each($biFunction)
178
    {
179 4
        $this->operationChain->append(new EachOperation($biFunction));
180
181 4
        return $this;
182
    }
183
184
    /**
185
     * @param \Closure|BinaryFunctionInterface $function
186
     *
187
     * @return $this
188
     */
189 12
    public function map($function)
190
    {
191 12
        $this->operationChain->append(new MapOperation($function));
192
193 12
        return $this;
194
    }
195
196
    ////  FULL SET OPERATIONS - working on all items at a time  ////////////////////////////////////////////////////////
197
198
    /**
199
     * Flattens the input recursively into a no longer nested iterator.
200
     *
201
     * Example: [1, [2, [3, 4], 5], 6] will become [1, 2, 3, 4, 5, 6]
202
     *
203
     * @return $this
204
     */
205 4
    public function flatten()
206
    {
207 4
        $this->operationChain->append(new FlattenOperation());
208
209 4
        return $this;
210
    }
211
212
    /**
213
     * @param UnaryFunctionInterface|\Closure $function
214
     *
215
     * @return $this
216
     */
217
    public function group($function)
218
    {
219
        $this->operationChain->append(new GroupOperation($function));
220
221
        return $this;
222
    }
223
224
    /**
225
     * @param int|null $sortFlags
226
     *
227
     * @see sort()
228
     *
229
     * @return $this
230
     */
231 3
    public function sort($sortFlags = null)
232
    {
233 3
        $this->operationChain->append(new SortOperation($sortFlags));
234
235 3
        return $this;
236
    }
237
238
    /**
239
     * @param UnaryFunctionInterface|\Closure $function Return the value used for comparison
240
     *
241
     * @return $this
242
     */
243
    public function sortBy($function)
244
    {
245
        $this->operationChain->append(new SortByOperation($function));
246
247
        return $this;
248
    }
249
250
    /**
251
     * @param int|null $sortFlags
252
     *
253
     * @see sort()
254
     *
255
     * @return $this
256
     */
257 1
    public function rsort($sortFlags = null)
258
    {
259 1
        $this->operationChain->append(new ReverseSortOperation($sortFlags));
260
261 1
        return $this;
262
    }
263
264
    /**
265
     * @param int|null $sortFlags
266
     *
267
     * @see ksort()
268
     *
269
     * @return $this
270
     */
271 1
    public function ksort($sortFlags = null)
272
    {
273 1
        $this->operationChain->append(new KeySortOperation($sortFlags));
274
275 1
        return $this;
276
    }
277
278
    /**
279
     * @param int|null $sortFlags
280
     *
281
     * @return $this
282
     */
283 1
    public function krsort($sortFlags = null)
284
    {
285 1
        $this->operationChain->append(new KeyReverseSortOperation($sortFlags));
286
287 1
        return $this;
288
    }
289
290
    /**
291
     * @param BinaryFunctionInterface|\Closure $biFunction
292
     *
293
     * @return $this
294
     */
295 2
    public function usort($biFunction)
296
    {
297 2
        $this->operationChain->append(new UserSortOperation($biFunction));
298
299 2
        return $this;
300
    }
301
302
    /**
303
     * @param int|null $sortFlags
304
     *
305
     * @return $this
306
     */
307 1
    public function unique($sortFlags = null)
308
    {
309 1
        $this->operationChain->append(new UniqueOperation($sortFlags));
310
311 1
        return $this;
312
    }
313
314
    /**
315
     * @param BinaryFunctionInterface|\Closure $biFunction
316
     *
317
     * @return $this
318
     */
319 1
    public function uksort($biFunction)
320
    {
321 1
        $this->operationChain->append(new UserKeySortOperation($biFunction));
322
323 1
        return $this;
324
    }
325
326
    /**
327
     * @return $this
328
     */
329 1
    public function reverse()
330
    {
331 1
        $this->operationChain->append(new ReverseOperation());
332
333 1
        return $this;
334
    }
335
336
    ////  TERMINAL OPERATIONS - generating output and ending the chain  ////////////////////////////////////////////////
337
338
    /**
339
     * @return \Iterator
340
     */
341 33
    public function collect()
342
    {
343 33
        return $this->solveOperationsAndApplyTerminal(new CollectOperation());
344
    }
345
346
    /**
347
     * Terminal operation that will collect a "real" array with numeric keys 0, 1, 2, ...
348
     *
349
     * The original keys will be lost. If you need the original keys please use toKeyValueArray()
350
     *
351
     * @see Psi::toKeyValueArray()
352
     *
353
     * @return array
354
     */
355 24
    public function toArray()
356
    {
357 24
        return $this->solveOperationsAndApplyTerminal(new CollectToArrayOperation());
358
    }
359
360
    /**
361
     * Terminal operation that will collect a php array while keeping the keys.
362
     *
363
     * Use this over toArray() if keeping the keys is necessary
364
     *
365
     * @see Psi::toArray()
366
     *
367
     * @return array
368
     */
369 3
    public function toKeyValueArray()
370
    {
371 3
        return $this->solveOperationsAndApplyTerminal(new CollectToKeyValueArrayOperation());
372
    }
373
374
    /**
375
     * @param BinaryFunctionInterface|Callable $keyMapper   Maps the value down to a string or number
376
     * @param BinaryFunctionInterface|Callable $valueMapper If not given the value will not be changed
377
     *
378
     * @return array
379
     */
380
    public function toMap($keyMapper, $valueMapper = null)
381
    {
382
        return $this->solveOperationsAndApplyTerminal(
383
            new CollectToMapOperation(
384
                $keyMapper,
385
                $valueMapper ? $valueMapper : new IdentityMapper()
386
            )
387
        );
388
    }
389
390
    /**
391
     * @param string $delimiter
392
     *
393
     * @return string
394
     */
395 1
    public function join($delimiter)
396
    {
397 1
        return $this->solveOperationsAndApplyTerminal(new JoinOperation($delimiter));
398
    }
399
400
    /**
401
     * @param mixed $default
402
     *
403
     * @return mixed
404
     */
405 2
    public function getFirst($default = null)
406
    {
407 2
        return $this->solveOperationsAndApplyTerminal(new GetFirstOperation($default));
408
    }
409
410
    /**
411
     * @return float
412
     */
413 1
    public function min()
414
    {
415 1
        return $this->solveOperationsAndApplyTerminal(new MinOperation());
416
    }
417
418
    /**
419
     * @return float
420
     */
421 1
    public function max()
422
    {
423 1
        return $this->solveOperationsAndApplyTerminal(new MaxOperation());
424
    }
425
426
    /**
427
     * @return float
428
     */
429 2
    public function sum()
430
    {
431 2
        return $this->solveOperationsAndApplyTerminal(new SumOperation());
432
    }
433
434
    /**
435
     * @return float
436
     */
437 2
    public function avg()
438
    {
439 2
        return $this->solveOperationsAndApplyTerminal(new AverageOperation());
440
    }
441
442
    /**
443
     * @return int
444
     */
445 1
    public function count()
446
    {
447 1
        return $this->solveOperationsAndApplyTerminal(new CountOperation());
448
    }
449
450
    ////  PRIVATE METHODS  /////////////////////////////////////////////////////////////////////////////////////////////
451
452
    /**
453
     * @param TerminalOperationInterface $terminal
454
     *
455
     * @return mixed
456
     */
457 70
    private function solveOperationsAndApplyTerminal(TerminalOperationInterface $terminal)
458
    {
459
        // When we have not a single operation in the chain we add a dummy one, in order to map down multiple input
460
        // living inside the \AppendIterator (in case we have multiple inputs)
461 70
        if (count($this->operationChain) === 0) {
462 22
            $this->operationChain->append(new EmptyIntermediateOp());
463
        }
464
465 70
        $iterator = $this->factory->createIterator($this->inputs, $this->options);
466 70
        $solver   = $this->factory->createSolver();
467
468 70
        $result = $solver->solve($this->operationChain, $iterator);
469
470 70
        return $terminal->apply($result);
471
    }
472
}
473