Completed
Push — master ( a9170f...6467e6 )
by Roman
19:21 queued 08:22
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

5 Methods

Rating   Name   Duplication   Size   Complexity  
A Collection::sum() 0 9 2
A Collection::reverse() 0 3 1
A Collection::count() 0 3 1
A Collection::current() 0 3 1
A Collection::pop() 0 3 1
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\Append,
14
        Method\Chunk,
15
        Method\Column;
16
17
    /** @var string */
18
    private $type = null;
19
20
    /** @var array */
21
    protected $container = [];
22
23
    public function __construct(string $type, iterable ...$iterables)
24
    {
25
        static::validateObject($type);
26
27
        $this->type = $type;
28
29
        foreach ($iterables as $iterable) {
30
            foreach ($iterable as $index => $item) {
31
                $this->add($item, $index);
32
            }
33
        }
34
    }
35
36
    final public function type(): string
37
    {
38
        return $this->type;
39
    }
40
41
    public function offsetExists($offset): bool
42
    {
43
        return isset($this->container[$offset]);
44
    }
45
46
    public function offsetGet($offset): object
47
    {
48
        return $this->container[$offset];
49
    }
50
51
    public function offsetUnset($offset): void
52
    {
53
        if ($this->offsetExists($offset)) {
54
            unset($this->container[$offset]);
55
        }
56
    }
57
58
    public function getIterator(): \ArrayIterator
59
    {
60
        return new \ArrayIterator($this->container);
61
    }
62
63
    /**
64
     * @param mixed $index
65
     * @param mixed $value
66
     *
67
     * @throws Exception\InvalidElement
68
     */
69
    public function offsetSet($index, $value): void
70
    {
71
        $this->add($value, $index);
72
    }
73
74
    public function jsonSerialize(): array
75
    {
76
        return get_object_vars($this);
77
    }
78
79
    public function isCompatible(iterable $collection): bool
80
    {
81
        try {
82
            foreach ($collection as $item) {
83
                $this->validate($item);
84
            }
85
        } catch (Exception\InvalidElement $exception) {
86
            return false;
87
        }
88
89
        return true;
90
    }
91
92
    public function first(): object
93
    {
94
        reset($this->container);
95
96
        return $this->current();
97
    }
98
99
    public function last(): object
100
    {
101
        end($this->container);
102
103
        return $this->current();
104
    }
105
106
    /**
107
     * @param object $item
108
     *
109
     * @throws Exception\InvalidElement
110
     */
111
    public function validate(object $item): void
112
    {
113
        $type = $this->type();
114
115
        if (!$item instanceof $type) {
116
            throw new Exception\InvalidElement($item, $type);
117
        }
118
    }
119
120
    public function map(\Closure $closure, iterable ...$collections): array
121
    {
122
        $result = [];
123
        $count = $this->count();
124
        $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...
125
126
        foreach ($collections as $index => $collection) {
127
            if (!$this->isCompatible($collection)) {
128
                throw new Exception\IncompatibleIterable($collection, 'Given iterable object contain invalid element');
129
            }
130
131
            if ($count !== count($collection)) {
132
                throw new Exception\IncompatibleIterable(
133
                    $collection,
134
                    'Given iterable object must contain same count elements'
135
                );
136
            }
137
138
            foreach ($collection as $item) {
139
                $values[$index + 1][] = $item;
140
            }
141
        }
142
143
        foreach (range(0, $this->count() - 1) as $index) {
144
            $result[] = call_user_func(
145
                $closure,
146
                ...array_map(function (array $collection) use ($index) {
147
                    return $collection[$index];
148
                }, $values)
149
            );
150
        }
151
152
        return $result;
153
    }
154
155
    public function reverse(bool $preserveKeys = false): Collection
156
    {
157
        return new static($this->type(), array_reverse($this->container, $preserveKeys));
158
    }
159
    
160
    public function current()
161
    {
162
        return current($this->container);
163
    }
164
165
    public function pop(): object
166
    {
167
        return array_pop($this->container);
168
    }
169
170
    public function sum(\Closure $callback)
171
    {
172
        $sum = 0;
173
174
        foreach ($this as $element) {
175
            $sum += call_user_func($callback, $element);
176
        }
177
178
        return $sum;
179
    }
180
181
    public function count(): int
182
    {
183
        return count($this->container);
184
    }
185
186
    public function add($item, $index = null): void
187
    {
188
        $this->validate($item);
189
        $this->container[$index ?? $this->count()] = $item;
190
    }
191
192
    /**
193
     * @param string $name
194
     * @param array $arguments
195
     *
196
     * @return Collection
197
     */
198
    public static function __callStatic(string $name, array $arguments = [])
199
    {
200
        if (!empty($arguments) && is_array($arguments[0])) {
201
            $arguments = $arguments[0];
202
        }
203
204
        static::validateObject($name);
205
206
        reset($arguments);
207
208
        if (current($arguments) instanceof Collection) {
209
            return new static($name, ...$arguments);
210
        } else {
211
            return new static($name, $arguments);
212
        }
213
    }
214
215
    protected static function validateObject(string $type): void
216
    {
217
        if (!class_exists($type)) {
218
            throw new Exception\UnprocessedType($type);
219
        }
220
    }
221
}
222