Custom sort on value suffix representing sizes (XXS, XS, S, M, L, XL, XXL)

116 views Asked by At

I'm trying to sort my array values by size but I have a problem. My array pulls out values that are different than the values of the "cmp" function used to sort. The code is this:

function cmp($a, $b)
{

    $sizes = array(
        "XXS" => 0,
        "XS" => 1,
        "S" => 2,
        "M" => 3,
        "L" => 4,
        "XL" => 5,
        "XXL" => 6,
    );

    $asize = $sizes[$a];
    $bsize = $sizes[$b];

    if ($asize == $bsize) {
        return 0;
    }

    return ($asize > $bsize) ? 1 : -1;
}

$your_array = array("GL001_XXL", "GL001_L", "GL001_XXS", "GL001_S");

usort($your_array, "cmp");

When I try to run this code, it tells me "Undefined key".

How can I sort my array without errors?

2

There are 2 answers

4
Chris Haas On BEST ANSWER

As noted in the comments, if your pattern is truly xxx_size, you can just explode on the _ and grab the second item.

The <=> can be used whenever you've got the "return 0, 1 or -1" pattern, too.

function sort_array_item_by_size($a, $b): int
{
    static $sizes = [
        "XXS" => 0,
        "XS" => 1,
        "S" => 2,
        "M" => 3,
        "L" => 4,
        "XL" => 5,
        "XXL" => 6,
    ];

    // The next three lines really should include some error checking
    $size_a = explode("_", $a)[1];
    $size_b = explode("_", $b)[1];

    return $sizes[$size_a] <=> $sizes[$size_b];
}

$your_array = ["GL001_XXL", "GL001_L", "GL001_XXS", "GL001_S"];

usort($your_array, "sort_array_item_by_size");

print_r($your_array);

// Array
// (
//     [0] => GL001_XXS
//     [1] => GL001_S
//     [2] => GL001_L
//     [3] => GL001_XXL
// )

Demo: https://3v4l.org/Dqfi6

0
mickmackusa On

I advise against using usort() for this task because this involves parsing both strings each iteration (which is more than N times).

Instead, it will be more performant and very concise to use array_multisort(). Because your input strings appear to be consistently padded with zeros, you can simply parse the strings by grabbing the substring starting from the 6th character offset.

Code: (Demo)

define('SIZES', array_flip(['XXS', 'XS', 'S', 'M', 'L', 'XL', 'XXL']));

$array = ["GL001_XXL", "GL001_L", "GL001_XXS", "GL001_S"];

array_multisort(
    array_map(fn($v) => SIZES[substr($v, 6)], $array),
    $array
);
var_export($array);

This approach will have the bonus effect of breaking ties (identical size values) by performing natural comparisons on the whole strings.