Page 1 of 1

Sorting Associative Array

Posted: Thu Jun 17, 2010 4:52 pm
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

Re: Sorting Associative Array

Posted: Thu Jun 17, 2010 5:03 pm
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).

Re: Sorting Associative Array

Posted: Thu Jun 17, 2010 7:44 pm
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?

Re: Sorting Associative Array

Posted: Thu Jun 17, 2010 7:54 pm
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.

Re: Sorting Associative Array

Posted: Thu Jun 17, 2010 8:52 pm
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?

Re: Sorting Associative Array

Posted: Fri Jun 18, 2010 10:32 am
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!

Re: Sorting Associative Array

Posted: Fri Jun 18, 2010 11:02 am
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']);

Re: Sorting Associative Array

Posted: Fri Jun 18, 2010 11:22 am
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.

Re: Sorting Associative Array

Posted: Fri Jun 18, 2010 11:36 am
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.

Re: Sorting Associative Array

Posted: Fri Jun 18, 2010 12:38 pm
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 " = "

Re: Sorting Associative Array

Posted: Fri Jun 18, 2010 12:42 pm
by AbraCadaver
Because that's not the original loop from your first post. :dubious:

Re: Sorting Associative Array

Posted: Fri Jun 18, 2010 1:08 pm
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....

Re: Sorting Associative Array

Posted: Fri Jun 18, 2010 2:18 pm
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.

Re: Sorting Associative Array

Posted: Tue Jun 22, 2010 7:17 pm
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>";
  }

Re: Sorting Associative Array

Posted: Tue Jun 22, 2010 7:30 pm
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