0

here is my array sample $data[][];

Array( [0] => Array ( [id] => 1349
 [rating1] => 1.9378838029981E-7
 [rating2] => 1.1801796607774 )
 [1] => Array ( [id] => 1350
 [rating1] => 5.5499981876923E-7
 [rating2] => 1.5121329727308 )
 [2] => Array ( [id] => 1377
 [rating1] => 0.00023952225410117
 [rating2] => 2.1947077830236 )
 [3] => Array ( [id] => 1378
 [rating1] => 0.00022982302863634
 [rating2] => 2.2135588326622 )
 [4] => Array ( [id] => 1379
 [rating1] => 0.00026272979843585
 [rating2] => 2.2388295595073 )
 [5] => Array ( [id] => 1380
 [rating1] => 0.0002788640872546
 [rating2] => 2.1815325502993 )
)

I want to find max($data[][rating?]) but return $data[id][max_rating?] i.e. id associated with the max value.
Finding the max was easy for one particular, say rating1, I used array_reduce as follows (this is inspired from this SO ):

$max = array_reduce($data, function($a, $b) {
 return $a > $b['rating1'] ? $a : $b['rating1']; 
});

Now I have two questions :
1. How can I extend above array_reduce to include rating2 ? I have other ratingX as well.
2. I do not want the max value, rather the $data[][id] associated with the max.

I am not so much concerned about Q1, but the second one is important as I don't want to search through the array again to get associated $data[][id].
One line of thought is to use array_map instead of array_reduce, but I couldn't come up with a version which will pass on both [id] and [rating?]. Also, there are complications when I try to max() multiple rating? at one shot, as each rating will have different max, which in turn associates with different [id].
EDIT : Just to be clear, I want all the respective ids associated with respective max of each rating?

