1

I am currently trying to sort some clothing size arrays (S M L XL XXL etc.) that belong to an array. I am able to do this via the following function (thanks to this place Php Array Sorting Clothing Sizes (XXS XS S M L XL XXL) and Numbers on a Dynamic Array):

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;

    }

    usort($the_array, "cmp");

This is all very well for an array that looks like this: $the_array("S", "M", "XL"). However, my array looks a bit like this:

$the_array = array("S : price £10", "XXL : price £10", "M : price £10", "XS : price £10")

This makes it not work... I need a function that ideally only looks at the first part of the array up to the ":". Is there such a thing?

Thanks for the help.

Community
  • 1
  • 1
30secondstosam
  • 4,476
  • 4
  • 28
  • 33
  • This is wrong: "XXS" => 0 ... etc, In an array you must have data binded to metadata not inverse. The correct bidding is like this: metadata => data, for your case is $var = array('XXS', 'XS', 'S', 'M', 'L', 'XL', 'XXL'), after that make an var_dump($var); you will see that you will have ascending keys that you can use for sorting. After fixing this try to rethink your problem. And you alsow need to normalize **$the_array** to have a canonical form, metadata => data binding, something like this: $size_price = array('S'=>10, 'XXL'=> 10 ...). – Starlays Nov 01 '12 at 13:00
  • Sorry ^^ my examples were just examples. I was not being completely literal. I just wanted to make the point about how it worked. Thanks for your time though. – 30secondstosam Nov 01 '12 at 13:18

4 Answers4

2

This solution will search the beginning of each string for the size values. It only works so long as no size is the prefix of another size, such as 'X' would be a prefix of 'XL'. It doesn't rely on the particular format of your data; it will find the earliest occurrence of a valid size string.

// Your comparison function:
function cmp($a, $b) {
  // Make the array static for performance.
  static $sizes = array('XXS', 'XS', 'S', 'M', 'L', 'XL', 'XXL');

  // Find the size of $a and $b (the first string for which strpos===0)
  $asize = 100; // Sort non-size strings after matching sizes.
  $apos = -1;
  $bsize = 100;
  $bpos = -1;
  foreach ($sizes AS $val => $str) {
    // It's important to use `===` because `==` will match on
    // FALSE, which is returns for no match.
    if (($pos = strpos($a, $str)) !== FALSE && ($apos < 0 || $pos < $apos)) {
      $asize = $val;
      $apos = $pos;
    }
    if (($pos = strpos($b, $str)) !== FALSE && ($bpos < 0 || $pos < $bpos)) {
      $bsize = $val;
      $bpos = $pos;
    }
  }

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

usort($the_array, 'cmp');
meustrus
  • 6,637
  • 5
  • 42
  • 53
  • I just noticed a minor error in `$sizes`. Make sure you check my edit or else you won't be able to recognize XXS. – meustrus Nov 01 '12 at 14:41
1

Works for any quantity of X's.

function cmpSizes($a, $b) {
    list($a, $b) = array(strtolower($a), strtolower($b));
    $weights = array('s' => 1, 'm' => 2, 'l' => 3);
    $primaryWeights = array();
    foreach(array('a', 'b') as $var) {
        if(is_numeric(strpos($$var, 's'))) {
            $weight = $weights['s'];
        } elseif(is_numeric(strpos($$var, 'm'))) {
            $weight = $weights['m'];
        } elseif(is_numeric(strpos($$var, 'l'))) {
            $weight = $weights['l'];
        } else {
            return -1;
        }
        $primaryWeights[$var] = $weight;
    }
    if($primaryWeights['a'] === $primaryWeights['b']) {
        $xCt = array('a' => substr_count($a, 'x'), 'b' => substr_count($b, 'x'));
        if($xCt['a'] > $xCt['b']) {
            return $primaryWeights['a'] === $weights['s'] ? -1 : 1;
        } elseif($xCt['a'] < $xCt['b']) {
            return $primaryWeights['a'] === $weights['s'] ? 1 : -1;
        }
        return 0;
    } elseif ($primaryWeights['a'] > $primaryWeights['b']) {
        return 1;
    }
    return -1;
}

So example code

$userSizes = array('XS','S','XXXXS','XXL','L','M','XL');
usort($userSizes, 'cmpSizes');
var_dump($userSizes);

Yields

array(7) {
  [0] =>
  string(5) "XXXXS"
  [1] =>
  string(2) "XS"
  [2] =>
  string(1) "S"
  [3] =>
  string(1) "M"
  [4] =>
  string(1) "L"
  [5] =>
  string(2) "XL"
  [6] =>
  string(3) "XXL"
}
Daniel B.
  • 1,650
  • 1
  • 19
  • 40
0

Calling usort() and making use of explode() in its callback will allow you to sort on the first part.

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

// Anonymous function -- PHP 5.3+ only...
// Uses the $sizes array from the higher scope...
usort($the_array, function($a, $b) use ($sizes) {
  // Split each on space,colon,space
  list($asize, $aprice) = explode(' : ', $a);
  list($bsize, $bprice) = explode(' : ', $b);

  // Perform the usort comparison
  // which uses the sizes matched above as array keys to the $sizes array
  if ($sizes[$asize] == $sizes[$bsize]) {
    return 0;
  }
  return $sizes[$asize] < $sizes[$bsize] ? -1 : 1;
});

// Output:
var_dump($the_array);
array(4) {
  [0] =>
  string(15) "XS : price £10"
  [1] =>
  string(14) "S : price £10"
  [2] =>
  string(14) "M : price £10"
  [3] =>
  string(16) "XXL : price £10"
}

If the delimiter is not always space,colon,space as above and varies, you would need to use preg_split() instead of explode().

// Split on any number of spaces surrounding the :
list($asize, $aprice) = preg_split('/\s*:\s*/', $a);
Michael Berkowski
  • 267,341
  • 46
  • 444
  • 390
0

If all you're trying to do is to sort the array by sizes, the following will do:

/** THE SORT FUNCTION **/
function sortSize($data_arr)
{
    $sizes_arr = array('XXS' => 0, 'XS'  => 1, 'S'   => 2, 'M'   => 3, 'L'   => 4, 'XL'  => 5, 'XXL' => 6);

    $data_sort_arr = array();
    foreach ($data_arr as $value)
    {
        //get the size
        $size_item_arr = explode(':', $value);
        $size_item_str = trim($size_item_arr[0]);

        //get the position of size from sizes array
        $size_pos_int = intval($sizes_arr[$size_item_str]);

        //populate new array with sorted data
        $data_sort_arr[$size_pos_int] = $value;
    }

    //sort then reset keys numerically
    ksort($data_sort_arr);
    return array_values($data_sort_arr);
}
/** THE SORT FUNCTION **/

/** THE TEST **/
$data_arr = array("S : price £10", "XXL : price £10", "M : price £10", "XS : price £10");    
print_r(sortSize($data_arr));
/** THE TEST **/

I got the following result:

Array
(
    [0] => XS : price £10
    [1] => S : price £10
    [2] => M : price £10
    [3] => XXL : price £10
)

I hope this is what you need?!

Cheers.

ObiHill
  • 11,448
  • 20
  • 86
  • 135