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 id
s associated with respective max
of each rating?
2 Answers 2
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']);
-
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'.wadkar– wadkar2011年08月05日 13:49:46 +00:00Commented 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.wadkar– wadkar2011年08月05日 13:52:53 +00:00Commented 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?)knittl– knittl2011年08月05日 13:55:00 +00:00Commented 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 @knittlwadkar– wadkar2011年08月05日 16:55:28 +00:00Commented Aug 5, 2011 at 16:55
$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)
}
-
I want to compare rating1 of each
$data[]
and not between each$data[][id]
! So there are threemax
and three correspondingid
swadkar– wadkar2011年08月05日 13:18:32 +00:00Commented Aug 5, 2011 at 13:18 -
This finds the row that has the maximum value in either rating1 or rating2Bob Vale– Bob Vale2011年08月05日 13:26:27 +00:00Commented 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?wadkar– wadkar2011年08月05日 13:28:36 +00:00Commented 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 idBob Vale– Bob Vale2011年08月05日 13:34:50 +00:00Commented Aug 5, 2011 at 13:34
-
I have no provided a completely generic solution for any number of rating columnsBob Vale– Bob Vale2011年08月05日 13:51:44 +00:00Commented Aug 5, 2011 at 13:51
Explore related questions
See similar questions with these tags.
array(1349 => array ('rating1'=>....,'rating2'=>....),1350=> array.....