Passed
Push — 1.x ( 54e9f8...70893a )
by Ulises Jeremias
02:25
created

Map::toArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php namespace Mbh\Collection;
2
3
/**
4
 * MBHFramework
5
 *
6
 * @link      https://github.com/MBHFramework/mbh-framework
7
 * @copyright Copyright (c) 2017 Ulises Jeremias Cornejo Fandos
8
 * @license   https://github.com/MBHFramework/mbh-framework/blob/master/LICENSE (MIT License)
9
 */
10
11
use Mbh\Collection\Interfaces\Collection as CollectionInterface;
12
use Mbh\Collection\Interfaces\Hashable as HashableInterface;
13
use Mbh\Collection\Interfaces\Sequenceable as SequenceableInterface;
14
use Traversable;
15
use ArrayAccess;
16
use IteratorAggregate;
17
use OutOfBoundsException;
18
use OutOfRangeException;
19
use UnderflowException;
20
21
/**
22
 * A Map is a sequential collection of key-value pairs, almost identical to an
23
 * array used in a similar context. Keys can be any type, but must be unique.
24
 *
25
 * @package structures
26
 * @author Ulises Jeremias Cornejo Fandos <[email protected]>
27
 */
28
class Map implements ArrayAccess, CollectionInterface, IteratorAggregate
29
{
30
    use Traits\Collection;
31
    use Traits\Functional;
32
    use Traits\SquaredCapacity;
33
34
    const MIN_CAPACITY = 8.0;
35
36
    /**
37
     * @var FixedArray internal array to store pairs
38
     */
39
    private $pairs;
40
41
    /**
42
     * Creates a new instance.
43
     *
44
     * @param array|Traversable $pairs
45
     */
46
    public function __construct($pairs = [])
47
    {
48
        FixedArray::fromArray([]);
49
50
        $this->putAll($pairs);
51
    }
52
53
    /**
54
     * @inheritDoc
55
     */
56
    public function clear()
57
    {
58
        $this->pairs->clear();
59
        $this->capacity = self::MIN_CAPACITY;
60
    }
61
62
    /**
63
     * @inheritDoc
64
     */
65
    public function count(): int
66
    {
67
        return count($this->pairs);
68
    }
69
70
    /**
71
     * Return the first Pair from the Map
72
     *
73
     * @return Pair
74
     *
75
     * @throws UnderflowException
76
     */
77
    public function first(): Pair
78
    {
79
        if ($this->isEmpty()) {
80
            throw new UnderflowException();
81
        }
82
83
        return $this->pairs->first();
84
    }
85
86
    /**
87
     * Returns the value associated with a key, or an optional default if the
88
     * key is not associated with a value.
89
     *
90
     * @param mixed $key
91
     * @param mixed $default
92
     *
93
     * @return mixed The associated value or fallback default if provided.
94
     *
95
     * @throws OutOfBoundsException if no default was provided and the key is
96
     *                               not associated with a value.
97
     */
98
    public function get($key, $default = null)
99
    {
100
        if (($pair = $this->lookupKey($key))) {
101
            return $pair->value;
102
        }
103
104
        // Check if a default was provided.
105
        if (func_num_args() === 1) {
106
            throw new OutOfBoundsException();
107
        }
108
109
        return $default;
110
    }
111
112
    /**
113
     * Returns whether an association a given key exists.
114
     *
115
     * @param mixed $key
116
     *
117
     * @return bool
118
     */
119
    public function hasKey($key): bool
120
    {
121
        return $this->lookupKey($key) !== null;
122
    }
123
124
    /**
125
     * Returns whether an association for a given value exists.
126
     *
127
     * @param mixed $value
128
     *
129
     * @return bool
130
     */
131
    public function hasValue($value): bool
132
    {
133
        return $this->lookupValue($value) !== null;
134
    }
135
136
    /**
137
     * Returns a set of all the keys in the map.
138
     *
139
     * @return Set
140
     */
141
    public function keys(): Set
142
    {
143
        return new Set($this->pairs->map(function ($pair) {
0 ignored issues
show
Unused Code introduced by
The call to Mbh\Collection\Set::__construct() has too many arguments starting with $this->pairs->map(function(...) { /* ... */ }). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

143
        return /** @scrutinizer ignore-call */ new Set($this->pairs->map(function ($pair) {

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
144
            return $pair->key;
145
        }));
146
    }
147
148
    /**
149
     * Determines whether two keys are equal.
150
     *
151
     * @param mixed $a
152
     * @param mixed $b
153
     *
154
     * @return bool
155
     */
156
    private function keysAreEqual($a, $b): bool
157
    {
158
        if (is_object($a) && $a instanceof HashableInterface) {
159
            return get_class($a) === get_class($b) && $a->equals($b);
160
        }
161
162
        return $a === $b;
163
    }
164
165
    /**
166
     * Return the last Pair from the Map
167
     *
168
     * @return Pair
169
     *
170
     * @throws UnderflowException
171
     */
172
    public function last(): Pair
173
    {
174
        if ($this->isEmpty()) {
175
            throw new UnderflowException();
176
        }
177
178
        return $this->pairs->last();
179
    }
180
181
182
    /**
183
     * Attempts to look up a key in the table.
184
     *
185
     * @param $key
186
     *
187
     * @return Pair|null
188
     */
189
    private function lookupKey($key)
190
    {
191
        foreach ($this->pairs as $pair) {
192
            if ($this->keysAreEqual($pair->key, $key)) {
193
                return $pair;
194
            }
195
        }
196
    }
197
198
    /**
199
     * Attempts to look up a key in the table.
200
     *
201
     * @param $value
202
     *
203
     * @return Pair|null
204
     */
205
    private function lookupValue($value)
206
    {
207
        foreach ($this->pairs as $pair) {
208
            if ($pair->value === $value) {
209
                return $pair;
210
            }
211
        }
212
    }
213
214
    /**
215
     * Returns a sequence of pairs representing all associations.
216
     *
217
     * @return SequenceableInterface
218
     */
219
    public function pairs(): SequenceableInterface
220
    {
221
        return $this->pairs->map(function ($pair) {
222
            return $pair->copy();
223
        });
224
    }
225
226
    /**
227
     * Associates a key with a value, replacing a previous association if there
228
     * was one.
229
     *
230
     * @param mixed $key
231
     * @param mixed $value
232
     */
233
    public function put($key, $value)
234
    {
235
        $pair = $this->lookupKey($key);
236
        if ($pair) {
237
            $pair->value = $value;
238
        } else {
239
            $this->checkCapacity();
240
            $this->pairs[] = new Pair($key, $value);
241
        }
242
    }
243
244
    /**
245
     * Creates associations for all keys and corresponding values of either an
246
     * array or iterable object.
247
     *
248
     * @param Traversable|array $values
249
     */
250
    public function putAll($values)
251
    {
252
        foreach ($values as $key => $value) {
253
            $this->put($key, $value);
254
        }
255
    }
256
257
    /**
258
     * Return the pair at a specified position in the Map
259
     *
260
     * @param int $position
261
     *
262
     * @return Pair
263
     *
264
     * @throws OutOfRangeException
265
     */
266
    public function skip(int $position): Pair
267
    {
268
        if ($position < 0 || $position >= count($this->pairs)) {
269
            throw new OutOfRangeException();
270
        }
271
272
        return $this->pairs[$position]->copy();
273
    }
274
275
    /**
276
     * @inheritDoc
277
     */
278
    public function toArray(): array
279
    {
280
        $array = [];
281
        foreach ($this->pairs as $pair) {
282
            $array[$pair->key] = $pair->value;
283
        }
284
285
        return $array;
286
    }
287
288
    /**
289
     * Returns a sequence of all the associated values in the Map.
290
     *
291
     * @return SequenceableInterface
292
     */
293
    public function values(): SequenceableInterface
294
    {
295
        return $this->pairs->map(function ($pair) {
296
            return $pair->value;
297
        });
298
    }
299
300
    /**
301
     * @inheritDoc
302
     */
303
    public function getIterator()
304
    {
305
        foreach ($this->pairs as $pair) {
306
            yield $pair->key => $pair->value;
307
        }
308
    }
309
310
    /**
311
     * Returns a representation to be used for var_dump and print_r.
312
     */
313
    public function __debugInfo()
314
    {
315
        return $this->pairs()->toArray();
316
    }
317
318
    /**
319
     * @inheritdoc
320
     */
321
    public function offsetSet($offset, $value)
322
    {
323
        $this->put($offset, $value);
324
    }
325
326
    /**
327
     * @inheritdoc
328
     *
329
     * @throws OutOfBoundsException
330
     */
331
    public function &offsetGet($offset)
332
    {
333
        $pair = $this->lookupKey($offset);
334
        if ($pair) {
335
            return $pair->value;
336
        }
337
        throw new OutOfBoundsException();
338
    }
339
340
    /**
341
     * @inheritdoc
342
     */
343
    public function offsetUnset($offset)
344
    {
345
        $this->remove($offset, null);
0 ignored issues
show
Bug introduced by
The method remove() does not exist on Mbh\Collection\Map. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

345
        $this->/** @scrutinizer ignore-call */ 
346
               remove($offset, null);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
346
    }
347
348
    /**
349
     * @inheritdoc
350
     */
351
    public function offsetExists($offset)
352
    {
353
        return $this->get($offset, null) !== null;
354
    }
355
}
356