41

So I have this function:

function toAlpha($data){
    $alphabet =   array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z');
    $alpha_flip = array_flip($alphabet);
    if($data <= 25){
      return $alphabet[$data];
    }
    elseif($data > 25){
      $dividend = ($data + 1);
      $alpha = '';
      $modulo;
      while ($dividend > 0){
        $modulo = ($dividend - 1) % 26;
        $alpha = $alphabet[$modulo] . $alpha;
        $dividend = floor((($dividend - $modulo) / 26));
      } 
      return $alpha;
    }
}

which given a number converts it into character and it works fine

but then I also want a reverse function of this that given any output of this function, return the exact input that was put in to produce that output and I tried this:

function toNum($data){
$alphabet =   array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z');
    $alpha_flip = array_flip($alphabet);
  if(strlen($data) == 1){
          return (isset($alpha_flip[$data]) ? $alpha_flip[$data] : FALSE);
        }
        else if(strlen($data) > 1){
          $num = 1;
          for($i = 0; $i < strlen($data); $i++){
            if(($i + 1) < strlen($data)){
              $num *= (26 * ($alpha_flip[$data[$i]] + 1));
            }
            else{
              $num += ($alpha_flip[$data[$i]] + 1);
            }
          }
          return ($num + 25);
        }
}

but it's not working properly...toAlpha(728) is producing 'aba' but toNum('aba') is producing 1378 rather than 728...

What did I do wrong? How can I fix the reverse function so that it works properly?

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
pillarOfLight
  • 8,592
  • 15
  • 60
  • 90
  • So you're trying to convert a number base 10 to base 26, is that it? – Mathieu Dumoulin Oct 05 '11 at 15:57
  • 2
    What is the original toAlpha function supposed to do? What is the desired behavior if the number is over 25? – Alex Turpin Oct 05 '11 at 15:57
  • it becomes double letters, triple letters, etc eg: 26 = aa, 27 = ab, etc – pillarOfLight Oct 05 '11 at 15:59
  • If 'a' is your 0 then shouldn't aa also be 0? At least, if you're just trying to implement straight-up base-26 using letters rather than numbers. – Hammerite Oct 05 '11 at 16:12
  • The desired result from both functions is really unclear. Can you explain what the functions should output exactly and by what rules? – Decent Dabbler Oct 05 '11 at 16:14
  • IMO, the best solution in the general case is to use [`chr()`](http://php.net/manual/en/function.chr.php) & `ord()`, e.g. `$c = chr(ord('a') - 1 + $n);` - you can even factor out `ord('a') - 1` (or `ord('A')` etc.) as a constant here. –  Nov 26 '17 at 16:10
  • Brilliant just what I was looking for (with the answer below). This was doing my head in but I now see why. This is NOT as simple `base 26` question. `z` is 25 = highest number in `base 26` but `zz` is NOT 675 but 701. This is because `a` does not operate as just a zero but it operates as a `1` after `z` or 25. Unlike 0 `a` has a *positional* value as a leading "zero" and effectively turns into `base 27`. So while 01 means 1 the equivalent `aa` does not represent 00 but `26 0`. So the value formula is 26*27*27... if you see what I mean. Very odd BUT it all works fine. Thanks – BeNice Nov 06 '18 at 12:02

12 Answers12

155

Shortest way, in PHP >= 4.1.0

$alphabet = range('A', 'Z');

echo $alphabet[3]; // returns D

echo array_search('D', $alphabet); // returns 3
Pupil
  • 23,834
  • 6
  • 44
  • 66
Cyril
  • 3,048
  • 4
  • 26
  • 29
  • 1
    This will not work with any integer exceeding the length of the alphabet array; e.g. "42". – kirgy May 26 '16 at 10:37
  • @kirgy I posted an answer based on this case that works with integers exceeding the length of the alphabet array. – rvbarreto Mar 18 '19 at 17:10
  • 1
    For A letter it will print "0" number, it is wrong, so the new code should be : echo array_search($letter, $alphabet)+1; – Bbbb Dec 25 '22 at 19:50
  • 1
    This has been the correct answer to the wrong question since the question was posted. The answer scores on this page are conveying an inappropriate message to researchers. This answer should not have a positive score because it does not satisfy the brief. – mickmackusa Jan 26 '23 at 20:00
17

I don't understand at all the logic you're trying to use in that function. What you're trying to do seems very strange (why does 'a' map to zero and yet 'aa' maps to 26?), but this appears to work. (You will want to use some more test cases, I only checked that it gives the correct output for the case 'aba'.)

function toNum($data) {
    $alphabet = array( 'a', 'b', 'c', 'd', 'e',
                       'f', 'g', 'h', 'i', 'j',
                       'k', 'l', 'm', 'n', 'o',
                       'p', 'q', 'r', 's', 't',
                       'u', 'v', 'w', 'x', 'y',
                       'z'
                       );
    $alpha_flip = array_flip($alphabet);
    $return_value = -1;
    $length = strlen($data);
    for ($i = 0; $i < $length; $i++) {
        $return_value +=
            ($alpha_flip[$data[$i]] + 1) * pow(26, ($length - $i - 1));
    }
    return $return_value;
}
Zathrus Writer
  • 4,311
  • 5
  • 27
  • 50
Hammerite
  • 21,755
  • 6
  • 70
  • 91
  • 2
    In base 10, the successor of 9 is not 00. So why is aa the successor of z? – Hammerite Oct 06 '11 at 00:39
  • 7
    Excel does this. Maybe @pillarOfLight was trying to figure out something with spreadsheets. – Stephane Sep 13 '12 at 03:03
  • 33
    $alphabet = range('a', 'z'); – wesside Oct 09 '12 at 20:51
  • @Hammerite had me confused but after the single character the second `a` in `aa` moves this from base 26 to `base 27` as it has a positional value (so a-z PLUS the absence of an a = 27 characters). It is as if `001` 101! – BeNice Nov 06 '18 at 12:11
  • Dumb question time. 1) Why do you need ` $alpha_flip = array_flip($alphabet);` and in the original there is a line `$modulo;`. Both seem redundant. 2) I asked above but will repeat here: What does the % do? (I have tried Goog, PHP.net, SO, etc but searching with a `%` is rather entertaining.) – BeNice Nov 06 '18 at 15:06
