skip to Main Content

Im stuck on this for quite some time. I have to work with identifiers that are always 4 characters long and consist of 0-9a-z. An example would be iulz. So the range would be something like:

0000 0001 0002 ... 0009 000a 000b ... 000z 0010 ... zzzz

So in the case of my example, iulz I know that the ID before that would be iuly and after that ium0. But i cannot seem to work out how to calculate this. The functions hexdec and dechex would help me if they would include the whole alphabet.

My first try was to make a logical list with some for loops but this takes an impossible amount of memory. I am looking for a way to make the following two functions:

function getNext(string $id) : string { ... }
function getPrev(string $id) : string { ... }
$this->getNext('iulz') // returns 'ium0'
$this->getPrev('iulz') // returns 'iuly'

Can anyone get me on the right track here? Thnxz!

2

Answers


  1. Your encoding scheme contains 36 symbols (digits from 0 to 9 and letters from a to z), so a possible approach is to implement a math with Base36. PHP supports base_convert() function to convert a number between arbitrary bases.

    <?php
    
    function getNext($id) {
        $n = base_convert($id, 36, 10);
        $n = $n + 1;
        $n = base_convert($n, 10, 36);
        $n = str_pad($n, 4, '0', STR_PAD_LEFT);
        return $n;
    }
    
    function getPrev($id) {
        $n = base_convert($id, 36, 10);
        $n = $n - 1;
        $n = base_convert($n, 10, 36);
        $n = str_pad($n, 4, '0', STR_PAD_LEFT);
        return $n;
    }
    
    $id = 'iulz';
    echo "ID: " . $id . ", next ID: " . getNext($id) . ", previous ID: " . getPrev($id) . ".";
    
    ?>
    

    Result:

    ID: iulz, next ID: ium0, previous ID: iuly.
    

    As an additional note, you need to define the expected behaviour of the functions, if the $id is outside the [0000 .. zzzz] range.

    Login or Signup to reply.
  2. function previousId(string $alphabet, string $id)
    {
      $length = strlen($id);
      $first = $alphabet[0];
      $last = $alphabet[strlen($alphabet) - 1];
      if ($id === str_repeat($first, $length)) {
        return '––––';
      }
      for ($len = $length - 1; $len >= 0; $len--) {
        if (str_ends_with($id, str_repeat($first, $len))) {
          $pos = $length - $len - 1;
          $char = $alphabet[strpos($alphabet, $id[$pos]) - 1];
          return substr($id, 0, $pos) . $char . str_repeat($last, $len);
        }
      }
    }
    
    function nextId(string $alphabet, string $id)
    {
      $length = strlen($id);
      $first = $alphabet[0];
      $last = $alphabet[strlen($alphabet) - 1];
      if ($id === str_repeat($last, $length)) {
        return '––––';
      }
      for ($len = $length - 1; $len >= 0; $len--) {
        if (str_ends_with($id, str_repeat($last, $len))) {
          $pos = $length - $len - 1;
          $char = $alphabet[strpos($alphabet, $id[$pos]) + 1];
          return substr($id, 0, $pos) . $char . str_repeat($first, $len);
        }
      }
    }
    

    These two functions take an arbitrary alphabet (of unique characters). The id can be of any length smaller than the length of the alphabet string. Both error condition could and should be added to these function.

    If there is no preceding value for previousId the function currently returns a message for illustration purposes. Possible real values: false, null, wrap around to the last value (‘zzzz’), or maybe throw a RuntimeException or test the input value before calling previousId. Same applies for a call of nextId with an id of ‘zzzz’, which could return ‘0000’, or false, or null, etc.

    $alphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
    $id = 'iulz';
    
    echo previousId($alphabet, $id) . " < $id > " . nextId($alphabet, $id) . "n";
    
    // Outputs: iuly < iulz > ium0
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search