asked Aug 5, 2011 at 12:49
6
  • this looks like a table from a database. if so, do your filtering at the db-level! Commented Aug 5, 2011 at 13:10
  • no this is NOT, based on the data from table, I calculate rating1,rating2... and add that rating to the array already available from mysql_fetch_array() . Commented Aug 5, 2011 at 13:22
  • you could do the rating calculation in sql too, you know. anyway, have a look at my answer and see if it works for you Commented Aug 5, 2011 at 13:23
  • hmm, lets just say its not possible and leave it at that. I am going through your suggestions. Thanks in advance :) Commented Aug 5, 2011 at 13:31
  • if [id] is unique is there any reason why you are not using the id as the array key instead of having it as a field in the sub array? so array(1349 => array ('rating1'=>....,'rating2'=>....),1350=> array..... Commented Aug 5, 2011 at 13:37

2 Answers 2

1

assuming your array is unsorted you have to loop through it at least once (either manually or using builtin functions). i'd use the following code:

$array = array(
 array( 'id' => 1349, 'sudhi_rating1' => 1.9378838029981E-7, 'sudhi_rating2' => 1.1801796607774 ),
 array( /* ... */ ),
 /* ... */
);
$idx_max = 0;
foreach($array as $idx => $item) {
 if($item['sudhi_rating1'] > $array[$idx_max]['sudhi_rating1'])
 $idx_max = $idx;
}
echo "Rating 1 has max value at id ", htmlspecialchars($array[$idx_max]['id']);

you can extend the code to check multiple ratings at once (make $idx_max an array itself and add more ifs):

$idx_max = array (
 'sudhi_rating1' => 0,
 'sudhi_rating2' => 0,
 /* ... */ );
foreach($array as $idx => $item) {
 foreach($idx_max as $rating => &$max) {
 if($item[$rating] > $array[$max][$rating])
 $max = $idx;
 }
}
foreach($idx_max as $rating => $max)
 echo 'Max value for ', htmlspecialchars($rating), ' has id ', htmlspeciachars($array[$max]['id']);
answered Aug 5, 2011 at 12:57
4
  • the foreach(&$idx_max as $rating => $max) is generating error, if you want to pass $idx_max as reference i think $max should be replaced with &$max , i tried and it works now. Fabulous !! excellent peace of code. Thanks a lot, just one tiny problem, it returns 5,4,4 for rating1,2,3 i.e. not the 'id' but I can easily do $data[5]['id'] to get the value of 'id'. Commented Aug 5, 2011 at 13:49
  • you fixed it, thanks for this excellent answer ! Just to improve the efficiency, what if the $data array is passed by reference too? Its not modified anywhere... I dont like the foreach syntax because of the copy constraints... I never know how man thousand arrays I will have to process. Commented Aug 5, 2011 at 13:52
  • @sudhi: i've already noticed the id issue which was fixed 7 minutes ago. thanks for pointing out the wrong place of the reference operator. as for passing the array itself as reference: try for yourself, it's hard to say beforehand, but i don't think there will be too much of an overhead (copy-on-write?) Commented Aug 5, 2011 at 13:55
  • hmm... copy-on-write, i always forget that, and my instincts tell me not to create copies of things that you are not going to change. PHP is very different in that regard. Anyways I don't have the time and necessary skills to rationalize the difference between passing by reference and passing by copy, so I will keep it simple and follow what you prescribed. Thanks a lot @knittl Commented Aug 5, 2011 at 16:55
1
$max = array_reduce($data, function($a, $b) { 
 if (is_null($a)) return $b;
 return max($a['rating1'],$a['rating2'])>max($b['rating1'],$b['rating2']) ? $a : $b;
});

Result: no Entries $max= NULL otherwise $max['id'] is the id with the max rating

Alternatively this generic code

$max = array_reduce($data, function($a, $b) {
 if (is_null($a)) return $b;
 return maxRating($a)>maxRating($b) ? $a : $b;
 });
function maxRating($row){
 return (max(array_intersect_key($row,array_flip(array_filter(array_keys($row),function ($item) { return strstr($item,'rating')!==FALSE;})))));
}

Will find for all ratings of the form rating?

EDIT -- The code was trying to answer Q1 here is the answer for just Q2

$max = array_reduce($data, function($a, $b) { 
 if (is_null($a)) return $b;
 return $a['rating1']>$b['rating1'] ? $a : $b;
});

EDIT2 -- This is a generic solution for any number of rating? columns

$ratingKeys=array_filter(array_keys($data[0]),function ($item) { return strstr($item,'rating')!==FALSE;});
$max = array_reduce($data,function($a,$b) use (&$ratingKeys) {
 if (is_null($a)) {
 $a=array();
 foreach($ratingKeys as $key) {
 $a[$key]=$b[$key];
 $a[$key.'_id'] = $b['id'];
 }
 return $a;
 }
 foreach($ratingKeys as $key) {
 if ($a[$key]<$b[$key]) {
 $a[$key]=$b[$key];
 $a[$key.'_id']=$b['id'];
 }
 }
 return $a;
});

This code results in

array(4) {
 ["rating1"]=> float(0.0002788640872546)
 ["rating1_id"]=> int(1380)
 ["rating2"]=> float(2.2388295595073)
 ["rating2_id"]=> int(1379)
}

EDIT 3 -- If you change the format of the input array to use id as the array key, you can massively simplify

$max=array_reduce(array_keys($data),function ($a,$b) use (&$data) {
 if (is_null($a)) $a=array();
 foreach(array_keys($data[$b]) as $item) {
 if (!isset($a[$item]) {
 $a[$item] = $b;
 } else {
 if ($data[$a[$item]][$item]) < $data[$b][$item]) $a[$item]=$b;
 }
 return $a;
 }
});

This code results in

array(2) {
 ["rating1"]=> int(1380)
 ["rating2"]=> int(1379)
}
answered Aug 5, 2011 at 13:06
9
  • I want to compare rating1 of each $data[] and not between each $data[][id] ! So there are three max and three corresponding ids Commented Aug 5, 2011 at 13:18
  • This finds the row that has the maximum value in either rating1 or rating2 Commented Aug 5, 2011 at 13:26
  • ohhk, maybe its still not clear to you, i want to find max in rating1 alone. ignore rating2, now how can i get id as well? Commented Aug 5, 2011 at 13:28
  • I have provided rating1 alone, $max will equal the matching row so you can then just use $max['id'] to get the id Commented Aug 5, 2011 at 13:34
  • I have no provided a completely generic solution for any number of rating columns Commented Aug 5, 2011 at 13:51

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.