2

i have array of digits, special chars and letters. How i can sort it that first walked digist, then special chars and leters.

$c64 = str_split(
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
);

I trying doing like this

array_multisort($l64, SORT_DESC);

But it return special chars, digits and letters

Smandoli
  • 6,919
  • 3
  • 49
  • 83
Patrick Burns
  • 1,763
  • 6
  • 21
  • 35
  • 1
    i guess you have to write a function for that yourself and use uasort http://www.php.net/manual/en/function.uasort.php – Preexo Jul 04 '13 at 15:31
  • So you are using `SORT_DESC` and actually write *but*? That makes no sense. The documentation of `SORT_DESC` was not announced to work the way you're using it, so how could you overlook that if I may ask? – hakre Jul 04 '13 at 15:47

2 Answers2

4

This sort of thing can be implemented in very "clever" ways, so I couldn't resist:

$c64 = str_split(
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
);

$sortValue = function($char) {
    return !ctype_digit($char) << 9 | !ctype_punct($char) << 8 | ord($char);
}

$sortFunction = function($x, $y) use ($sortValue) {
    return $sortValue($x) - $sortValue($y);
};

usort($c64, $sortFunction);

See it in action.

How it works

I 'm taking advantage of the fact that ord returns the ordinal value of a character, which is both what plain sort uses to sort the input and also constrained to the range [0, 255] -- i.e. it uses no more than 8 bits for its value.

What this code is doing is taking the return value of ord (remember: that's how the default sort works) and augmenting it with two extra bits of information: the MSB represents "is this character not a digit?" and the LSB represents "is this character not punctuation?".

These bits are ORed together with the ordinal value, producing 10-bit quantities that look like this:

Bit#    10  9  8  7  6  5  4  3  2  1  0
         ^  ^  ^                       ^
         |  |  \-----------+-----------/   
         |  |              \------------- ord
         |  \---------------------------- "not punctuation" bit
         \------------------------------- "not digit" bit

What happens when you treat these values as integers? Clearly values that correspond to non-digits are going to be larger than any others, and similarly values that correspond to non-punctuation are going to be larger than letters and such.

So by using the result of $x - $y to determine which item is larger we are in effect making digits be considered smaller than everything else and punctuation larger than digits but smaller than non-digits. This makes sort put digits first, then punctuation, then everything else when it makes an ascending sort.

Finally, it is very significant that the value of ord takes part in the comparison: for elements in the same class (e.g. digits) we want their sort order to be the same as the order a plain sort would produce.

hakre
  • 193,403
  • 52
  • 435
  • 836
Jon
  • 428,835
  • 81
  • 738
  • 806
0
usort($c64, function($a, $b){
    $aord = (is_numeric($a)) ? $a : ord($a);
    $bord = (is_numeric($b)) ? $b : ord($b);
    if ($aord == $bord) return 0;
    return ($aord < $bord) ? -1 : 1;
});
Expedito
  • 7,771
  • 5
  • 30
  • 43