16

There is a very clever solution by Theriault in the comments of PHPs base_convert function

/**
* Converts an integer into the alphabet base (A-Z).
*
* @param int $n This is the number to convert.
* @return string The converted number.
* @author Theriault
* 
*/
function num2alpha($n) {
    $r = '';
    for ($i = 1; $n >= 0 && $i < 10; $i++) {
    $r = chr(0x41 + ($n % pow(26, $i) / pow(26, $i - 1))) . $r;
    $n -= pow(26, $i);
    }
    return $r;
}
/**
* Converts an alphabetic string into an integer.
*
* @param int $n This is the number to convert.
* @return string The converted number.
* @author Theriault
* 
*/
function alpha2num($a) {
    $r = 0;
    $l = strlen($a);
    for ($i = 0; $i < $l; $i++) {
    $r += pow(26, $i) * (ord($a[$l - $i - 1]) - 0x40);
    }
    return $r - 1;
}
user414873
  • 741
  • 8
  • 20
  • Works great with any number: `num2alpha(25)` → "Z", `num2alpha(26)` → "AA", ... – Philipp Jul 31 '20 at 14:42
  • 1
    Tip: Use `97` instead of `0x41` and `96` instead of `0x40` to get lowercase letters. Or use `strtolower()` to convert the result to lowercase – Philipp Jul 31 '20 at 14:44
8

From number to alphabet (with A=0, B=1, etc...):

function toAlpha($num){
    return chr(substr("000".($num+65),-3));
}

You can do the same from alphabet to number with the function ord().

Changing 65 with 97, you can obtain the lowercase values.

narcolepticvolcano
  • 1,037
  • 1
  • 10
  • 25
  • Could use `chr(str_pad((65 + $num), 3, '0', STR_PAD_LEFT))` instead of the `substr` call – Othyn Jan 07 '20 at 12:14
  • One note about this: It will only work with numbers 0 - 25 (a-z). `toAlpha(26)` returns `{` (when using base 97 for lowercase) - use this if you always expect a 1-character letter – Philipp Jul 31 '20 at 14:39
5

Your problems comes from your map. Look at this:

$alpha[0] = 'Alphabet';
for ($i = 'a'; $i<'z'; $i++) {
    $alpha[] = $i;
}
$alpha[26] = 'z';

You can run this as high as you want and your server memory will allow. The PHP is buggy, and (at least on my server) if you use the <= operator:

$alpha[0] = 'Alphabet';
for ($i = 'a'; $i<='z'; $i++) {
    $alpha[] = $i;
}

then it will map all the way to [676]=> string(2) "yz"! You just have to play with it.

