1

How might I sort the following using PHP? (where wed_2_open comes after wed_1_close)

I have the following JSON data:

"hours": {
  "mon_1_open": 406800,
  "mon_1_close": 437400,
  "tue_1_open": 493200,
  "tue_1_close": 523800,
  "wed_1_open": 579600,
  "wed_1_close": 590400,
  "thu_1_open": 61200,
  "thu_1_close": 91800,
  "fri_1_open": 147600,
  "fri_1_close": 178200,
  "sat_1_open": 237600,
  "sat_1_close": 264600,
  "sun_1_open": 324000,
  "sun_1_close": 345600,
  "wed_2_open": 597600,
  "wed_2_close": 619200
}

Which I then turn into a usable format using JSON_decode:

$obj=json_decode($json);

This is put into a loop to use convert it to HTML:

foreach ($obj->hours as $key => $val) {
    // Turn array items into HTML list items
}

From previous answers it seems like usort may be an answer, but I get errors telling me $obj is an object rather than an array.

Thanks.

Community
  • 1
  • 1
Joe W
  • 998
  • 6
  • 16
  • 36
  • 1
    `json_decode($json, true)` to force it as an associative array. by default it returns a `stdClass` object – tradyblix Jun 14 '11 at 09:22

3 Answers3

4

Few notes:

  1. To use any of the array functions on the json_decode()ed data, you must pass true as the second parameter which gives you an associative array instead of an object.

  2. Whenever you plan to sort an array, look at this page which helps you decide which of the 12+ array sorting functions you should use.

Since the desired sorting is not intuitive (sorting by key puts friday on top and close before open) you should define a custom sorting function; use uksort() which allows you to do just that on array keys:

<?php
$data = json_decode('{"hours": {
    "mon_1_open": 406800,
    "mon_1_close": 437400,
    "tue_1_open": 493200,
    "tue_1_close": 523800,
    "wed_1_open": 579600,
    "wed_1_close": 590400,
    "thu_1_open": 61200,
    "thu_1_close": 91800,
    "fri_1_open": 147600,
    "fri_1_close": 178200,
    "sat_1_open": 237600,
    "sat_1_close": 264600,
    "sun_1_open": 324000,
    "sun_1_close": 345600,
    "wed_2_open": 597600,
    "wed_2_close": 619200
}}', true);

echo 'BEFORE ================================' . PHP_EOL;
print_r($data);

uksort($data['hours'], 'customsort'); 

echo 'AFTER  ================================' . PHP_EOL;
print_r($data);

function customsort($a, $b) {
    $tr_prefix = array(
        'mon' => 1,
        'tue' => 2,
        'wed' => 3,
        'thu' => 4,
        'fri' => 5,
        'sat' => 6,
        'sun' => 7
    );
    $tr_suffix = array(
        'open'  => 1,
        'close' => 2
    );
    $a = strtr(strtr($a, $tr_prefix), $tr_suffix);
    $b = strtr(strtr($b, $tr_prefix), $tr_suffix);
    return strcmp($a, $b);
}

Note: my customsort implementation shown above is vary naive. Improvise if necessary.

Salman A
  • 262,204
  • 82
  • 430
  • 521
  • Whilst the other posts were helpful, this one best identified the issue, which was the sorting of the days of the week. Combined with jond3k's answer, I have now got this working nicely. – Joe W Jun 15 '11 at 08:36
3
$array = $json_decode($json,true);  //> tradyblix
ksort($array);                      //> Sort by keys
dynamic
  • 46,985
  • 55
  • 154
  • 231
  • Using `TRUE` then breaks my `foreach ($obj->hours as $key => $val)` function as an invalid argument. How do I get around that? I guess using `TRUE` affects the need to use `as $key => $val`? – Joe W Jun 14 '11 at 09:40
  • 1
    @Joe: try `foreach($obj['hours'] as ...)`. – Salman A Jun 14 '11 at 10:29
2

First off, your json is invalid as you're not placing curly brackets around the associative array that contains 'hours'. This might not be an issue if you've cut it out for brevity, but I had to change it when debugging.

More importantly, you can ask json_decode to convert the data in to an associative array instead of an instance of stdClass. ksort will then allow you to sort by keys.

$json = file_get_contents('jsonsort.txt');
$obj = json_decode($json, true);

var_dump($obj);
ksort($obj['hours']);
var_dump($obj);

You could have sorted by values with sort - usort is not needed unless you need to make a custom comparison function, say, comparing instances of objects.

You can then iterate and emit with this (escaping & markup added):

echo "<ol>\n";
foreach($obj['hours'] as $key => $val) {
        echo "\t<li>" . htmlentities($key) . ' => ' . htmlentities($val) . "</li>\n";
}
echo "</ol>\n";
jond3k
  • 948
  • 9
  • 12
  • I haven't tested this yet, but I understand the explanation. I'll give it a try but this looks good to me, thanks. – Joe W Jun 14 '11 at 09:55