HashMap::filter()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 0
cts 10
cp 0
rs 9.8333
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 12
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
    public function __construct(array $map = [])
31
    {
32
        foreach ($map as $key => $value) {
33
            $this->put($key, $value);
34
        }
35
    }
36
37
    /**
38
     * Removes all mappings from map
39
     *
40
     * @return void
41
     */
42
    public function clear(): void
43
    {
44
        $this->elements = [];
45
    }
46
47
    /**
48
     * Returns true if the key exists in the lookup table
49
     *
50
     * @param mixed $key
51
     * @return bool
52
     */
53
    public function containsKey($key): bool
54
    {
55
        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
    public function containsValue($value): bool
65
    {
66
        return $this->exists(function (MapEntry $mapEntry) use ($value) {
67
            return $mapEntry->value === $value;
68
        });
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
    public function entrySet(SetInterface $set = null): SetInterface
81
    {
82
        $set = $set ?? new HashSet();
83
        $set->addAllArray($this->elements);
84
85
        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
    public function get($key)
96
    {
97
        return $this->doGet($this->hashCode($key));
98
    }
99
100
    /**
101
     * Returns true if the map is empty
102
     *
103
     * @return bool
104
     */
105
    public function isEmpty(): bool
106
    {
107
        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
    public function keySet(SetInterface $set = null): SetInterface
120
    {
121
        if (null === $set) {
122
            return new HashSet($this->getKeys());
123
        }
124
125
        $set->addAllArray($this->getKeys());
126
127
        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
    public function put($key, $value)
138
    {
139
        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
    public function remove($key)
150
    {
151
        return $this->doRemove($this->hashCode($key));
152
    }
153
154
    /**
155
     * Returns the number of mappings in the map
156
     *
157
     * @return int
158
     */
159
    public function count(): int
160
    {
161
        return count($this->elements);
162
    }
163
164
    /**
165
     * Returns the keys as a collection
166
     *
167
     * @param CollectionInterface $collection
168
     * @return CollectionInterface
169
     */
170
    public function keys(CollectionInterface $collection = null): CollectionInterface
171
    {
172
        if (null === $collection) {
173
            return new ArrayList($this->getKeys());
174
        }
175
176
        $collection->addAllArray($this->getKeys());
177
178
        return $collection;
179
    }
180
181
    /**
182
     * Returns the values as a collection
183
     *
184
     * @param CollectionInterface $collection
185
     * @return CollectionInterface
186
     */
187
    public function values(CollectionInterface $collection = null): CollectionInterface
188
    {
189
        if (null === $collection) {
190
            return new ArrayList($this->getValues());
191
        }
192
193
        $collection->addAllArray($this->getValues());
194
195
        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
    public function filter(callable $filter): MapInterface
208
    {
209
        $map = new static();
210
211
        /** @var MapEntry $mapEntry */
212
        foreach ($this->entrySet() as $mapEntry) {
213
            if (false !== $filter($mapEntry)) {
214
                $map->put($mapEntry->key, $mapEntry->value);
215
            }
216
        }
217
218
        return $map;
219
    }
220
221
    /**
222
     * Generate a hashcode for a php value
223
     *
224
     * @param mixed $value
225
     * @return string
226
     */
227
    protected function hashCode($value): string
228
    {
229
        $type = gettype($value);
230
        switch ($type) {
231
            case 'object':
232
                return spl_object_hash($value);
233
            case 'array':
234
                return md5(serialize($value));
235
            default:
236
                return $type . md5($value);
237
        }
238
    }
239
240
    /**
241
     * Returns an array of [@see MapEntry] keys
242
     *
243
     * @return array
244
     */
245
    private function getKeys(): array
246
    {
247
        return array_map(function (MapEntry $mapEntry) {
248
            return $mapEntry->key;
249
        }, $this->elements);
250
    }
251
252
    /**
253
     * Returns an array of [@see MapEntry] values
254
     *
255
     * @return array
256
     */
257
    private function getValues(): array
258
    {
259
        return array_map(function (MapEntry $mapEntry) {
260
            return $mapEntry->value;
261
        }, $this->elements);
262
    }
263
264
    /**
265
     * Internal implementation for containsKey()
266
     *
267
     * @param string $hashCode
268
     * @return bool
269
     */
270
    private function doContainsKey(string $hashCode): bool
271
    {
272
        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
    private function doGet(string $hashCode)
282
    {
283
        if (!$this->doContainsKey($hashCode)) {
284
            return null;
285
        }
286
287
        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
    private function doPut(string $hashCode, $key, $value)
299
    {
300
        $oldValue = $this->doRemove($hashCode);
301
        $this->elements[$hashCode] = new MapEntry($key, $value);
302
303
        return $oldValue;
304
    }
305
306
    /**
307
     * Internal implementation for remove()
308
     *
309
     * @param string $hashCode
310
     * @return mixed|null
311
     */
312
    private function doRemove(string $hashCode)
313
    {
314
        if (!$this->doContainsKey($hashCode)) {
315
            return null;
316
        }
317
318
        $oldValue = $this->doGet($hashCode);
319
        unset($this->elements[$hashCode]);
320
321
        return $oldValue;
322
    }
323
}
324