Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
360 views
in Technique[技术] by (71.8m points)

How to sort multidimensional array in PHP version 5.4 - with keys?

I asked a very similar question here and received an excellent answer.

But then I had to make one change to my array data, it broke the sort algorithm, and I don't know why.

In the previous question my array looked like this:

Array
(
    [0] => Array
        (
            [0] => Game
            [1] => Date
            [2] => Site
            [3] => Address
            [4] => FirstName
            [5] => LastName
            [6] => Email
            [7] => Phone
        )

    [1] => Array
        (
            [0] => B-Dry @ Blue Wave DH
            [1] => 7/9/2019 13:00
            [2] => Blue Wave Dover City Park
            [3] => Dover
            [4] => John
            [5] => Doe
            [6] => [email protected]
            [7] => (555) 555-4797
        )

    [2] => Array
        (
            [0] => B-Dry @ Blue Wave DH
            [1] => 7/9/2019 13:00
            [2] => Blue Wave Dover City Park
            [3] => Dover
            [4] => Frank
            [5] => Sinatra
            [6] => [email protected]
            [7] => (555) 685-5555
        )

    [3] => Array
        (
            [0] => B-Dry @ Gnaden
            [1] => 6/7/2019 18:00
            [2] => Gnaden Indian Valley HS
            [3] => Gnadenhutten
            [4] => Jimmy
            [5] => Dean
            [6] => [email protected]
            [7] => (330) 555-5555
        )
   [...many more...]
)

And applying the following sort algorithm from AymDev sorted it brilliantly:

$labelsRow = array_shift($cleanSheetArray);

$cleanSheetArray = array_map(
function ($cleanRowArray)
    {
    $cleanRowArray[1] = DateTime::createFromFormat('d/m/Y H:i', $cleanRowArray[1]);
    return $cleanRowArray;
    }

, $cleanSheetArray);

SortSheet($cleanSheetArray, 3);

function SortSheet(&$arr, $max_index = false, $index = 0) { // $index represents the number of columns to sort on, in order from the first column

    function mult_usort_callback($a, $b, $max_index, $index) {
        $max_index = $max_index ?: (count($a) - 1);

        // Recursively sort till the max index
        if ($a[$index] == $b[$index]) {
            if ($index < $max_index) {
                return mult_usort_callback($a, $b, $max_index, ($index + 1));
            } else {
                return 0;
            }
        }
        return $a[$index] > $b[$index] ? 1 : -1;
    };

    usort($arr, create_function('$a, $b', 'return mult_usort_callback($a, $b, ' . $max_index . ', ' . $index . ');'));
}

This sorts my multidim array and so far everything is perfect.

But then I made a change to my array structure thusly:

[151] => Array
    (
        [Game] => Kaufman @ B-Dry DH
        [Date] => DateTime Object
            (
                [date] => 2019-04-05 13:00:00.000000
                [timezone_type] => 3
                [timezone] => America/Denver
            )

        [Site] => B-Dry City Rec Park - Otsego Ave
        [Address] => Coshocton
        [FirstName] => Joe
        [LastName] => Barnes
        [Email] => [email protected]
        [Phone] => (740) 000-0000
    )

[152] => Array
    (
        [Game] => Kaufman @ B-Dry DH
        [Date] => DateTime Object
            (
                [date] => 2019-04-05 13:00:00.000000
                [timezone_type] => 3
                [timezone] => America/Denver
            )
        [Site] => B-Dry Ridgewood High School
        [Address] => Coshocton
        [FirstName] => 
        [LastName] => 
        [Email] => 
        [Phone] => 
    )

[153] => Array
    (
        [Game] => Kaufman @ B-Dry DH
        [Date] => DateTime Object
            (
                [date] => 2019-04-05 13:00:00.000000
                [timezone_type] => 3
                [timezone] => America/Denver
            )
         [Site] => B-Dry City Rec Park - Otsego Ave
         [Address] => Coshocton
         [FirstName] => 
         [LastName] => 
         [Email] => 
         [Phone] => 
    )

Observe that I changed the keys from indexers to column names. This is hugely helpful to me in processing later logic. Along with this data/key change, I attempted to change the date objectification thusly:

$cleanSheetArray = array_map(function ($cleanRowArray) {
    $cleanRowArray['Date'] = DateTime::createFromFormat('d/m/Y H:i', $cleanRowArray['Date']);
    return $cleanRowArray;
}, $cleanSheetArray);

That did appear to work from the standpoint of converting the string dates into date objects. But it broke the sorting. You can see evidence of the broken sort by looking at the 3 elements of the new array. Observe that array segments 151 and 153 should be consecutive because Game, Date, and Site are identical. 152 does not have identical values to 151 and 152. Game and Date are the same, but Site is different. In the previous array structure (using index keys instead of named keys) the sort was correct.

I suspect that I need to make some sort of change to the SortSheet() function, but I can't figure out what it is.

Important note: The key-name sequence will always be identical. In other words, the key sequence will always be Game, then Date, then Site, etc. So that sequence can always be counted on.

Any ideas?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

An quick fix, using the previous numerically ordered array, could have been:

// Save the keys
$keys = array_shift($data);

/* here, do the sorting ... */

// Then apply the keys to your ordered array
$data = array_map(function ($item) {
    global $keys;
    return array_combine($keys, $item);
}, $data);

But let's update my previous function:

function mult_usort_assoc(&$arr, $max_index = false, $index = false) {

    function mult_usort_callback($a, $b, $max_index, $index) {
        $max_index = $max_index ?: key(end($a));    // If not provided, takes last key
        $index     = $index ?: array_keys($a)[0];   // If not provided, takes first key

        // When data are equal, sort by next key only if not after the $max_index
        if ($a[$index] == $b[$index]) {
            $next = array_keys($a)[array_search($index,array_keys($a))+1];
            if ($index !== $max_index && !is_null($next)) {
                return mult_usort_callback($a, $b, $max_index, $next);
            } else {
                return 0;
            }
        }
        return $a[$index] > $b[$index] ? 1 : -1;
    }

    /* This part have been improved as we shouldn't use create_function()
       This function is now usable in PHP 7.2 */
    usort($arr, function ($a, $b) use ($max_index, $index) {
        return mult_usort_callback($a, $b, $max_index, $index);
    });
}

Usage and output:

mult_usort_assoc($data, 'Site');
echo '<pre>' . print_r($data, true) . '</pre>';

Used functions in this answer:


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...