value - Most efficient way to get next letter in the alphabet using PHP




php return character code (8)

Given any character from a to z, what is the most efficient way to get the next letter in the alphabet using PHP?


Get the users by first name start with a particular letter

In order to use LIKE, you need to use a meta_query

$args = array(
    'role' => 'user_role', 
    'meta_query' => array(
        array(
            'key' => 'first_name',
            'value' => array( 'A', 'B'),
            'compare' => 'BETWEEN'
        )
    )
);
$users= get_users($args);

How to implement in php next char using ord() and chr()

Requirements: Allow increment of a string in the same manner as PHP (please see links in the question). At first I thought: How unusual? I still think that.

Ok, We need to produce something that will do that without using the PHP routines.

Ok, what do have?

I thought of it as a 'base26' number. I chose to implement it as:

  • using an array for each digit that had a range of 1 .. 26.
  • convert the numerical digit in the array to display characters using an array lookup.

Working demonstration at eval.in

For convenience, I put it in PHP class:

I try and make the code understandable...

The base26 class:

class base26 { // it only does increment / addition 

    const
        numberBase = 26; 

    // These are characters used where A => 1 and Z is 26
    // These can be changed as you wish. They could be multibyte?

    //                              0        1         2     2
    //                              12345678901234567890123456
    public static $displayChars  = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';


    /**
    * current number -  least significant digit is digit[0]      
    * 
    * @var array $digit - integer with range 1 .. 26 
    */ 
    public $digit = array();


    // initialize if nothing provided
    public function __construct($letters = 'A') 
    {
        $this->init($letters);
    }

    /**
    * Convert letters to base26 and store in the $digit array
    * 
    * @param string $letters
    * @return void
    */
    public function init($letters = 'A')
    {
        $this->digit = array();
        $lsd =  strlen($letters) - 1;

        for ($idx = $lsd; $idx >= 0; $idx--) {
            $this->digit[] = $this->charValue($letters[$idx]);
        }
    }

    /**
    * Increment the 'least significant digit' and then propagate the `carry`
    * when it exceeds the number base limit. 
    * 
    * @param integer $int -- default to 1
    * @return void
    */
    public function inc($int = 1) // addition with carry - easy to understand
    {
        $curDigit = 0; // add to least significant digit
        $carry    = 0; 

        $this->digit[$curDigit] += $int; 

        while ($this->digit[$curDigit] > self::numberBase) {

            $carry = 1; // next carry
            $this->digit[$curDigit] %= self::numberBase;          // reset digit 

            $curDigit++; // check next digit...

            if (isset($this->digit[$curDigit])) {
                $this->digit[$curDigit] += $carry;
            }
            else {
                $this->digit[$curDigit] = $carry;   
            }
        }
    }

    /**
    * Convert a number to a character to display 
    * 
    * @param intger $int
    * 
    * @return char
    */
    public function toChar($int) 
    {
        return self::$displayChars[$int - 1];
    }

    /**
    * The value of the character in the number base range
    * 
    * @param undefined $letter
    * @return integer 
    */
    public function charValue($letter) 
    {
        return stripos(self::$displayChars, $letter) + 1;
    }

    /**
    * return the current number values as a string using the display characters
    * 
    * @return string 
    */ 
    public function current() 
    {
        $digitCount = count($this->digit);
        $outStr = str_pad('A', count($this->digit));
        $outIdx = $digitCount - 1;
        for ($idx = 0;  $idx < $digitCount; $idx++, $outIdx-- ) {
            $outStr[$outIdx] = $this->toChar($this->digit[$idx]);    
        }

        return $outStr;
    }
}

Examples:

// ---------------------------------------------
// show increment from Z -> AA
echo PHP_EOL;
$b26 = new base26('Z');
echo $b26->current() .'|';
$b26->inc();
echo $b26->current() .'|';

Output: Z|AA|

// ---------------------------------------------
// show increment from 'FY' -> FZ -> GA
echo PHP_EOL;
$b26->init('FY');
echo $b26->current() .'|';
$b26->inc();
echo $b26->current() .'|';
$b26->inc();
echo $b26->current() .'|';

