Completed
Pull Request — master (#39)
by Roman
09:41
created

Collection::map()   A

Complexity

Conditions 6
Paths 8

Size

Total Lines 33
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 33
rs 9.0111
c 0
b 0
f 0
cc 6
nc 8
nop 2
1
<?php
2
3
namespace kartavik\Support;
4
5
use kartavik\Support\Method;
6
7
/**
8
 * Class Collection
9
 * @package kartavik\Support\
10
 */
11
class Collection implements \ArrayAccess, \Countable, \IteratorAggregate, \JsonSerializable
12
{
13
    use Method\Column;
14
15
    /** @var Strict */
16
    private $strict = null;
17
18
    /** @var array */
19
    protected $container = [];
20
21
    /**
22
     * Collection constructor.
23
     *
24
     * @param Strict $type
25
     * @param iterable ...$iterables
26
     *
27
     * @throws Exception\Validation
28
     */
29
    public function __construct(Strict $type, iterable ...$iterables)
30
    {
31
        $this->strict = $type;
32
33
        foreach ($iterables as $iterable) {
34
            foreach ($iterable as $index => $item) {
35
                $this->append($item);
36
            }
37
        }
38
    }
39
40
    final public function type(): Strict
41
    {
42
        return $this->strict;
43
    }
44
45
    public function offsetExists($offset): bool
46
    {
47
        return isset($this->container[$offset]);
48
    }
49
50
    public function offsetGet($offset)
51
    {
52
        return $this->container[$offset];
53
    }
54
55
    public function offsetUnset($offset): void
56
    {
57
        unset($this->container[$offset]);
58
    }
59
60
    public function getIterator(): \ArrayIterator
61
    {
62
        return new \ArrayIterator($this->container);
63
    }
64
65
    /**
66
     * @param mixed $index
67
     * @param mixed $value
68
     *
69
     * @throws Exception\Validation
70
     */
71
    public function offsetSet($index, $value): void
72
    {
73
        $this->add($value, $index);
74
    }
75
76
    public function jsonSerialize(): array
77
    {
78
        return $this->container;
79
    }
80
81
    /**
82
     * @param mixed ...$var
83
     *
84
     * @return Collection
85
     * @throws Exception\Validation
86
     */
87
    public function append(...$var): Collection
88
    {
89
        foreach ($var as $item) {
90
            $this->add($item);
91
        }
92
93
        return $this;
94
    }
95
96
    public function isCompatible(iterable $collection): bool
97
    {
98
        try {
99
            foreach ($collection as $item) {
100
                $this->validate($item);
101
            }
102
        } catch (Exception\Validation $exception) {
103
            return false;
104
        }
105
106
        return true;
107
    }
108
109
    public function first()
110
    {
111
        reset($this->container);
112
113
        return $this->current();
114
    }
115
116
    public function last()
117
    {
118
        end($this->container);
119
120
        return $this->current();
121
    }
122
123
    /**
124
     * @param mixed $item
125
     *
126
     * @throws Exception\Validation
127
     */
128
    public function validate($item): void
129
    {
130
        $this->type()->validate($item);
131
    }
132
133
    public function chunk(int $size): Collection
134
    {
135
        return Collection::{Collection::class}(array_map(function ($chunk) {
136
            return new Collection($this->type(), $chunk);
137
        }, array_chunk($this->container, $size)));
138
    }
139
140
    public function map(\Closure $closure, iterable ...$collections): array
141
    {
142
        $result = [];
143
        $count = $this->count();
144
        $values[] = array_values($this->container);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$values was never initialized. Although not strictly required by PHP, it is generally a good practice to add $values = array(); before regardless.
Loading history...
145
146
        foreach ($collections as $index => $collection) {
147
            if (!$this->isCompatible($collection)) {
148
                throw new Exception\IncompatibleIterable($collection, 'Given iterable object contain invalid element');
149
            }
150
151
            if ($count !== count($collection)) {
152
                throw new Exception\IncompatibleIterable(
153
                    $collection,
154
                    'Given iterable object must contain same count elements'
155
                );
156
            }
157
158
            foreach ($collection as $item) {
159
                $values[$index + 1][] = $item;
160
            }
161
        }
162
163
        foreach (range(0, $this->count() - 1) as $index) {
164
            $result[] = call_user_func(
165
                $closure,
166
                ...array_map(function (array $collection) use ($index) {
167
                    return $collection[$index];
168
                }, $values)
169
            );
170
        }
171
172
        return $result;
173
    }
174
175
    /**
176
     * @param bool $preserveKeys
177
     *
178
     * @return Collection
179
     * @throws Exception\Validation
180
     */
181
    public function reverse(bool $preserveKeys = false): Collection
182
    {
183
        /** @var Collection $collection */
184
        $collection = static::{$this->strict->t()}();
185
186
        foreach (array_reverse($this->container, $preserveKeys) as $index => $item) {
187
            $collection->add($item, $index);
188
        }
189
190
        return $collection;
191
    }
192
193
    public function current()
194
    {
195
        return current($this->container);
196
    }
197
198
    public function pop()
199
    {
200
        return array_pop($this->container);
201
    }
202
203
    public function sum(\Closure $callback)
204
    {
205
        $sum = 0;
206
207
        foreach ($this as $element) {
208
            $sum += call_user_func($callback, $element);
209
        }
210
211
        return $sum;
212
    }
213
214
    public function count(): int
215
    {
216
        return count($this->container);
217
    }
218
219
    /**
220
     * @param $item
221
     * @param null $index
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $index is correct as it would always require null to be passed?
Loading history...
222
     *
223
     * @throws Exception\Validation
224
     */
225
    public function add($item, $index = null): void
226
    {
227
        $this->validate($item);
228
        $this->container[$index ?? $this->count()] = $item;
229
    }
230
231
    /**
232
     * @param string $name
233
     * @param array $arguments
234
     *
235
     * @return Collection
236
     * @throws Exception\Validation
237
     */
238
    public static function __callStatic(string $name, array $arguments = [])
239
    {
240
        return new static(Strict::{$name}(), ...$arguments);
241
    }
242
}
243