Completed
Push — master ( 3c3d94...73c299 )
by Nate
02:10
created

HashMap::getKeys()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 0
crap 1
1
<?php
2
/*
3
 * Copyright (c) Nate Brunette.
4
 * Distributed under the MIT License (http://opensource.org/licenses/MIT)
5
 */
6
7
namespace Tebru\Collection;
8
9
use OutOfRangeException;
10
11
/**
12
 * Class HashMap
13
 *
14
 * [@see MapInterface] implementation
15
 *
16
 * @author Nate Brunette <[email protected]>
17
 */
18
class HashMap extends AbstractMap
19
{
20
    /**
21
     * An array of [@see MapEntry] elements
22
     *
23
     * @var MapEntry[]
24
     */
25
    protected $elements = [];
26
27
    /**
28
     * Constructor
29
     *
30
     * @param array $map
31
     */
32 15
    public function __construct(array $map = [])
33
    {
34 15
        foreach ($map as $key => $value) {
35 1
            $this->put($key, $value);
36
        }
37 15
    }
38
39
    /**
40
     * Removes all mappings from map
41
     *
42
     * @return void
43
     */
44 2
    public function clear()
45
    {
46 2
        $this->elements = [];
47 2
    }
48
49
    /**
50
     * Returns true if the key exists in the lookup table
51
     *
52
     * @param mixed $key
53
     * @return bool
54
     */
55 57
    public function containsKey($key): bool
56
    {
57 57
        return array_key_exists($this->hashCode($key), $this->elements);
58
    }
59
60
    /**
61
     * Returns true if the value exists in the map
62
     *
63
     * @param mixed $value
64
     * @return bool
65
     */
66 2
    public function containsValue($value): bool
67
    {
68
        return $this->exists(function (MapEntry $mapEntry) use ($value) {
69 2
            return $mapEntry->value === $value;
70 2
        });
71
    }
72
73
    /**
74
     * Return a set representation of map
75
     *
76
     * If a set is passed in, that set will be populated, otherwise
77
     * a default set will be used.
78
     *
79
     * @param SetInterface $set
80
     * @return SetInterface
81
     */
82 11
    public function entrySet(SetInterface $set = null): SetInterface
83
    {
84 11
        $set = $set ?? new HashSet();
85 11
        $set->addAllArray($this->elements);
86
87 11
        return $set;
88
    }
89
90
    /**
91
     * Get the value at the specified key
92
     *
93
     * @param mixed $key
94
     * @return mixed
95
     * @throws \OutOfRangeException if the key doesn't exist
96
     */
97 14
    public function get($key)
98
    {
99 14
        $hashedKey = $this->hashCode($key);
100
101 14
        if (!$this->containsKey($key)) {
102 1
            throw new OutOfRangeException(sprintf('Tried to access array at key "%s"', $hashedKey));
103
        }
104
105 13
        return $this->elements[$hashedKey]->value;
106
    }
107
108
    /**
109
     * Returns true if the map is empty
110
     *
111
     * @return bool
112
     */
113 2
    public function isEmpty(): bool
114
    {
115 2
        return 0 === $this->count();
116
    }
117
118
    /**
119
     * Returns a set of the keys in the map
120
     *
121
     * If a set is passed in, that set will be populated, otherwise
122
     * a default set will be used.
123
     *
124
     * @param SetInterface $set
125
     * @return SetInterface
126
     */
127 2
    public function keySet(SetInterface $set = null): SetInterface
128
    {
129 2
        if (null === $set) {
130 1
            return new HashSet($this->getKeys());
131
        }
132
133 1
        $set->addAllArray($this->getKeys());
134
135 1
        return $set;
136
    }
137
138
    /**
139
     * Returns the previous value or null if there was no value
140
     *
141
     * @param mixed $key
142
     * @param mixed $value
143
     * @return mixed
144
     */
145 57
    public function put($key, $value)
146
    {
147 57
        $oldValue = $this->remove($key);
148 57
        $this->elements[$this->hashCode($key)] = new MapEntry($key, $value);
149
150 57
        return false === $oldValue ? null : $oldValue;
151
    }
152
153
    /**
154
     * Remove the mapping for the key and returns the previous value
155
     * or null
156
     *
157
     * @param mixed $key
158
     * @return mixed
159
     */
160 57
    public function remove($key)
161
    {
162 57
        if (!$this->containsKey($key)) {
163 57
            return false;
164
        }
165
166 10
        $hashedKey = $this->hashCode($key);
167
        /** @noinspection ExceptionsAnnotatingAndHandlingInspection */
168 10
        $oldValue = $this->get($key);
169 10
        unset($this->elements[$hashedKey]);
170
171 10
        return $oldValue;
172
    }
173
174
    /**
175
     * Returns the number of mappings in the map
176
     *
177
     * @return int
178
     */
179 13
    public function count(): int
180
    {
181 13
        return count($this->elements);
182
    }
183
184
    /**
185
     * Returns the keys as a collection
186
     *
187
     * @param CollectionInterface $collection
188
     * @return CollectionInterface
189
     */
190 3
    public function keys(CollectionInterface $collection = null): CollectionInterface
191
    {
192 3
        if (null === $collection) {
193 2
            return new ArrayList($this->getKeys());
194
        }
195
196 1
        $collection->addAllArray($this->getKeys());
197
198 1
        return $collection;
199
    }
200
201
    /**
202
     * Returns the values as a collection
203
     *
204
     * @param CollectionInterface $collection
205
     * @return CollectionInterface
206
     */
207 42
    public function values(CollectionInterface $collection = null): CollectionInterface
208
    {
209 42
        if (null === $collection) {
210 41
            return new ArrayList($this->getValues());
211
        }
212
213 1
        $collection->addAllArray($this->getValues());
214
215 1
        return $collection;
216
    }
217
218
    /**
219
     * Filter the collection using closure
220
     *
221
     * The closure will get passed a [@see MapEntry].  Returning true from the
222
     * closure will include that entry in the new map.
223
     *
224
     * @param callable $filter
225
     * @return MapInterface
226
     */
227 1
    public function filter(callable $filter): MapInterface
228
    {
229 1
        $map = new static();
230
231
        /** @var MapEntry $mapEntry */
232 1
        foreach ($this->entrySet() as $mapEntry) {
233 1
            if (false !== $filter($mapEntry)) {
234 1
                $map->put($mapEntry->key, $mapEntry->value);
235
            }
236
        }
237
238 1
        return $map;
239
    }
240
241
    /**
242
     * Generate a hashcode for a php value
243
     *
244
     * @param mixed $value
245
     * @return string
246
     */
247 57
    protected function hashCode($value): string
248
    {
249 57
        $type = gettype($value);
250
        switch ($type) {
251 57
            case 'object':
252 12
                return spl_object_hash($value);
253 51
            case 'array':
254 1
                return md5(serialize($value));
255
            default:
256 50
                return $type . md5($value);
257
        }
258
    }
259
260
    /**
261
     * Returns an array of [@see MapEntry] keys
262
     *
263
     * @return array
264
     */
265 5
    private function getKeys()
266
    {
267
        return array_map(function (MapEntry $mapEntry) {
268 5
            return $mapEntry->key;
269 5
        }, $this->elements);
270
    }
271
272
    /**
273
     * Returns an array of [@see MapEntry] values
274
     *
275
     * @return array
276
     */
277
    private function getValues()
278
    {
279 42
        return array_map(function (MapEntry $mapEntry) {
280 40
            return $mapEntry->value;
281 42
        }, $this->elements);
282
    }
283
}
284