Conner Burnett
Conner Burnett

Reputation: 512

Sorting of multidimensional array with with numbers and letters

How to sort multidimensional array. This is what my array looks like

[0] => Array
    (
      [id] => 1
      [title] => 3A
      [active] => 1
    )
[1] => Array
    (
      [id] => 1
      [title] => A
      [active] => 1
    )
[2] => Array
    (
      [id] => 1
      [title] => 2A
      [active] => 1
    )
[3] => Array
    (
      [id] => 1
      [title] => B
      [active] => 1
    )

I have tried several usort methods, but cannot seem to get this to work. I am needing the array sorted so that it will sort by numeric then by alpha numeric like so: A,B,2A,3A. I am not sure if this would be possible without adding a position field to dictate what order the titles are suppose to be in, or am I missing something here?

Upvotes: 1

Views: 315

Answers (3)

Casimir et Hippolyte
Casimir et Hippolyte

Reputation: 89547

You can build a "key" for each item where the digit part is padded on the left with 0s, this way, the sort function can perform a simple string comparison:

$temp = [];

foreach ($arr as $v) {
    $key = sscanf($v['title'], '%d%s');
    if (empty($key[0])) $key = [ 0, $v['title'] ];
    $key = vsprintf("%06d%s", $key);
    $temp[$key] = $v;
}

ksort($temp);

$result = array_values($temp);

demo

This technique is called a "Schwartzian Transform".

Upvotes: 1

Oleks
Oleks

Reputation: 1633

You can resolve that problem with help of usort and custom callback:

function customSort($a, $b)
{
    if ($a['id'] == $b['id']) {
        //if there is no number at the beginning of the title, I add '1' to temporary variable
        $aTitle = is_numeric($a['title'][0]) ? $a['title'] : ('1' . $a['title']);
        $bTitle = is_numeric($b['title'][0]) ? $b['title'] : ('1' . $b['title']);

        if ($aTitle != $bTitle) {
            return ($aTitle < $bTitle) ? -1 : 1;
        }
        return 0;
    }
    return ($a['id'] < $b['id']) ? -1 : 1;
}

usort($array, "customSort");

At first the function compares 'id' values and then if both items are equal it checks 'title' values.

Upvotes: 0

Guillaume Sainthillier
Guillaume Sainthillier

Reputation: 1685

As @Kargfen said, you can use usort with your custom function. Like this one :

usort($array, function(array $itemA, array $itemB) {
   return myCustomCmp($itemA['title'], $itemB['title']);
}); 

function myCustomCmp($titleA, $titleB) {
    $firstLetterA = substr($titleA, 0, 1);
    $firstLetterB = substr($titleB, 0, 1);

    //Compare letter with letter or number with number -> use classic sort
    if((is_numeric($firstLetterA) && is_numeric($firstLetterB)) || 
    (!is_numeric($firstLetterA) && !is_numeric($firstLetterB)) ||
    ($firstLetterA === $firstLetterB)
    ) {
        return strcmp($firstLetterA, $firstLetterB);
    }

    //Letters first, numbers after
    if(! is_numeric($firstLetterA)) {
        return -1;
    }

    return 1;
}

This compare-function is just based on the first letter of your titles, but it can do the job ;-)

Upvotes: 0

Related Questions