Sorting Associative Array

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
terra
Forum Newbie
Posts: 9
Joined: Sat Feb 07, 2009 1:06 pm

Sorting Associative Array

Post by terra »

the code

Code: Select all

  foreach ($json['users'] as $key=>$value){
    $twitter_screen_name = $value['twitter_screen_name'];
    $score = $value['kscore'];
	  echo "<a href='http://twitter.com/$twitter_screen_name' target='_blank'>$twitter_screen_name</a> = $score<br>";
  } 
works, but i would like to sort the list by $score, from high to low. what would be the best way to accomplish this? i could push $twitter_screen_name and $score into another array and then sort that... but isnt it possible to do it without creating the extra array?

Thanks
User avatar
requinix
Spammer :|
Posts: 6617
Joined: Wed Oct 15, 2008 2:35 am
Location: WA, USA

Re: Sorting Associative Array

Post by requinix »

Try usort or (and I think it'll work in your case) array_multisort. The first requires that you write a function to sort two $json['users'][___] arrays and the second needs you to pass in all the various array bits (users/twitter_screen_name, users/kscore, etc).
terra
Forum Newbie
Posts: 9
Joined: Sat Feb 07, 2009 1:06 pm

Re: Sorting Associative Array

Post by terra »

tasairis wrote:Try usort or (and I think it'll work in your case) array_multisort. The first requires that you write a function to sort two $json['users'][___] arrays and the second needs you to pass in all the various array bits (users/twitter_screen_name, users/kscore, etc).
thank you. would you mind elaborating a bit on that?
User avatar
requinix
Spammer :|
Posts: 6617
Joined: Wed Oct 15, 2008 2:35 am
Location: WA, USA

Re: Sorting Associative Array

Post by requinix »

I've hardly ever used array_multisort, even in times when it probably would have been easier, so with usort you'd have a function like

Code: Select all

function usercmp(array $a, array $b) {
	// sort first by score
	if ($a["kscore"] != $b["kscore"]) return $a["kscore"] - $b["kscore"];

	// otherwise use the screen name
	return strcmp($a["twitter_screen_name"], $b["twitter_screen_name"]);
}
Give $json and the name of that function to usort() and you should be good to go.
terra
Forum Newbie
Posts: 9
Joined: Sat Feb 07, 2009 1:06 pm

Re: Sorting Associative Array

Post by terra »

tasairis wrote:I've hardly ever used array_multisort, even in times when it probably would have been easier, so with usort you'd have a function like

Code: Select all

function usercmp(array $a, array $b) {
	// sort first by score
	if ($a["kscore"] != $b["kscore"]) return $a["kscore"] - $b["kscore"];

	// otherwise use the screen name
	return strcmp($a["twitter_screen_name"], $b["twitter_screen_name"]);
}
Give $json and the name of that function to usort() and you should be good to go.
i've tried many different ways.

Code: Select all

usort($json['users'], "usercmp" );
usort($json, "usercmp" );
i tried array_multisort. i tried usort. i cant get it to work. i get "Warning: usort() [function.usort]: Invalid comparison function in" I need more guidance than that. where do i call usort? before, in or after the loop I put in the OP?
terra
Forum Newbie
Posts: 9
Joined: Sat Feb 07, 2009 1:06 pm

Re: Sorting Associative Array

Post by terra »

the OP might not reveal it, but i'm a begginner.

do i need to push the dimension $json['users'] into a new array or are you telling me to apply usort to directly to $json. if yes, do i apply it to $json or $json['users']?

help please!
User avatar
AbraCadaver
DevNet Master
Posts: 2572
Joined: Mon Feb 24, 2003 10:12 am
Location: The Republic of Texas
Contact:

Re: Sorting Associative Array

Post by AbraCadaver »

You can use usort() if you don't care about the numeric keys being renumbered, or if you do use uasort(). Either way it is much simpler:

Code: Select all

function sort_scores_desc($a, $b) {
	return ($a['kscore'] < $b['kscore']);
}
usort($json['users'], 'sort_scores_desc');
If you want to use multisort it's a little more work:

Code: Select all

foreach ($json['users'] as $key => $value) {
	$twitter_screen_name[$key]  = $value['twitter_screen_name'];
	$kscore[$key] = $value['kscore'];
}
array_multisort($twitter_screen_name, SORT_DESC, $kscore, SORT_DESC, $json['users']);
mysql_function(): WARNING: This extension is deprecated as of PHP 5.5.0, and will be removed in the future. Instead, the MySQLi or PDO_MySQLextension should be used. See also MySQL: choosing an API guide and related FAQ for more information.
terra
Forum Newbie
Posts: 9
Joined: Sat Feb 07, 2009 1:06 pm

Re: Sorting Associative Array

Post by terra »

AbraCadaver wrote:You can use usort() if you don't care about the numeric keys being renumbered, or if you do use uasort(). Either way it is much simpler:

Code: Select all

function sort_scores_desc($a, $b) {
	return ($a['kscore'] < $b['kscore']);
}
usort($json['users'], 'sort_scores_desc');
If you want to use multisort it's a little more work:

Code: Select all

foreach ($json['users'] as $key => $value) {
	$twitter_screen_name[$key]  = $value['twitter_screen_name'];
	$kscore[$key] = $value['kscore'];
}
array_multisort($twitter_screen_name, SORT_DESC, $kscore, SORT_DESC, $json['users']);
i gave up on usort. i dont know when to use it. i tried multisort:

Code: Select all

	foreach ($json['users'] as $key => $value) {
	        $twitter_screen_name[$key]  = $value['twitter_screen_name'];
	        $kscore[$key] = $value['kscore'];
	}
	
	array_multisort($twitter_screen_name, SORT_DESC, $kscore, SORT_DESC, $json['users']);

	foreach ($twitter_screen_name as $key => $value) {
	 echo "$twitter_screen_name[$key] = $kscore[$key]\n";
	}
but i still get an unordered list.
User avatar
AbraCadaver
DevNet Master
Posts: 2572
Joined: Mon Feb 24, 2003 10:12 am
Location: The Republic of Texas
Contact:

Re: Sorting Associative Array

Post by AbraCadaver »

For usort() you would use it right before your original foreach() in the first post. As for multisort, your second foreach() is wrong, use the original from your first post.
mysql_function(): WARNING: This extension is deprecated as of PHP 5.5.0, and will be removed in the future. Instead, the MySQLi or PDO_MySQLextension should be used. See also MySQL: choosing an API guide and related FAQ for more information.
terra
Forum Newbie
Posts: 9
Joined: Sat Feb 07, 2009 1:06 pm

Re: Sorting Associative Array

Post by terra »

AbraCadaver wrote:For usort() you would use it right before your original foreach() in the first post. As for multisort, your second foreach() is wrong, use the original from your first post.

Code: Select all

	foreach ($json['users'] as $key => $value) {
	        $twitter_screen_name[$key]  = $value['twitter_screen_name'];
	        $kscore[$key] = $value['kscore'];
	}
	
	array_multisort($twitter_screen_name, SORT_DESC, $kscore, SORT_DESC, $json['users']);

  foreach ($json['users'] as $key=>$value){
    $twitter_screen_name = $value['twitter_screen_name'];
    $kscore = $value['kscore'];
    echo "$twitter_screen_name[$key] = $kscore[$key]\n";
  }
doesnt work. it returns a single letter followed by " = "
User avatar
AbraCadaver
DevNet Master
Posts: 2572
Joined: Mon Feb 24, 2003 10:12 am
Location: The Republic of Texas
Contact:

Re: Sorting Associative Array

Post by AbraCadaver »

Because that's not the original loop from your first post. :dubious:
mysql_function(): WARNING: This extension is deprecated as of PHP 5.5.0, and will be removed in the future. Instead, the MySQLi or PDO_MySQLextension should be used. See also MySQL: choosing an API guide and related FAQ for more information.
terra
Forum Newbie
Posts: 9
Joined: Sat Feb 07, 2009 1:06 pm

Re: Sorting Associative Array

Post by terra »

AbraCadaver wrote:Because that's not the original loop from your first post. :dubious:
duh! thats right. THANKS AbraCadaver!

PS: i also had to correct

Code: Select all

array_multisort($kscore, SORT_DESC, $json['users']);
because i wanted to sort only on score.

now, one last thing. i couldnt use neither of

Code: Select all

array_unique($json['users']);
array_unique($json);
maybe i am just too worried about efficient code for a beginner....
Last edited by terra on Tue Jun 22, 2010 7:21 pm, edited 1 time in total.
User avatar
McInfo
DevNet Resident
Posts: 1532
Joined: Wed Apr 01, 2009 1:31 pm

Re: Sorting Associative Array

Post by McInfo »

I'll throw my two cents in. This solution uses usort() and allows for sorting on any number of keys in the sub-arrays.

Code: Select all

<?php
header('Content-Type: text/plain');

// Sample array;
// Scores are strings here, but integer scores would work too
$json['users'] = array (
    array (
        'twitter_screen_name' => 'abc',
        'kscore' => '789'
    ),
    array (
        'twitter_screen_name' => 'ghi',
        'kscore' => '123'
    ),
    array (
        'twitter_screen_name' => 'def',
        'kscore' => '1234'
    ),
    array (
        'twitter_screen_name' => 'jkl',
        'kscore' => '123'
    ),
    array (
        'twitter_screen_name' => 'mno',
        'kscore' => '456'
    ),
    array (
        'twitter_screen_name' => 'abc',
        'kscore' => '789'
    )
);

// Compares user arrays $a and $b based on multi-column sorting rules;
// Returns -1 if $a should be placed before $b
//       or 1 if $a should be placed after $b
function compareUsers ($a, $b) {
    // A set of rules to judge how $a and $b compare;
    // $rules doesn't change, so it's static (shared among function calls);
    // The first part of a rule is an array key to match in $a and $b;
    // The second part of a rule is 1 to sort ascending, -1 descending
    static $rules = array (
        array ('kscore', -1),
        array ('twitter_screen_name', 1)
    );
    // Always starts with the first rule (resets internal array pointer)
    // or skips this block if there are no rules
    if (reset($rules)) {
        // Tries each rule until $a and $b are found to be unequal
        // or the rules are exhausted
        do {
            // Pulls parts of current rule into $key and $order
            list ($key, $order) = current($rules);
            // If $a and $b are unequal based on current rule...
            if ($a[$key] != $b[$key]) {
                // Breaks out of the function,
                // returning the inverse of $order if $a < $b
                // or $order if $a > $b
                return (($a[$key] < $b[$key]) ? -$order : $order);
            }
            // If $a and $b are equal based on current rule, tries next rule
            // or breaks out of loop if there are no more rules
        } while (next($rules));
    }
    // Rules are exhausted;
    // $a and $b must be equivalent;
    // Arbitrarily returns -1
    return -1;
}

// Sorts users array based on the comparison function
usort($json['users'], 'compareUsers');

// Displays the sorted array
print_r($json);
terra wrote:maybe i am just too worried about efficient code for a beginner....
Worry about writing correct code. You can refactor and optimize later.
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Re: Sorting Associative Array

Post by Benjamin »

terra wrote:the code

Code: Select all

  foreach ($json['users'] as $key=>$value){
    $twitter_screen_name = $value['twitter_screen_name'];
    $score = $value['kscore'];
	  echo "<a href='http://twitter.com/$twitter_screen_name' target='_blank'>$twitter_screen_name</a> = $score<br>";
  } 
works, but i would like to sort the list by $score, from high to low. what would be the best way to accomplish this? i could push $twitter_screen_name and $score into another array and then sort that... but isnt it possible to do it without creating the extra array?

Thanks
Untested:

Code: Select all

function mdarray_sort_asc($items, $key){
    $compare = create_function('$a,$b','
        if ($a["'.$key.'"] == $b["'.$key.'"]) {
            return 0;
        } else {
            return ($a["'.$key.'"] > $b["'.$key.'"]) ? 1 : -1;
        }
    ');
    usort($items,$compare);
    return $items;
}

$json['users'] = mdarray_sort_asc($json['users'], 'kscore');

 foreach ($json['users'] as $key=>$value){
    $twitter_screen_name = $value['twitter_screen_name'];
    $score = $value['kscore'];
	  echo "<a href='http://twitter.com/$twitter_screen_name' target='_blank'>$twitter_screen_name</a> = $score<br>";
  }
terra
Forum Newbie
Posts: 9
Joined: Sat Feb 07, 2009 1:06 pm

Re: Sorting Associative Array

Post by terra »

Thanks Benjamin and McInfo. AbaCadaver's solution works.

The only remaining issue was array_unique, but I confirmed that array_unique doesn't work with multi-dimensional arrays - there is a bug in array_unique. more info at:

http://www.phpdevblog.net/2009/01/using ... rrays.html
Post Reply