1

I am doing an Excel-like webapp where the columns are "numbered" like a,b,c,...,aa,ab,...,az,ba,...,zz,aaa,.... But if I write

$start = 'a';
$end = 'z';

while($start <= $end){
    echo $start++ . ", ";
}

(notice $start <= $end) it will go a up to yz, not just a-z because

echo 'aa' <= 'z'; // true, but
echo 'za' <= 'z'; // false.

Is there a function or a way to compare two variables taking in mind that

$a = "z"; echo ++$a; // aa
$a = "zz"; echo ++$a; // aaa

so that

$a = "z"; $b = $a; $b++; // $b = aa

AisLessThanB(a,b); // returns true

and so on? That means where

a < z < aa < az < zz < aaa < zzz < aaaa

is always true?

EDIT: Something like

$start = 'a';
$end = 'cv'; // 100 columns

should work since we work with loads of columns.

Thank you!

2
  • 1
    why is AA < Z Commented Oct 17, 2017 at 10:17
  • @alej are we just trying to validate that an infinite loop will not occur or are you always going to have sufficient control of the start and finish? Are you not happy to simply check with !==? I mean PHP8.3 has str_increment(), but I don't see that adding any benefit over ++. Please clarify. Commented Feb 9, 2024 at 10:18

4 Answers 4

0

Try this:

// From https://stackoverflow.com/a/3580935/3088508
function getLetterIndexInAlphabet ($letter) {
    return ord($letter) - 96;
}

function convertStringToNumber ($inStr) {
    $letters = array_reverse(str_split($inStr));
    $outNum = 0;
    for ($i = count($letters) - 1; $i >= 0; $i--) {
        $outNum += getLetterIndexInAlphabet($letters[$i]) * (pow(26, $i));
    }
    return $outNum;
}

function AisLessThanB ($a, $b) {
    $aInt = convertStringToNumber($a);
    $bInt = convertStringToNumber($b);
    return ($aInt < $bInt);
}

convertStringToNumber is the most complicated function in here, so let's have a look at how it works on a few values:

  • 'c' evaluates to 2 which is calculated by:
    1. 'c' = 3 (index in alphabet)
  • 'ba' evaluates to 53 which is calculated by:
    1. 'b' = 52 = (2 (index in alphabet) x (26 (base value) ^ 1 (column number))).
    2. 'a' = 1 (index in alphabet)
  • 'bca' evaluates to 1431 which is calculated by:
    1. 'b' = 1352 = (2 (index in alphabet) x (26 (base value) ^ 2 (column number))).
    2. 'c' = 78 = (3 (index in alphabet) x (26 (base value) ^ 1 (column number))).
    3. 'a' = 1 (index in alphabet)
  • 'bdca' evaluates to 37935 which is calculated by:
    1. 'b' = 35152 = (2 (index in alphabet) x (26 (base value) ^ 3 (column number))).
    2. 'd' = 2704 = (4 (index in alphabet) x (26 (base value) ^ 2 (column number))).
    3. 'c' = 78 = (3 (index in alphabet) x (26 (base value) ^ 1 (column number))).
    4. 'a' = 1 (index in alphabet)

Here's some test cases:

echo "   a < z    = " . ((AisLessThanB(   'a',    'z')) ? 'true' : 'false') . "\n";
echo "   z < aa   = " . ((AisLessThanB(   'z',   'aa')) ? 'true' : 'false') . "\n";
echo "  aa < az   = " . ((AisLessThanB(  'aa',   'az')) ? 'true' : 'false') . "\n";
echo "  az < zz   = " . ((AisLessThanB(  'az',   'zz')) ? 'true' : 'false') . "\n";
echo "  zz < aaa  = " . ((AisLessThanB(  'zz',  'aaa')) ? 'true' : 'false') . "\n";
echo " aaa < zzz  = " . ((AisLessThanB( 'aaa',  'zzz')) ? 'true' : 'false') . "\n";
echo " zzz < aaaa = " . ((AisLessThanB( 'zzz', 'aaaa')) ? 'true' : 'false') . "\n";
echo "\n===================\n\n";
echo "   z < a    = " . ((AisLessThanB(   'z',    'a')) ? 'true' : 'false') . "\n";
echo "  aa < z    = " . ((AisLessThanB(  'aa',    'z')) ? 'true' : 'false') . "\n";
echo "  az < aa   = " . ((AisLessThanB(  'az',   'aa')) ? 'true' : 'false') . "\n";
echo "  zz < az   = " . ((AisLessThanB(  'zz',   'az')) ? 'true' : 'false') . "\n";
echo " aaa < zz   = " . ((AisLessThanB( 'aaa',   'zz')) ? 'true' : 'false') . "\n";
echo " zzz < aaa  = " . ((AisLessThanB( 'zzz',  'aaa')) ? 'true' : 'false') . "\n";
echo "aaaa < zzz  = " . ((AisLessThanB('aaaa',  'zzz')) ? 'true' : 'false') . "\n";

And they output:

   a < z    = true
   z < aa   = true
  aa < az   = true
  az < zz   = true
  zz < aaa  = true
 aaa < zzz  = true
 zzz < aaaa = true

===================

   z < a    = false
  aa < z    = false
  az < aa   = false
  zz < az   = false
 aaa < zz   = false
 zzz < aaa  = false
aaaa < zzz  = false

eval.in demo

Thanks for asking such an interesting question!

Sign up to request clarification or add additional context in comments.

1 Comment

Great answer! I just changed AisLessThanB's return to $aInt - $bInt so I can use AisLessThanB($a, $b) vs < 0, <= 0 or == 0. Thank you!
0

I don't think it is a good idea to increment the string like this. Actually in increment an ASCII value of a char. The comparison of the strings in this case is in alphabetical order ( https://stackoverflow.com/a/12888720/5346387 ) so I guess it will not work as you expect.

But for your question - I don't think there would be an PHP native function like this. I suggest to create a mapping function from linear list of numbers [0,1,2,3...27,28..] to your alphabetical code [a,b,c..aa,ab...]. I hope I helped :)

Comments

0

What if you compare the string length before comparing the characters?

function aIsLessThanB($a, $b) {

  if(strlen($a) == strlen($b)) {
    $strlen = strlen($a);

    for($i = 0; $i < $strlen; $i++) {

      // pick single character at position $i
      $charA = substr($a, $i, 1);
      $charB = substr($b, $i, 1);

      // convert ASCII to integer and compare
      if(ord($charA) < ord($charB)) {
       return true;
      } elseif(ord($charA) > ord($charB)) {
       return false;
      }
    }

  } elseif(strlen($a) < strlen($b)) {
    return true;
  } else {
    return false;
  }
}

Comments

0

I don't think there is a built-in way to achieve this, but I was able to come up with these 2 methods:

$start = 'a';
$end = 'z';

function sumDecimal($n)
{
    return(ord($n));
}

while($start <= $end)
{
    $startArr = str_split($start);
    $endArr = str_split($end);
    $startVal = array_sum(array_map("sumDecimal", str_split($start)));
    $endVal = array_sum(array_map("sumDecimal", str_split($end)));
    if($startVal <= $endVal)
    {
        echo $start++ . ", ";
    }
    else
    {
        break;
    }
}

This will split the $start and $end strings into Array and then will get ASCII value of all of its characters and will sum it up. As long as the value of $start is less than or equal to the value of $end it will iterate, otherwise it will stop. DEMO

Beware: it will treat anything other than a...a or z...z incorrectly. e.g. It will evaluate az and za as the same.

Or

$start = 'a';
$end = 'z';

while($start <= $end)
{
    if(strlen($start) <= strlen($end))
    {
        echo $start++ . ", ";   
    }
    else
    {
        break;
    }
}

This will iterate over as long as the length of $start is less than or equal to $end. DEMO

1 Comment

Thank you for your comment. Your code stops already at the second iteration when $start = 'a'; $end = 'aa'; are set, and sadly our files have no less than ~30 columns so this won't work for us.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.