11

Its working nicely:

$str = 'a';
echo ++$str; // prints 'b'

$str = 'z';
echo ++$str; // prints 'aa' 

Its very useful to get next column name in an excel file.

But if I use similar code using -- operator to get the previous letter then its not working:

$str = 'b';
echo --$str; // prints 'b' but I need 'a'

$str = 'aa';
echo --$str; // prints 'aa' but I need 'z'

What can be the solution to get the previous letter similarly? And what can be the reason as its not working?

Community
  • 1
  • 1
itsazzad
  • 6,868
  • 7
  • 69
  • 89

4 Answers4

9
$str='z';
echo chr(ord($str)-1);   //y

Note: This isn't circular for a-z. Need to add rules for that

Fiddle

Edit This edit covers for your special requirement from excel example. Although its a little longer piece of code.

//Step 1: Build your range; We cant just go about every character in every language.

$x='a';
while($x!='zz')         // of course you can take that to zzz or beyond etc
{
  $values[]=$x++;       // A simple range() call will not work for multiple characters
}
$values[]=$x;           // Now this array contains range `a - zz`

//Step 2:  Provide reference
$str='ab';

//Step 3: Move next or back
echo $values[array_search(strtolower($str),$values)-1];   // Previous = aa
echo $values[array_search(strtolower($str),$values)+1];   // Next     = ac

Fiddle

Hanky Panky
  • 46,730
  • 8
  • 72
  • 95
0

Using array:

$cla=array('A', 'B', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 
'P', 'R', 'S', 'Š', 'Z', 'Ž', 'T', 'U', 'V', 'Õ', 'Ä', 'Ö', 'Ü'); 
$direction = -1;
$element = 'Ü';
$element2 = $cla[((array_search($element,$cla)+count($cla)+$direction)%count($cla))]; 
Margus
  • 19,694
  • 14
  • 55
  • 103
  • Note this is 1 way circular linked list, buy you might want 2 way circular linked list. Construction of that is quite similar or as charset does not change you could just use 2 circular lists with different ordering. – Margus Oct 14 '14 at 08:07
  • For less experienced programmer it might be simpler to use array and change the pointer as it hits the bounds. – Margus Oct 14 '14 at 08:08
  • Note this is not case sensitive search! Converting them to lower case would to create 'case sensitive search' would not work for umlaut letters. – Margus Oct 14 '14 at 08:31
0

str_prev.php:

<?php

function str_prev($str) {
  $chars = str_split($str);
  $nb_chars = strlen($str);

  $ret = true;
  for ($i = $nb_chars-1; $i >= 0; --$i) {
    if ($chars[$i] === 'a') {
      $chars[$i] = 'z';
    } else {
      $chars[$i] = chr(ord($chars[$i]) - 1);
      $ret = false;
      break;
    }
  }

  if ($ret)
    $chars[$nb_chars-1] = '';

  return implode('', $chars);
}

$tests = array(
  [ 'a',        ''    ],
  [ 'b',        'a'   ],
  [ 'z',        'y'   ],
  [ 'aa',       'z'   ],
  [ 'ab',       'aa'  ],
  [ 'az',       'ay'  ],
  [ 'ba',       'az'  ],
  [ 'ca',       'bz'  ],
  [ 'aaa',      'zz'  ],
  [ 'aba',      'aaz'  ],
  [ 'abb',      'aba'  ],
  [ 'cha',      'cgz'  ],
  [ 'zhuwncha', 'zhuwncgz'  ],
);

foreach ($tests as $t) {
  $prev = str_prev($t[0]);
  if ($prev === $t[1]) {
    print "str_prev('$t[0]') -> '$prev' : correct\n";
  } else {
    print "str_prev('$t[0]') -> '$prev' : wrong, should have been $t[1]\n";
  }
}

?>
$ php str_prev.php
str_prev('a') -> '' : correct
str_prev('b') -> 'a' : correct
str_prev('z') -> 'y' : correct
str_prev('aa') -> 'z' : correct
str_prev('ab') -> 'aa' : correct
str_prev('az') -> 'ay' : correct
str_prev('ba') -> 'az' : correct
str_prev('ca') -> 'bz' : correct
str_prev('aaa') -> 'zz' : correct
str_prev('aba') -> 'aaz' : correct
str_prev('abb') -> 'aba' : correct
str_prev('cha') -> 'cgz' : correct
str_prev('zhuwncha') -> 'zhuwncgz' : correct
Oranbe
  • 1
  • 1
-1

I could solve in this way. How is it? The cons is that it only can handle uppercase right now. Some more work can also fix that.

<?php
function get_previous_letter($string){
    $last = substr($string, -1);
    $part=substr($string, 0, -1);
    if(strtoupper($last)=='A'){
        $l = substr($part, -1);
        if($l=='A'){
            return substr($part, 0, -1)."Z";
        }
        return $part.chr(ord($l)-1);
    }else{
        return $part.chr(ord($last)-1);
    }
}
echo get_previous_letter("AAAAAA");
?>

CODEPAD

itsazzad
  • 6,868
  • 7
  • 69
  • 89
  • Because `AAA` is not in the given range, first you need to change the upper limit to include AAA in that answer as already mentioned in the comments :) – Hanky Panky Oct 14 '14 at 08:38
  • How to know the upper range? And will the array in the loop not create overhead? Try using zzzzz. It will stackoverflow – itsazzad Oct 14 '14 at 08:42
  • 1
    You have to define it according to your requirements. You cant just go about including every character. If zzzzzz is your actual requirement in excel then that means you are parsing a spreadsheet with millions of columns? Yes any unneeded range will use up all the memory available. If there are no limits then your answer is better. – Hanky Panky Oct 14 '14 at 08:44