在不同的键上排序多维数组

问题描述:

I have a load of folders and I use DirectoryIterator to get them into a multidimensional array. The outcome of this is something like this

array:10 [▼
  "SomeTitle" => array:2 [▼
    2018 => array:3 [▼
      "February" => array:4 [▶]
      "January" => array:1 [▶]
      "March" => array:1 [▶]
    ]
    2017 => array:11 [▼
      "February" => array:9 [▶]
      "January" => array:12 [▶]
      "March" => array:9 [▶]
      "September" => array:9 [▶]
      "June" => array:8 [▶]
      "December" => array:12 [▶]
      "October" => array:8 [▶]
      "July" => array:10 [▶]
      "April" => array:8 [▶]
      "August" => array:10 [▶]
      "May" => array:10 [▶]
    ]
  ]
]

So I have the main key, followed by year, then month, and then some other data.

What I am trying to do is organise the data by year and month. So 2018 should always be first. I then need the months to be organised in month order. At the moment, I am passing the array to this function

function sortArray($arr) {
    ksort($arr);

    foreach ($arr as $k => $v) {
        if (is_array($v)) {
            $arr[$k] = $this->sortArray($v);
        }
    }
    return $arr;
}

I dont think I need the ksort as this seems to put 2017 first. When I run the above, I get the following

array:10 [▼
  "SomeTitle" => array:2 [▼
    2017 => array:11 [▼
      "April" => array:8 [▶]
      "August" => array:10 [▶]
      "December" => array:12 [▶]
      "February" => array:9 [▶]
      "January" => array:12 [▶]
      "July" => array:10 [▶]
      "June" => array:8 [▶]
      "March" => array:9 [▶]
      "May" => array:10 [▶]
      "October" => array:8 [▶]
      "September" => array:9 [▶]
    ]
    2018 => array:3 [▼
      "February" => array:4 [▶]
      "January" => array:1 [▶]
      "March" => array:1 [▶]
    ]
  ]
]

So everything is basically in alphabetical order. Is there any way to change this so the year starts with newest to oldest, and the months are in calendar order?

Thanks

You can use uksort() to check manually:

  • if the keys is a numeric value: sort naturally
  • if the keys doesn't match with strtotime() sort with strcmp() (It could be better to check if the key is equal to a "predefined" month name instead of checking strtotime() === false.)
  • else sort using strtotime()

Code:

function sortArray($arr) {
    uksort($arr, function($k1, $k2) {
        if (is_numeric($k1)) return $k1-$k2 ;
        if (strtotime($k1) === false) return strcmp($k1, $k2);
        return strtotime($k1) - strtotime($k2);
    });

    foreach ($arr as $k => $v) {
        if (is_array($v)) {
            $arr[$k] = $this->sortArray($v);
        }
    }
    return $arr;
}
$array = sortArray($array);
print_r($array);

Outputs:

Array (
  [SomeTitle] => Array (
    [2017] => Array (
      [January] => Array()
      [February] => Array()
      [March] => Array()
      [April] => Array()
      [May] => Array()
      [June] => Array()
      [July] => Array()
      [August] => Array()
      [September] => Array()
      [October] => Array()
      [December] => Array()
    )
    [2018] => Array (
      [January] => Array()
      [February] => Array()
      [March] => Array()
    )
  )
)

Here is a working demonstration.

Numeric keys corresponding to the month would make more sense, but to do it with the names:

function sortArray($arr) {
    krsort($arr);

    foreach ($arr as $k => &$v) {
        if (is_array($v)) {
            array_multisort(array_map(function($m) {
                                          return date('m', strtotime($m));
                                      }, array_keys($v)), $v);
        }
    }
    return $arr;
}

You want krsort to sort in reverse, then map the array months to get the month number and sort the original on that. Pay attention to &$v.

You could just use this instead of date:

return strtotime($m);