Passed
Push — 1.x ( 8ab215...54e9f8 )
by Ulises Jeremias
02:30
created

Map::values()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
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
     * Returns a sequence of all the associated values in the Map.
259
     *
260
     * @return SequenceableInterface
261
     */
262
    public function values(): SequenceableInterface
263
    {
264
        return $this->pairs->map(function ($pair) {
265
            return $pair->value;
266
        });
267
    }
268
269
    /**
270
     * @inheritDoc
271
     */
272
    public function getIterator()
273
    {
274
        foreach ($this->pairs as $pair) {
275
            yield $pair->key => $pair->value;
276
        }
277
    }
278
279
    /**
280
     * Returns a representation to be used for var_dump and print_r.
281
     */
282
    public function __debugInfo()
283
    {
284
        return $this->pairs()->toArray();
285
    }
286
287
    /**
288
     * @inheritdoc
289
     */
290
    public function offsetSet($offset, $value)
291
    {
292
        $this->put($offset, $value);
293
    }
294
295
    /**
296
     * @inheritdoc
297
     *
298
     * @throws OutOfBoundsException
299
     */
300
    public function &offsetGet($offset)
301
    {
302
        $pair = $this->lookupKey($offset);
303
        if ($pair) {
304
            return $pair->value;
305
        }
306
        throw new OutOfBoundsException();
307
    }
308
309
    /**
310
     * @inheritdoc
311
     */
312
    public function offsetUnset($offset)
313
    {
314
        $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

314
        $this->/** @scrutinizer ignore-call */ 
315
               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...
315
    }
316
317
    /**
318
     * @inheritdoc
319
     */
320
    public function offsetExists($offset)
321
    {
322
        return $this->get($offset, null) !== null;
323
    }
324
}
325