I didn't want to map a letter to [0] so I just put a title in there. Obviously you can leave it out if you want 0=>a, 1=>b, etc.

Once the array is correct, the function is trivial.

Mason Barge
  • 409
  • 4
  • 4
5

Using Cyril's answer, I elaborated a bit for a case with more than one letter.

function lettersToNumber($letters){
    $alphabet = range('A', 'Z');
    $number = 0;

    foreach(str_split(strrev($letters)) as $key=>$char){
        $number = $number + (array_search($char,$alphabet)+1)*pow(count($alphabet),$key);
    }
    return $number;
}

A few results for the function are displayed bellow:

lettersToNumber("A"); //returns 1
lettersToNumber("E"); //returns 5
lettersToNumber("Z"); //returns 26
lettersToNumber("AB"); //returns 28
lettersToNumber("AP"); //returns 42
lettersToNumber("CE"); //returns 83
rvbarreto
  • 683
  • 9
  • 24
4

to convert number to alphacode

for example: 1402 to bax

function number_to_alpha($num, $code)
{   
    $alphabets = array('', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');

    $division = floor($num / 26);
    $remainder = $num % 26; 

    if($remainder == 0)
    {
        $division = $division - 1;
        $code .= 'z';
    }
    else
        $code .= $alphabets[$remainder];

    if($division > 26)
        return number_to_alpha($division, $code);   
    else
        $code .= $alphabets[$division];     

    return strrev($code);
}

to convert alphacode to number

for example: bax to 1402

function alpha_to_number($code)
{
    $alphabets = array('', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');

    $sumval = 0;

    $code = strtolower(trim($code));

    $arr = str_split($code);
    $arr_length = count($arr);

    for($i = 0, $j = $arr_length-1; $i < $arr_length; $i++, $j--)
    {
        $arr_value = array_search($arr[$i], $alphabets);
        $sumval = $sumval + ($arr_value * pow(26, $j));
    }

    return $sumval;
}
Jignesh Joisar
  • 13,720
  • 5
  • 57
  • 57
Praveen
  • 95
  • 8
2

I took the 'corrected' original, removed the debug code, and other unnecessary code, modified it so it will work with any number of characters. For example, Greek only has 24 characters.

function toAlpha($number, $alphabet)
    {

        $count = count($alphabet);
        if ($number <= $count) {
            return $alphabet[$number - 1];
        }
        $alpha = '';
        while ($number > 0) {
            $modulo = ($number - 1) % $count;
            $alpha  = $alphabet[$modulo] . $alpha;
            $number = floor((($number - $modulo) / $count));
        }
        return $alpha;
    }

    toAlpha(45,range('a','z'));

And here are some examples of ranges:

// lower greek
$range = ['α', 'β', 'γ', 'δ', 'ε', 'ζ', 'η', 'θ', 'ι', 'κ', 'λ', 'μ', 'ν', 'ξ', 'ο', 'π', 'ρ', 'σ', 'τ', 'υ', 'φ', 'χ', 'ψ', 'ω'];
// upper greek 
$range = ['Α', 'Β', 'Γ', 'Δ', 'Ε', 'Ζ', 'Η', 'Θ', 'Ι', 'Κ', 'Λ', 'Μ', 'Ν', 'Ξ', 'Ο', 'Π', 'Ρ', 'Σ', 'Τ', 'Υ', 'Φ', 'Χ', 'Ψ', 'Ω'];
// georgian 
$range = ['ჵ' => 10000, 'ჰ' => 9000, 'ჯ' => 8000, 'ჴ' => 7000, 'ხ' => 6000, 'ჭ' => 5000, 'წ' => 4000, 'ძ' => 3000, 'ც' => 2000, 'ჩ' => 1000, 'შ' => 900, 'ყ' => 800, 'ღ' => 700, 'ქ' => 600, 'ფ' => 500, 'ჳ' => 400, 'ტ' => 300, 'ს' => 200, 'რ' => 100, 'ჟ' => 90, 'პ' => 80, 'ო' => 70, 'ჲ' => 60, 'ნ' => 50, 'მ' => 40, 'ლ' => 30, 'კ' => 20, 'ი' => 10, 'თ' => 9, 'ჱ' => 8, 'ზ' => 7, 'ვ' => 6, 'ე' => 5, 'დ' => 4, 'გ' => 3, 'ბ' => 2, 'ა' => 1];
  • Stupid question why `$number--;` not `$number;`. If this works it looks to be just what I needed. Also what is % **LATER** sadly this does not run. ` $alpha = $alphabet[$modulo].$alpha;` has an undefined `$alpha`. The original had `$alpha = " "` but even with this inserted it still does not work. I did not realise the OP had provided a working script for this. I wasted 20 mins trying to get this working so, sadly, you are getting my first down vote. If you fix it I will remove the down vote. – BeNice Nov 06 '18 at 11:38
  • @BeNice I just ran the code. The example works fine, but your right in that there is issues with the starting point of the number. There is also a PHP notice that is thrown, but this does not affect the running of it. – Dr. Sassafras Nov 07 '18 at 14:51
  • @BeNice I have updated the example to what I ultimately ended up using. – Dr. Sassafras Nov 07 '18 at 15:00
  • Thanks I tweaked another version and have, as per usual, hacked a solution together (it works I know not why, but it works!) Could u possibly explain the `--` and the `%` pls. – BeNice Nov 07 '18 at 21:48
  • 1
    @BeNice http://php.net/manual/en/language.operators.increment.php http://php.net/manual/en/language.operators.arithmetic.php – Dr. Sassafras Nov 08 '18 at 22:04
  • thanks for that. It wd be nice if PHP.net could intercept a query like "What is a %". Not rocket science but without knowing how to look ... I could not find. Much appreciated. Oh blimey `--` - I have used `++` a hundred times. Age is slowly turning my brain to mush. As to `%` being modulo well for us part timers I like clarity over br3evity but, as I said, I am a part timer. – BeNice Nov 08 '18 at 22:23
0

Here is a fix to original function toAlpha. It is not working for toAlpha(27)

function toAlpha($n,$case = 'upper'){
    $alphabet   = array('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
    $n = $n-1;
    Util::error_log('N'.$n);
    if($n <= 26){
        $alpha =  $alphabet[$n-1];
    } elseif($n > 26) {
        $dividend   = ($n);
        $alpha      = '';
        $modulo;
        while($dividend > 0){
            $modulo     = ($dividend - 1) % 26;
            $alpha      = $alphabet[$modulo].$alpha;
            $dividend   = floor((($dividend - $modulo) / 26));
        }
    }

    if($case=='lower'){
        $alpha = strtolower($alpha);
    }
    Util::error_log("**************".$alpha);
    return $alpha;

}
Santosh Pradhan
  • 149
  • 1
  • 11
0

We can convert our number from decimal (base 10) to alphanumeric (base 36) and shift the position from the numeric block to alphabetic block using base_convert():

function toLetter(int $num) {
    return strtoupper(base_convert($num + 9, 10, 36));
}
shaedrich
  • 5,457
  • 3
  • 26
  • 42
0

i do this stuff to use fix char lenght before int parameter A = 0 ,Z = 25

function returnUIDfixChar(int $inc,int $charlength=3,int $min_input_length=3,string $prefix=''):string {
$alpha = range('A','Z');
$max_alpa_int = count($alpha)-1;
$over = 0;
$output_prefix = '';
$first_split = 0;
if(strlen($inc)>$min_input_length)
{
    $first_split = substr((string) $inc,0,strlen($inc)-$min_input_length);
}
$second_split = substr((string) $inc,0,$min_input_length);
for($i=0;$i<$charlength;$i++)
{
    $temp_over = (float) $first_split - $max_alpa_int;
    if($temp_over>0)
    {
        $output_prefix = $alpha[$max_alpa_int].$output_prefix;
        $first_split -= $max_alpa_int;
    }
    elseif($first_split<$max_alpa_int && $first_split>0)
    {
        $output_prefix = $alpha[$first_split].$output_prefix;
        $first_split -= $first_split;
    }
    else
    {
        $output_prefix = $alpha[0].$output_prefix;
    }
    $over = $first_split;
}
if(strlen($second_split)<$min_input_length)
{
    for($i=0;$i<$min_input_length-strlen($second_split);$i++)
    {
        $second_split = '0'.$second_split;
    }
}
return $output_prefix.($first_split>0?$first_split:'').$second_split;

}

-2
function toNum($str) {
  $num = 0;
  for ($i = 0; $i < strlen($str); $i++) {
    $num += ord($str[$i]);
    $num *= 26;
  }
  return $num;
}

function toStr($num) {
  $str = '';
  while ($num > 0) {
    $str = chr($num % 26) . $str;
    $num = (int) ($num / 26);
  }
  return $str;
}