Map::computeIfPresent()   A
last analyzed

Complexity

Conditions 5
Paths 3

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 10
c 1
b 0
f 0
nc 3
nop 2
dl 0
loc 14
ccs 10
cts 10
cp 1
crap 5
rs 9.6111
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * @author hashashiyyin [email protected] / [email protected]
5
 * Date: 24/05/24
6
 * Time: 13:41
7
 *
8
 */
9
10
namespace Matecat\SubFiltering\Utils;
11
12
use ArrayAccess;
13
use ArrayIterator;
14
use Countable;
15
use DomainException;
16
use IteratorAggregate;
17
18
/**
19
 * A Java like Interface helper for key/value php arrays, with safe access to the elements (no warning for undefined index)
20
 *
21
 */
22
class Map implements ArrayAccess, IteratorAggregate, Countable {
23
24
    /**
25
     * @var array
26
     */
27
    private array $map;
28
29 76
    public function __construct( array $map ) {
30 76
        if ( !empty( $map ) && Utils::array_is_list( $map ) ) {
31 1
            throw new DomainException( "Invalid map provided" );
32
        }
33 75
        $this->map = $map;
34
    }
35
36
    /**
37
     * @return int
38
     */
39 10
    public function count(): int {
40 10
        return sizeof( $this->map );
41
    }
42
43
44
    /**
45
     * @return ArrayIterator
46
     */
47 2
    public function getIterator(): ArrayIterator {
48 2
        return new ArrayIterator( $this->map );
49
    }
50
51
    /**
52
     * @param $offset
53
     *
54
     * @return bool
55
     */
56 1
    public function offsetExists( $offset ): bool {
57 1
        return array_key_exists( $offset, $this->map );
58
    }
59
60
    /**
61
     * @param $offset
62
     *
63
     * @return mixed|null
64
     */
65 1
    public function offsetGet( $offset ) {
66 1
        return $this->get( $offset );
67
    }
68
69
    /**
70
     * @param $offset
71
     * @param $value
72
     *
73
     * @return void
74
     */
75 1
    public function offsetSet( $offset, $value ) {
76 1
        $this->put( $offset, $value );
77
    }
78
79
    /**
80
     * @param $offset
81
     *
82
     * @return void
83
     */
84 1
    public function offsetUnset( $offset ) {
85 1
        $this->remove( $offset );
86
    }
87
88
    /**
89
     * Static helper for constructor
90
     *
91
     * @param array $map
92
     *
93
     * @return static
94
     */
95 76
    public static function instance( array $map = [] ): Map {
96 76
        return new static( $map );
97
    }
98
99
    /**
100
     * Safe key access. Avoid warnings.
101
     *
102
     * @param       $needle
103
     *
104
     * @return mixed|null
105
     */
106 61
    public function get( $needle ) {
107 61
        return $this->getOrDefault( $needle, null );
108
    }
109
110
    /**
111
     * @param $needle
112
     * @param $default
113
     *
114
     * @return mixed
115
     */
116 61
    public function getOrDefault( $needle, $default ) {
117 61
        return array_key_exists( $needle, $this->map ) ? $this->map [ $needle ] : $default;
118
    }
119
120
    /**
121
     * Removes all the mappings from this map.
122
     * @return void
123
     */
124 1
    public function clear() {
125 1
        $this->map = [];
126
    }
127
128
    /**
129
     * Returns a shallow copy of this HashMap instance: the keys and values themselves are not cloned.
130
     * @return $this
131
     */
132 1
    public function __clone() {
133 1
        return new static( $this->map );
134
    }
135
136 1
    public function containsKey( $offset ): bool {
137 1
        return array_key_exists( $offset, $this->map );
138
    }
139
140
    /**
141
     * Returns true if this map maps one or more keys to the specified value.
142
     * If the value is found more than once, the first matching key is returned.
143
     *
144
     * @param $value
145
     *
146
     * @return boolean
147
     */
148 1
    public function containsValue( $value ): bool {
149 1
        return !empty( array_search( $value, $this->map, true ) );
150
    }
151
152
/**
153
      * Iterates over each entry in the map and applies the given callable function.
154
      * The callable function receives the key and value of each entry as arguments.
155
      *
156
      * Actions are performed in the order of the map's entry set iteration.
157
      * If the callable throws an exception, the iteration is stopped, and the exception is propagated.
158
      *
159
      * @param callable $callable A function to execute for each entry in the map.
160
      *                           The function should accept two parameters: the key and the value.
161
      *
162
      * @return void
163
      */
164 1
     public function for_each( callable $callable ) {
165 1
         foreach ( $this->map as $k => $v ) {
166 1
             $callable( $k, $v );
167
         }
168
     }
169
170
    /**
171
     * Returns true if this map contains no key-value mappings.
172
     *
173
     * @return bool
174
     */
175 57
    public function isEmpty(): bool {
176 57
        return empty( $this->map );
177
    }
178
179
    /**
180
     * @return string[]
181
     */
182 1
    public function keySet(): array {
183 1
        return array_keys( $this->map );
184
    }
185
186
    /**
187
     * Associates the specified value with the specified key in this map. If the map previously contained a mapping for the key, the old value is replaced.
188
     *
189
     * @param $offset
190
     * @param $value
191
     *
192
     * @return mixed|null the previous value associated with `key`, or null if there was no mapping for `key`.
193
     */
194 3
    public function put( $offset, $value ) {
195 3
        $previousValue        = $this->get( $offset );
196 3
        $this->map[ $offset ] = $value;
197
198 3
        return $previousValue;
199
    }
200
201
    /**
202
     * @param $map
203
     *
204
     * @return void
205
     */
206 1
    public function putAll( $map ) {
207 1
        foreach ( $map as $k => $v ) {
208 1
            $this->map[ $k ] = $v;
209
        }
210
    }
211
212
    /**
213
     * If the specified key is not already associated with a value (or is mapped to null) associates it with the given value and returns null, else returns the current value.
214
     *
215
     * @param $offset
216
     * @param $value
217
     *
218
     * @return mixed|null the previous value associated with the specified key, or null if there was no mapping for the key.
219
     *               A null return can also indicate that the map previously associated null with the `key`
220
     */
221 1
    public function putIfAbsent( $offset, $value ) {
222 1
        $previousValue = $this->get( $offset );
223 1
        if ( $previousValue === null ) {
224 1
            $this->map[ $offset ] = $value;
225
        }
226
227 1
        return $previousValue;
228
    }
229
230
    /**
231
     * Removes the mapping for the specified key from this map if present.
232
     *
233
     * @param $offset
234
     *
235
     * @return bool true if the value was removed
236
     */
237 2
    public function remove( $offset ): bool {
238 2
        $exists = array_key_exists( $offset, $this->map );
239 2
        if ( $exists ) {
240 2
            unset( $this->map[ $offset ] );
241
        }
242
243 2
        return $exists;
244
    }
245
246
    /**
247
     * Replaces the entry for the specified key only if it is currently mapped to some value.
248
     *
249
     * @return mixed|null the previous value associated with the specified key, or null if there was no mapping for the key.
250
     *                      A null return can also indicate that the map previously associated null with the key.
251
     */
252 1
    public function replace( $offset, $value ) {
253 1
        $exists        = array_key_exists( $offset, $this->map );
254 1
        $previousValue = $this->get( $offset );
255 1
        if ( $exists ) {
256 1
            $this->map[ $offset ] = $value;
257
        }
258
259 1
        return $previousValue;
260
    }
261
262
    /**
263
     * Replaces the entry for the specified key only if currently mapped to the specified value.
264
     *
265
     * @param $offset
266
     * @param $newValue
267
     * @param $oldValue
268
     *
269
     * @return boolean true if the value was replaced
270
     */
271 1
    public function replaceIfEquals( $offset, $newValue, $oldValue ): bool {
272 1
        $exists        = array_key_exists( $offset, $this->map );
273 1
        $previousValue = $this->get( $offset );
274 1
        if ( $exists && $previousValue === $oldValue ) {
275 1
            $this->map[ $offset ] = $newValue;
276
277 1
            return true;
278
        }
279
280 1
        return false;
281
    }
282
283
    /**
284
     * Replaces each entry's value with the result of invoking the given function on that entry until all entries have been processed,
285
     * or the function throws an exception.
286
     * Exceptions thrown by the function are relayed to the caller.
287
     *
288
     * @param callable $callable
289
     *
290
     * @return void
291
     */
292 1
    public function replaceAll( callable $callable ) {
293 1
        foreach ( $this->map as $offset => $value ) {
294 1
            $this->map[ $offset ] = $callable( $offset, $value );
295
        }
296
    }
297
298
    /**
299
     * @return int
300
     */
301 3
    public function size(): int {
302 3
        return sizeof( $this->map );
303
    }
304
305
    /**
306
     * @return array
307
     */
308 1
    public function values(): array {
309 1
        return array_values( $this->map );
310
    }
311
312
    /**
313
     * Attempts to compute a mapping for the specified key and its current mapped value (or null if there is no current mapping).
314
     * For example, to either create or append a String msg to a value mapping:
315
     * <code>
316
     *     $_empty = 'EMPTY';
317
     *     $map->compute( "foo", function( $k, $v ) use ( $_empty ) { ($v == null) ? $_empty : 'NO MORE ' . $_empty  } );
318
     * </code>
319
     *
320
     * If the function returns null, the mapping is removed (or remains absent if initially absent).
321
     * If the function itself throws an (unchecked) exception, the exception is rethrown, and the current mapping is left unchanged.
322
     *
323
     * @param          $offset
324
     * @param callable $callable
325
     *
326
     * @return mixed|null the new value associated with the specified key, or null if none
327
     */
328 1
    public function compute( $offset, callable $callable ) {
329 1
        $exists = array_key_exists( $offset, $this->map );
330 1
        if ( $exists ) {
331 1
            $res = $callable( $offset, $this->get( $offset ) );
332 1
            if ( $res == null ) {
333 1
                unset( $this->map[ $offset ] );
334
            } else {
335 1
                $this->map[ $offset ] = $callable( $offset, $this->get( $offset ) );
336
337 1
                return $this->map[ $offset ];
338
            }
339
        }
340
341 1
        return null;
342
    }
343
344
    /**
345
     * If the specified key is not already associated with a value (or is mapped to null),
346
     * attempts to compute its value using the given mapping function and enters it into this map unless null.
347
     *
348
     * If the function returns null, no mapping is recorded. If the function itself throws an (unchecked) exception,
349
     * the exception is rethrown, and no mapping is recorded.
350
     *
351
     * The most common usage is to construct a new object serving as an initial mapped value or memoized result, as in:
352
     *
353
     * @param          $offset
354
     * @param callable $callable
355
     *
356
     * @return mixed|null the current (existing or computed) value associated with the specified key, or null if the computed value is null
357
     */
358 1
    public function computeIfAbsent( $offset, callable $callable ) {
359 1
        $exists        = array_key_exists( $offset, $this->map );
360 1
        $previousValue = $this->get( $offset );
361 1
        $res           = null;
362 1
        if ( !$exists || $previousValue === null ) {
363 1
            $res = $callable( $offset, $this->get( $offset ) );
364 1
            if ( $res != null ) {
365 1
                $this->map[ $offset ] = $res;
366
            }
367
368
        }
369
370 1
        return $res ?: null;
371
372
    }
373
374
    /**
375
     * If the value for the specified key is present and non-null, attempts to compute a new mapping given the key and its current mapped value.
376
     *
377
     * If the function returns null, the mapping is removed.
378
     * If the function itself throws an (unchecked) exception, the exception is rethrown, and the current mapping is left unchanged.
379
     *
380
     * @param          $offset
381
     * @param callable $callable
382
     *
383
     * @return mixed|null the new value associated with the specified key, or null if none
384
     */
385 1
    public function computeIfPresent( $offset, callable $callable ) {
386 1
        $exists        = array_key_exists( $offset, $this->map );
387 1
        $previousValue = $this->get( $offset );
388 1
        $res           = null;
389 1
        if ( $exists && $previousValue !== null ) {
390 1
            $res = $callable( $offset, $this->map[ $offset ] );
391 1
            if ( $res == null ) {
392 1
                unset( $this->map[ $offset ] );
393
            } else {
394 1
                $this->map[ $offset ] = $res;
395
            }
396
        }
397
398 1
        return $res ?: null;
399
    }
400
401
}