Completed
Push — master ( 3f6140...f73305 )
by Nate
02:46
created

HashMap::hashCode()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 7
cts 7
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 9
nc 3
nop 1
crap 3
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
     * The mapping keys
22
     *
23
     * @var array
24
     */
25
    protected $keys = [];
26
27
    /**
28
     * The mapping values
29
     *
30
     * @var array
31
     */
32
    protected $values = [];
33
34
    /**
35
     * Removes all mappings from map
36
     *
37
     * @return void
38
     */
39 1
    public function clear()
40
    {
41 1
        $this->keys = [];
42 1
        $this->values = [];
43 1
    }
44
45
    /**
46
     * Returns true if the key exists in the lookup table
47
     *
48
     * @param mixed $key
49
     * @return bool
50
     */
51 23
    public function containsKey($key): bool
52
    {
53 23
        return array_key_exists($this->hashCode($key), $this->keys);
54
    }
55
56
    /**
57
     * Returns true if the value exists in the map
58
     *
59
     * By default this method will use strict comparison checking, passing false
60
     * in will use a double equals (==) instead.
61
     *
62
     * @param mixed $value
63
     * @param bool $strict
64
     * @return bool
65
     */
66 3
    public function containsValue($value, bool $strict = true): bool
67
    {
68 3
        return in_array($value, $this->values, $strict);
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 5
    public function entrySet(SetInterface $set = null): SetInterface
81
    {
82 5
        $set = $set ?? new HashSet();
83 5
        foreach ($this->keys as $hashCode => $key) {
84 5
            $set->add(new MapEntry($key, $this->values[$hashCode]));
85
        }
86
87 5
        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 4
    public function get($key)
98
    {
99 4
        $hashedKey = $this->hashCode($key);
100 4
        if (!$this->containsKey($key)) {
101 1
            throw new OutOfRangeException(sprintf('Tried to access array at key "%s"', $hashedKey));
102
        }
103
104 3
        return $this->values[$hashedKey];
105
    }
106
107
    /**
108
     * Returns true if the map is empty
109
     *
110
     * @return bool
111
     */
112 2
    public function isEmpty(): bool
113
    {
114 2
        return 0 === $this->count();
115
    }
116
117
    /**
118
     * Returns a set of they keys in the map
119
     *
120
     * If a set is passed in, that set will be populated, otherwise
121
     * a default set will be used.
122
     *
123
     * @param SetInterface $set
124
     * @return SetInterface
125
     */
126 2
    public function keySet(SetInterface $set = null): SetInterface
127
    {
128 2
        if (null === $set) {
129 1
            return new HashSet(new ArrayList($this->keys));
130
        }
131
132 1
        $set->addAll(new ArrayList($this->keys));
133
134 1
        return $set;
135
    }
136
137
    /**
138
     * Returns the previous value or null if there was no value
139
     *
140
     * @param mixed $key
141
     * @param mixed $value
142
     * @return mixed
143
     */
144 23
    public function put($key, $value)
145
    {
146 23
        $oldValue = null;
147 23
        $hashedKey = $this->hashCode($key);
148 23
        if ($this->containsKey($key)) {
149 1
            $oldValue = $this->get($key);
150 1
            $this->remove($hashedKey);
151
        }
152
153 23
        $this->keys[$hashedKey] = $key;
154 23
        $this->values[$hashedKey] = $value;
155
156 23
        return $oldValue;
157
    }
158
159
    /**
160
     * Remove the mapping for the key and returns the previous value
161
     * or null
162
     *
163
     * @param mixed $key
164
     * @return mixed
165
     */
166 3
    public function remove($key)
167
    {
168 3
        $hashedKey = $this->hashCode($key);
169 3
        if (!$this->containsKey($key)) {
170 2
            return false;
171
        }
172
173 1
        $oldValue = $this->get($key);
174 1
        unset($this->keys[$hashedKey], $this->values[$hashedKey]);
175
176 1
        return $oldValue;
177
    }
178
179
    /**
180
     * Returns the number of mappings in the map
181
     *
182
     * @return int
183
     */
184 6
    public function count(): int
185
    {
186 6
        return count($this->keys);
187
    }
188
189
    /**
190
     * Returns the keys as a collection
191
     *
192
     * @param CollectionInterface $collection
193
     * @return CollectionInterface
194
     */
195 9 View Code Duplication
    public function keys(CollectionInterface $collection = null): CollectionInterface
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
196
    {
197 9
        if (null === $collection) {
198 8
            return new ArrayList($this->keys);
199
        }
200
201 1
        $collection->addAll(new ArrayList($this->keys));
202
203 1
        return $collection;
204
    }
205
206
    /**
207
     * Returns the values as a collection
208
     *
209
     * @param CollectionInterface $collection
210
     * @return CollectionInterface
211
     */
212 2 View Code Duplication
    public function values(CollectionInterface $collection = null): CollectionInterface
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
213
    {
214 2
        if (null === $collection) {
215 1
            return new ArrayList($this->values);
216
        }
217
218 1
        $collection->addAll(new ArrayList($this->values));
219
220 1
        return $collection;
221
    }
222
223
    /**
224
     * Filter the collection using closure
225
     *
226
     * The closure will get passed a [@see MapEntry].  Returning true from the
227
     * closure will include that entry in the new map.
228
     *
229
     * @param callable $filter
230
     * @return MapInterface
231
     */
232 1
    public function filter(callable $filter): MapInterface
233
    {
234 1
        $map = new HashMap();
235
236
        /** @var MapEntry $mapEntry */
237 1
        foreach ($this->entrySet() as $mapEntry) {
238 1
            if (false !== $filter($mapEntry)) {
239 1
                $map->put($mapEntry->key, $mapEntry->value);
240
            }
241
        }
242
243 1
        return $map;
244
    }
245
246
    /**
247
     * Generate a hashcode for a php value
248
     *
249
     * @param mixed $value
250
     * @return string
251
     */
252 23
    protected function hashCode($value): string
253
    {
254 23
        $type = gettype($value);
255
        switch ($type) {
256 23
            case 'object':
257 5
                return spl_object_hash($value);
258 21
            case 'array';
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
259 1
                return md5(serialize($value));
260
            default:
261 20
                return $type . md5($value);
262
        }
263
    }
264
}
265