Search code examples
phparraysfunctioncachingcollections

Collection Framework - need to cache result from the PHP method


Need help/clarification with caching results from the PHP method that takes a string and returns the number of unique characters in the string (method below ⬇️ done and works)

function uniqueCharacters($str): int
    {
        $counter = [];
        $splitted = str_split($str);
        foreach($splitted as $s) {
            if(!isset($counter[$s]))
                $counter[$s] = 0;
                $counter[$s]++;
        }
        $unique = array_keys(array_filter($counter, function($c) {
            return $c == 1;
        }));

        return count($unique);
    }

It is expected that a string with the same character sequence may be passed several times to the method. Since the counting operation can be time-consuming, the method should cache the results, so that when the method is given a string previously encountered, it will simply retrieve the stored result. Use collections and maps where appropriate.

Tried a solution from https://stackoverflow.com/questions/3540403/caching-function-results-in-php but that one is not what was asked from the task...


Solution

  • Just keep a static cache in your function and only do the work if encountering the string for the first time. The static keyword will ensure that the variable's contents are not lost between subsequent function calls.

    Because you only have a single string as an incoming parameter, you don't need to serialize the data to form a string. When declaring string keys in an array, those values will not be corrupted. However if your values -- to become keys -- are floats, booleans, null, arrays, objects, resources, (and probably a few other types that I'm not thinking of) then you'd better convert it to some form of string to be safe. In other words, you can safely use my snippet if your incoming parameter is string or int typed (not a mix of int and strings).

    Code: (Demo)

    function countUniqueCharacters(string $str): int
    {
        static $cache = [];
        if (!key_exists($str, $cache)) {
            echo PHP_EOL . "*** doing the work on $str ***";
            $cache[$str] = count(
                array_filter(
                    count_chars($str, 1),
                    fn($count) => $count === 1
                )
            );
        }
        return $cache[$str];
    }
    
    $tests = ['seven', 'eight', 'nine', 'seven', 'eight'];
    
    foreach ($tests as $test) {
        echo PHP_EOL . "$test : " . countUniqueCharacters($test);
    }
    

    Output:

    *** doing the work on seven ***
    seven : 3
    *** doing the work on eight ***
    eight : 5
    *** doing the work on nine ***
    nine : 2
    seven : 3
    eight : 5
    

    Because your return value is an int (cannot be null), you can simplify the above using the null coalescing operator like this: (Demo)

    function countUniqueCharacters(string $str): int
    {
        static $cache = [];
        return $cache[$str] ??= count(
                   array_filter(
                       count_chars($str, 1),
                       fn($count) => $count === 1
                   )
               );
    }
    

    Relevant links: