dongyuan1983
dongyuan1983
2017-10-17 10:14

PHP:使用递增的字符变量进行计数时的比较

已采纳

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!

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

4条回答

  • dongxiao1591 dongxiao1591 4年前

    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') . "
    ";
    echo "   z < aa   = " . ((AisLessThanB(   'z',   'aa')) ? 'true' : 'false') . "
    ";
    echo "  aa < az   = " . ((AisLessThanB(  'aa',   'az')) ? 'true' : 'false') . "
    ";
    echo "  az < zz   = " . ((AisLessThanB(  'az',   'zz')) ? 'true' : 'false') . "
    ";
    echo "  zz < aaa  = " . ((AisLessThanB(  'zz',  'aaa')) ? 'true' : 'false') . "
    ";
    echo " aaa < zzz  = " . ((AisLessThanB( 'aaa',  'zzz')) ? 'true' : 'false') . "
    ";
    echo " zzz < aaaa = " . ((AisLessThanB( 'zzz', 'aaaa')) ? 'true' : 'false') . "
    ";
    echo "
    ===================
    
    ";
    echo "   z < a    = " . ((AisLessThanB(   'z',    'a')) ? 'true' : 'false') . "
    ";
    echo "  aa < z    = " . ((AisLessThanB(  'aa',    'z')) ? 'true' : 'false') . "
    ";
    echo "  az < aa   = " . ((AisLessThanB(  'az',   'aa')) ? 'true' : 'false') . "
    ";
    echo "  zz < az   = " . ((AisLessThanB(  'zz',   'az')) ? 'true' : 'false') . "
    ";
    echo " aaa < zz   = " . ((AisLessThanB( 'aaa',   'zz')) ? 'true' : 'false') . "
    ";
    echo " zzz < aaa  = " . ((AisLessThanB( 'zzz',  'aaa')) ? 'true' : 'false') . "
    ";
    echo "aaaa < zzz  = " . ((AisLessThanB('aaaa',  'zzz')) ? 'true' : 'false') . "
    ";
    

    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!

    点赞 评论 复制链接分享
  • douzhan3900 douzhan3900 4年前

    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

    点赞 评论 复制链接分享
  • dsplos5731 dsplos5731 4年前

    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;
      }
    }
    
    点赞 评论 复制链接分享
  • dqv84329 dqv84329 4年前

    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 :)

    点赞 评论 复制链接分享

相关推荐