1

This is a follow-up to this slightly simpler question. The main difference is the increased complexity of the items being sorted (arrays, not integers), required usort

Sorry about the title, starting to stretch my descriptive powers! I've got a PHP associative array, with strings as keys. Each value is an array of 2-element arrays (they're data points for a graph). I want to sort each array of points, according to the numerical value of the first element in the point. I'm convinced this should be easy, and I've found enough examples that I think I should be doing the right thing, but it's not quite working, so there's a typo or I'm an idiot or something...

PHP:

//Each fruit corresponds to an array (series) of 2-element arrays (data points)
$data = [
    'banana' => [
        [1,1],[3,1],[2,1]
    ],
    'orange' => [
        [5,1],[1,1],[3,1]
    ]
];

echo "Before sort:\n";
var_dump($data);

//For each fruit, I want to order the 2-element arrays by the first element
//NB following previous Q tried & to force reference, doesn't seem to work here
foreach ($data as $key => &$series)
{
    usort($series, 'sortByZeroethElement');
}

echo "\n\nAfter sort:\n";
var_dump($data);

function sortByZeroethElement($a, $b)
{
    return (int) $a[0] > (int) $b[0] ? 1 : (int) $a[0] == (int) $b[0] ? 0 : -1;
}

Output:

Before sort:
array(2) {
  'banana' =>
  array(3) {
    [0] =>
    array(2) {
      [0] =>
      int(1)
      [1] =>
      int(1)
    }
    [1] =>
    array(2) {
      [0] =>
      int(3)
      [1] =>
      int(1)
    }
    [2] =>
    array(2) {
      [0] =>
      int(2)
      [1] =>
      int(1)
    }
  }
  'orange' =>
  array(3) {
    [0] =>
    array(2) {
      [0] =>
      int(5)
      [1] =>
      int(1)
    }
    [1] =>
    array(2) {
      [0] =>
      int(1)
      [1] =>
      int(1)
    }
    [2] =>
    array(2) {
      [0] =>
      int(3)
      [1] =>
      int(1)
    }
  }
}


After sort:
array(2) {
  'banana' =>
  array(3) {
    [0] =>
    array(2) {
      [0] =>
      int(2)
      [1] =>
      int(1)
    }
    [1] =>
    array(2) {
      [0] =>
      int(3)
      [1] =>
      int(1)
    }
    [2] =>
    array(2) {
      [0] =>
      int(1)
      [1] =>
      int(1)
    }
  }
  'orange' =>
  array(3) {
    [0] =>
    array(2) {
      [0] =>
      int(3)
      [1] =>
      int(1)
    }
    [1] =>
    array(2) {
      [0] =>
      int(1)
      [1] =>
      int(1)
    }
    [2] =>
    array(2) {
      [0] =>
      int(5)
      [1] =>
      int(1)
    }
  }
}

Desired result:

'banana' => [
        [1,1],[2,1],[3,1]
    ],
    'orange' => [
        [1,1],[3,1],[5,1]
    ]

As you can see, in the output the inner arrays of points have not been sorted. What am I doing wrong? (PHP 5.5.9, Windows 7)

Community
  • 1
  • 1
frumious
  • 1,567
  • 15
  • 25

2 Answers2

3

your comparison function malfunctions. You need to group the 'else', or it does the following (I believe): in the first else-case it valuates 1 or (int) $a[0] == (int) $b[0]. The next ? then makes it evaluate that as boolean again (1 == true or (int) $a[0] == (int) $b[0]). I added 2 brackets and this works for me:

function sortByZeroethElement($a, $b)
{
    return (int) $a[0] > (int) $b[0] ? 1 : ((int) $a[0] == (int) $b[0] ? 0 : -1);
}

And heres a shorter version using a lambda and some math:

foreach ($data as $key => &$series)
{
    usort($series, function($a, $b) { return $a[0] - $b[0]; });
}
Felk
  • 7,720
  • 2
  • 35
  • 65
  • Ah, that's it, you're a star. I actually tried that second comparison function at one point, but that was before I realised I needed a reference in the foreach. Double whammy... – frumious Jun 04 '14 at 00:31
1

Alternatively, you could just do a simple foreach loop in conjunction to serialize/unserialize. Consider this example:

$data = [
    'banana' => [
        [1,1],[3,1],[2,1]
    ],
    'orange' => [
        [5,1],[1,1],[3,1]
    ]
];

foreach($data as $fruit => $values) {
    $temp = array();
    foreach($values as &$value) {
        $temp[] = serialize($value);
    }
    sort($temp);
    $temp = array_map('unserialize', $temp);
    $data[$fruit] = $temp;
}

echo '<pre>';
print_r($data);
echo '</pre>';

Sample Output:

Array
(
    [banana] => Array
        (
            [0] => Array
                (
                    [0] => 1
                    [1] => 1
                )

            [1] => Array
                (
                    [0] => 2
                    [1] => 1
                )

            [2] => Array
                (
                    [0] => 3
                    [1] => 1
                )

        )

    [orange] => Array
        (
            [0] => Array
                (
                    [0] => 1
                    [1] => 1
                )

            [1] => Array
                (
                    [0] => 3
                    [1] => 1
                )

            [2] => Array
                (
                    [0] => 5
                    [1] => 1
                )

        )

)
user1978142
  • 7,946
  • 3
  • 17
  • 20
  • Thanks for that - I think I like the other way better, but I'll keep this one in my pocket for some other occasion! – frumious Jun 04 '14 at 00:32