Completed
Push — master ( 73c299...7c5d05 )
by Nate
02:10
created

HashMap::doContainsKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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