Output: FY|FZ|GA|

// ---------------------------------------------
// Show it does what PHP does...
echo PHP_EOL;
$b26 = new base26();
while ($b26->current() <= 'Z') {
    echo $b26->current() .'|';
    $b26->inc();
}

Output: 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|AA|AB|AC|AD|AE|AF|AG|AH|AI|AJ|AK|AL|AM|AN|AO ...

As I observed in my note, after you reach/pass the value of 'Z', your $char variable becomes a 2 char string effectively. I cannot comment on details about this, but the following would be a probably not too expensive generic solution to your request: always use the "last" character of the string.

  $cmax=ord('A');
  $char='A';
  foreach (range('A', 'z') as $c) {
    $next=chr((((ord($c) - $cmax) + 1) % 26) + $cmax);
    ++$char;
    echo ord($c)." ".$c." ".$next." ".$char[strlen($char)-1]."\n";
  }

https://eval.in/537739

65 A B B
66 B C C
67 C D D
68 D E E
69 E F F
70 F G G
71 G H H
72 H I I
73 I J J
74 J K K
75 K L L
76 L M M
77 M N N
78 N O O
79 O P P
80 P Q Q
81 Q R R
82 R S S
83 S T T
84 T U U
85 U V V
86 V W W
87 W X X
88 X Y Y
89 Y Z Z
90 Z A A
91 [ B B
92 \ C C
93 ] D D
94 ^ E E
95 _ F F
96 ` G G
97 a H H
98 b I I
99 c J J
100 d K K
101 e L L
102 f M M
103 g N N
104 h O O
105 i P P
106 j Q Q
107 k R R
108 l S S
109 m T T
110 n U U
111 o V V
112 p W W
113 q X X
114 r Y Y
115 s Z Z
116 t A A
117 u B B
118 v C C
119 w D D
120 x E E
121 y F F
122 z G G

As has been mentioned, using LIKE means it will match any value that contains the letter, regardless if the letter is at the start or not. So using LIKE probably won't get you what you want.

The approach in WordPress seems to be using the "BETWEEN" comparison, so that it will match any value where the word is between one letter and the subsequent letter.

Per the tutorial at http://www.essentialwp.com/tag/meta_query/, you can get the character you want the word(s) to start with, and then use PHP functionality to find the subsequent character, which allows you to filter the results correctly.

// Get the value for surname and attach it to a variable
$letter = $_GET['surname'];]

// We need to establish the letter after the first letter from $letter, so in this case 'D'
$letterAfter = chr(ord($letter)+1);

// Craft a custom wp_query call
$args = array(
    'post_type' => 'profiles',  // use this if you are using a custom post type
    'order' => 'ASC',
    'meta_query' => array(
        array(
            'key' => 'surname', // name of your custom field
            'value' => array( $letter, $letterAfter ),
            'orderby' => 'meta_value',
            'compare' => 'BETWEEN',
            'order' => 'ASC'
        )
    )
 );
$wp_query = new WP_Query( $args );

// then run your loop....

You'll likely need to handle the letter Z differently, see Get the users by first name start with letter “Z”

You might also be interested in Most efficient way to get next letter in the alphabet using PHP


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


It depends on what you want to do when you hit Z, but you have a few options:

$nextChar = chr(ord($currChar) + 1); // "a" -> "b", "z" -> "{"

You could also make use of PHP's range() function:

$chars = range('a', 'z');  // ['a', 'b', 'c', 'd', ...]

The most efficient way of doing this in my opinion is to just increment the string variable.

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

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

As seen incrementing 'z' give 'aa' if you don't want this but instead want to reset to get an 'a' you can simply check the length of the resulting string and if its >1 reset it.

$ch = 'a';
$next_ch = ++$ch; 
if (strlen($next_ch) > 1) { // if you go beyond z or Z reset to a or A
 $next_ch = $next_ch[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))]; 




alphabet