Sorting a Multi-Dimensional Array with PHP
Adam Scheinberg, January 16, 2009 (16 years ago)
Every so often I find myself with a multidimensional array that I want to sort by a value in a sub-array. I have an array that might look like this:
The problem is thus: I'd like to echo out the songs I like in the format "Songname (Artist)," and I'd like to do it alphabetically by artist. PHP provides many functions for sorting arrays, but none will work here. ksort() will allow me to sort by key, but the keys in the $songs array are irrelevant. asort() allows me to sort and preserves keys, but it will sort $songs by the value of each element, which is also useless, since the value of each is "array()". usort() is another possible candidate and can do multi-dimensional sorting, but it involves building a callback function and is often pretty long-winded. Even the examples in the PHP docs references specific keys.
So I developed a quick function to sort by the value of a key in a sub-array. Please note this version does a case-insensitive sort. See subval_sort() below.
To use it on the above, I would simply type:
This is what you should expect see:
The songs, sorted by artist.
//an array of some songs I like
$songs = array(
'1' => array('artist'=>'The Smashing Pumpkins', 'songname'=>'Soma'),
'2' => array('artist'=>'The Decemberists', 'songname'=>'The Island'),
'3' => array('artist'=>'Fleetwood Mac', 'songname' =>'Second-hand News')
);
The problem is thus: I'd like to echo out the songs I like in the format "Songname (Artist)," and I'd like to do it alphabetically by artist. PHP provides many functions for sorting arrays, but none will work here. ksort() will allow me to sort by key, but the keys in the $songs array are irrelevant. asort() allows me to sort and preserves keys, but it will sort $songs by the value of each element, which is also useless, since the value of each is "array()". usort() is another possible candidate and can do multi-dimensional sorting, but it involves building a callback function and is often pretty long-winded. Even the examples in the PHP docs references specific keys.
So I developed a quick function to sort by the value of a key in a sub-array. Please note this version does a case-insensitive sort. See subval_sort() below.
function subval_sort($a,$subkey) {
foreach($a as $k=>$v) {
$b[$k] = strtolower($v[$subkey]);
}
asort($b);
foreach($b as $key=>$val) {
$c[] = $a[$key];
}
return $c;
}
To use it on the above, I would simply type:
$songs = subval_sort($songs,'artist');
print_r($songs);
This is what you should expect see:
Array
(
[0] => Array
(
[artist] => Fleetwood Mac
[song] => Second-hand News
)
[1] => Array
(
[artist] => The Decemberists
[song] => The Island
)
[2] => Array
(
[artist] => The Smashing Pumpkins
[song] => Cherub Rock
)
)
The songs, sorted by artist.
Your best bet is likely to first divide out into multiple associative arrays (one for artist, one for song, etc...) keyed off of the row number from the main array. After that you can use array_multisort (or whatever the function is named) to get the sorting you want.
<pre>
function subval_sort($a,$subkey) {
foreach($a as $k=>$v) {
$b[$k] = strtolower($v[$subkey]);
}
asort($b);
foreach($b as $k=>$v) {
$c[] = $a[$k];
}
return $c;
}
</pre>
You basically want to sort by a value in a subarray while keeping key value association. There, it's all in that sentence. I don't get why you used the value you wanted to sort by as key and the key as value. This way you won't have problems with duplicate values.
I hope you don't use that coding style and simply did it for brevity. You must be getting hammered with PHP notices about assignment before declaration. ;-P
As for the coding style, what, specifically, do you have a problem with? Dynamic assignment? Not saying $b = array()?
The "two arrays" it creates are destroyed on the fly, so, unless the arrays do contain a very large numbers of entries, I think it ought to safely execute quickly in PHP's memory space, which by default, if I recall, is 8MB per script.
Thanks! =)
--- cheers ---
<pre>
function subval_sort($a,$subkey,$sort) {
foreach($a as $k=>$v) {
$b[$k] = strtolower($v[$subkey]);
}
$sort($b);
foreach($b as $key=>$val) {
$c[] = $a[$key];
}
return $c;
}
$songs = subval_sort($songs,'artist',arsort);
print_r($songs);
</pre>
just a small contribution, my case was to sort a multidimensional array and sort by filename where the names was partly aphanumerical (ie 5-9.pdf, 5-10.pdf, 5-11.pdf and so on)
using the function above the sorting was un-native; 10 comes before 2, so the result would be 4-1.pdf, 4-10.pdf, 4-2pdf.. but replace the asort($b); with natsort($b); (using native sort instead) solved the problem so 2 comes before 10.
thought it might save a few minutes for anyone with similair task.
best regards
Cenneth
<pre>
function subval_sort($a,$subkey) {
foreach($a as $k=>$v) {
$b[$k] = $v[$subkey];
}
asort($b);
foreach($b as $key=>$val) {
$c[$key] = $a[$key];
}
return $c;
} // End of subval_sort
</pre>
it's a clever function
:) great work guys.
I actually needed this for a future Mystique widget :)
any thoughts how to arrange by two subkeys?
as in arrange by artist name, and within artist name have song names arranged as well?
I had to do a sort by two subject keys. The problem with the asort in php is it does not keep order in fields that have the same value. I rewrote a merge sort algorithm I found online to do essentially an asort (keep keys). You can use this to sort one way and then sort again another to essentially get your search by two subkeys. If you want to code shoot me an email at tomjung09@gmail.com
I ran into a conflict in Wordpress between theme and plugin and had to rewrite the query - which eliminated the orderby option.
Adding a custom field to each page to sort it by was where I was headed and your little gem helped me get there.
Many, many, many thanks!
<a href="http://www.exorithm.com/algorithm/view/sort_multi_array" rel="nofollow">http://www.exorithm.com/algorithm/view/sort_multi_array</a>
Nicolas.
<code><pre>function compare($a, $b)
{
return ($a['artist'] > $b['artist']);
}
usort($songs, "compare");</pre></code>
Much better than unnecessary looping & sorting in my opinion.
<code>
function mdsort($array, $sort, $sorttype)
{
$listing=array();
foreach($array as $key => $value)
{
$listing[$key]=$value[$sort];
}
$sorttype($listing);
$keylisting=array();
foreach($listing as $key => $value)
{
$keylisting[]=$key;
}
return $keylisting;
}
$ylist=mdsort($mainlist, name, asort);
</code>
<code>
print_r($modulestats);
Array (
[0] => Array ( [module] => 14 [received] => 908 )
[1] => Array ( [module] => 15 [received] => 897 )
[2] => Array ( [module] => 6 [received] => 994 )
)
$sorted = subval_sort($modulestats, 'module');
print_r($sorted);
Array (
[0] => Array ( [module] => 6 [received] => 994 )
[1] => Array ( [module] => 14 [received] => 908 )
[2] => Array ( [module] => 15 [received] => 897 )
)
</code>
Since I was fed up with it, i thought "let's look again"... and here we are, 6 lines of code... nice!
<code>
function subval_sort( $a, $subkey, $order='asc' ) {
foreach( $a as $k=>$v )
$b[$k] = strtolower( $v[$subkey] );
if( $order === 'dec' )
arsort( $b );
else
asort( $b );
foreach( $b as $key=>$val )
$c[$key] = $a[$key];
return $c;
}
</code>
thank you very much :D
Makes me wonder why PHP doesn't have an internal function to do this, it seems like it would be such a commonly usable thing.
Secondly, I was wondering if it would be possible to take your function a little further...
I was looking to sort an array subkey by the order of another array. I have a user generated playlist, then all the songs are gathered. Then I need the gathered songs to be sorted by their trackName and match the order of the song titles the user entered in the Playlist Array.
I have made a post on Stack Overflow, regarding this possibility but I figured i'd make a post on here as well since you are indeed the original author of this wonderful function.
If you're curious the link to the post on Stack Overflow is located here:
http://stackoverflow.com/questions/6711548/sort-array-subkey-based-on-another-arrays-order
Otherwise, I would love to hear a response from you... :)
Thanks!
-Michael Ecklund
Thanks!
I added a few bits of the code together to allow for variables called the same name and sorting via asc or desc.
function subval_sort( $a, $subkey, $order) {
foreach( $a as $k=>$v )
$b[$k] = strtolower( $v[$subkey] );
if( $order === 'desc' )
arsort( $b );
else
asort( $b );
foreach( $b as $k=>$v )
$c[] = $a[$k];
return $c;
}
$albums = subval_sort($albums,'name', 'asc');
Thanks for sharing.
Ioan
You're so great, your func help me alot.
Thanks again ^^
<pre>
function subval_sort($a,$subkey) {
foreach($a as $k=>$v) {
$b[$k] = strtolower($v->$subkey);
}
asort($b);
foreach($b as $key=>$val) {
$c[] = $a[$key];
}
return $c;
}
</pre>
Thanks a lot its working fine...
I have a multi-dimension array have "date" element
Do you know how to sort by "date"?
function subval_sort($a,$subkey) {
foreach($a as $k=>$v) {
$b[$k] = strtolower($v[$subkey]);
}
asort($b);
foreach($b as $key=>$val) {
$c[] = $a[$key];
}
return $c;
}
$sorted = subval_sort($array,'date');
[/code]
function super_unique($array,$key) // Function for Unique Arrays
{
$temp_array = array();
foreach ($array as &$v) {
if (!isset($temp_array[$v[$key]]))
$temp_array[$v[$key]] =& $v;
}
$array = array_values($temp_array);
return $array;
}
function subval_sort($a,$subkey) { // Function for Alphasorting
foreach($a as $k=>$v) {
$b[$k] = strtolower($v[$subkey]);
}
asort($b);
foreach($b as $key=>$val) {
$c[] = $a[$key];
}
return $c;
}
function cmp($a, $b){
return strcmp($a['artist'], $b['artist']);
}
usort($songs, "cmp");