I have an array of players each with a number of points.
I can easily sort the array in order of the points using a custom usort function.
But when two players have the same amount of points I need to rank them as the same winning position within the list.
E.g.
Nick - 25
Tom - 18
Chris - 18
Dave - 16
James - 8
In this case the data that I require would be
Nick - 1st
Tom - (joint) 2nd
Chris - (joint) 2nd
Dave 3rd
James 4th
So players with equal scores are designated the same ranking position.
What is the best way of calculating these positions?
Thanks!
This will give you what you want with an additional fix: if you have two players in position 2 the next player should be in position 4. If you don't want this additional fix move $count++; into the if statement.
$count = 0;
$position = 0;
$last_score = -1;
foreach ($players as $player)
{
$count++;
if ($last_score !== $player->score)
{
$position = $count;
}
$player->position = $position;
$last_score = $player->score;
}
I think the following code would do the job as required:
$scores = array('Nick'=>25,'Tom'=>18,'Chris'=>18,'Dave'=>16, 'James'=> 8,);
$scores_numbers = array_unique(array_values($scores));
$scores_counter = array_count_values($scores);
$pos = 1;
foreach($scores_numbers as $num) {
foreach($scores as $name=>$score) {
if($num == $score) {
if($scores_counter[$score] > 1) {
echo "$name - (joint) $pos<br />";
} else {
echo "$name - $pos<br />";
}
}
}
$pos++;
}
I have updated the code to avoid the duplicating output.
If you are retrieving player stats from DB then below code can be useful to rank them :
<?php
$players = [
['name' => 'Ash',
'point' => 0
],
['name' => 'Bob',
'point' => 10
],
['name' => 'Cane',
'point' => 0
],
['name' => 'Dory',
'point' => 6
],
['name' => 'Efat',
'point' => 6
],
];
usort($players, function($v1, $v2){
return $v2['point'] - $v1['point'];
});
$ranking = [];
$currentPosition = 0;
$heighestPoint = PHP_INT_MIN;
foreach($players as $player){
if($heighestPoint !== $player['point']){
$currentPosition++;
$heighestPoint = $player['point'];
}
$ranking[$currentPosition][] = $player;
}
print_r($ranking);
Output :
Array
(
[1] => Array
(
[0] => Array
(
[name] => Bob
[point] => 10
)
)
[2] => Array
(
[0] => Array
(
[name] => Dory
[point] => 6
)
[1] => Array
(
[name] => Efat
[point] => 6
)
)
[3] => Array
(
[0] => Array
(
[name] => Ash
[point] => 0
)
[1] => Array
(
[name] => Cane
[point] => 0
)
)
)
May it helps
/*Below array must be sorted */
$a1=array("Nick"=>25,"Tom"=>18,"Chris"=>18,"Dave"=>16,"James"=>8);
$makeUnique=array_unique($a1);
$makeUnique=array_values($makeUnique);
$pArr=array_map('setPosition',$a1);
print_r($pArr);
function setPosition($a){
Global $makeUnique;
return array_search($a,$makeUnique)+1;
}
OUT PUT
Array ( [Nick] => 1 [Tom] => 2 [Chris] => 2 [Dave] => 3 [James] => 4 )
I used usort to sort assoc array
$players = [["name" => "Nick" , "score" => 25] , ["name" => "Tom" , "score" => 18] , ["name" => "chris" , "score" => 18] , ["name" => "dave" , "score" => 16 ] , ["name" => "james" , "score" => 8]];
usort($players , function($a , $b) {return $b["score"] - $a["score"];} );
var_dump($players);
$lastScore = null;
$pos = 1;
foreach($players as $player)
{
if($lastScore != null && $lastScore == $player["score"])
echo $pos-1 . " - " . $player["name"] . '<br/>';
else
echo $pos++ . " - " . $player["name"] . '<br/>';
$lastScore = $player["score"];
}
checkout this code in teh playground
Related
How to update an array of objects, adding the quantities if you already have the same ID, or if you have not created a new object.
I tried to explain in the code with the arrays and also with the idea of how I would like the result to be.
old Array
$a1 = [
array(
"id" => 1,
"qty" => 1
),
array(
"id" => 2,
"qty" => 1
)
];
$a2 = [
array(
"id" => 1,
"qty" => 1
)
];
$output = array_merge($a1, $a2);
echo '<pre>';
print_r($output);
echo '</pre>';
Result Error:
Array
(
[0] => Array
(
[id] => 1
[qty] => 1
)
[1] => Array
(
[id] => 2
[qty] => 1
)
[2] => Array
(
[id] => 1
[qty] => 1
)
)
What I need, in addition to if the ID does not contain, add.
Array
(
[0] => Array
(
[id] => 1
[qty] => 2
)
[1] => Array
(
[id] => 2
[qty] => 1
)
)
You can take the first array as base, then search for the key (if existing) where the product matches the id. Then either add the quantity and recalculate the price or you just add the reformatted element (id to product conversion).
$result = $a;
foreach($b as $element) {
$matchingProductIndex = array_search($element['id'], array_column($a, 'product'));
if ($matchingProductIndex !== false) {
$pricePerUnit = $result[$matchingProductIndex]['price'] / $result[$matchingProductIndex]['qty'];
$result[$matchingProductIndex]['qty'] += $element['qty'];
$result[$matchingProductIndex]['price'] = $result[$matchingProductIndex]['qty'] * $pricePerUnit;
} else {
$result[] = [
'qty' => $element['qty'],
'product' => $element['id'],
'price' => $element['price'],
];
}
}
print_r($result);
Working example.
Loop through both arrays with foreach and check the ids against each other.
https://paiza.io/projects/lnnl5HeJSFIOz_6KD6HRIw
<?php
$arr1 = [['qty' => 4, 'id' => 4],['qty' => 1,'id' => 30]];
$arr2 = [['id' => 30, 'qty' => 19],['id' => 31, 'qty' => 2]];
$arr3 = [];
foreach($arr1 as $iArr1){
$match = false;
foreach($arr2 as $iArr2){
if($iArr1['id'] === $iArr2['id']){
$arr3[] = ['id' => $iArr1['id'], 'qty' => $iArr1['qty'] + $iArr2['qty']];
$match = true;
}
}
if(!$match){
$arr3[] = $iArr1;
$arr3[] = $iArr2;
}
}
print_r($arr3);
?>
One approach could be one I more often suggested.
First lets merge $a2 with one to simplify looping over one larger collection.
If we then create a small mapping from id to its index in the result array we can update the running total of qty.
$map = [];
$result = [];
// Merge the two and do as per usual, create a mapping
// from id to index and update the qty at the corresponding index.
foreach (array_merge($a1, $a2) as $subarr) {
$id = $subarr['id'];
if (!key_exists($id, $map)) {
$index = array_push($result, $subarr) - 1;
$map[$id] = $index;
continue;
}
$result[$map[$id]]['qty'] += $subarr['qty'];
}
echo '<pre>', print_r($result, true), '</pre>';
Output:
Array
(
[0] => Array
(
[id] => 1
[qty] => 2
)
[1] => Array
(
[id] => 2
[qty] => 1
)
)
I am trying to rearrange my array according to "product_type".
Every 4th position of index "product_type" should be "manufacturer" in the result.
If there are too few "seller" entries then the "manufacturer" entry may be exist in the next available position (and vice versa).
My current input is like this:
$arr1 = array(
0 => ['product_type'=>'manufacturer','id'=>1],
1 => ['product_type'=>'manufacturer','id'=>2],
2 => ['product_type'=>'seller'],
3 => ['product_type'=>'seller'],
4 => ['product_type'=>'seller'],
5 => ['product_type'=>'seller'],
6 => ['product_type'=>'seller'],
7 => ['product_type'=>'manufacturer','id'=>3],
);
My expected new array should be like this:
Array(
[0] => Array
(
[product_type] => seller
)
[1] => Array
(
[product_type] => seller
)
[2] => Array
(
[product_type] => seller
)
[3] => Array
(
[product_type] => manufacturer
[id] => 1
)
[4] => Array
(
[product_type] => seller
)
[5] => Array
(
[product_type] => seller
)
[6] => Array
(
[product_type] => manufacturer
[id] => 2
)
[7] => Array
(
[product_type] => manufacturer
[id] => 3
)
)
For this solution, I am trying this, but here in the result in 1st position I get "manufacturer" product.
$arr1 = array(
0 => ['product_type'=>'manufacturer','id'=>1],
1 => ['product_type'=>'manufacturer','id'=>2],
2 => ['product_type'=>'seller'],
3 => ['product_type'=>'seller'],
4 => ['product_type'=>'seller'],
5 => ['product_type'=>'seller'],
6 => ['product_type'=>'seller'],
7 => ['product_type'=>'manufacturer','id'=>3],
) ;
function rearrange(&$arr, $n)
{
for ($i = 0; $i < $n; $i++)
{
$j = $i+1;
if($arr[$i]['product_type'] =='manufacturer'){
if($j%4!= 0){
//echo $i;
$temp = $arr[$i];
$arr[$i] = $arr[$i + 1];
$arr[$i + 1] = $temp;
} else {
if ($i==0 || $i==1 || $i==2) {
$temp = $arr[$i];
$arr[$i] = $arr[$i + 1];
$arr[$i + 1] = $temp;
} else {
$temp = $arr[$i];
$arr[$i] = $temp;
}
}
} else {
$temp = $arr[$i];
$arr[$i] = $temp;
}
}
return $arr;
}
$keys = array_keys ($arr1);
$n = count ($keys);
$arr = rearrange($arr1, $n);
print_r($arr);
FIDDLE
Store the manufacturer entries at every 4th key as you iterate.
Store the seller entries at every key that is not every 4th key.
Sort on the newly assigned keys.
Re-index the array.
This way your pattern is upheld as long as you have enough qualifying data to do so. When you have too much of one group of data, then it just gets lumped at the end.
Code: (Demo)
$arr1 = array(
0 => ['product_type'=>'manufacturer','id'=>1],
1 => ['product_type'=>'manufacturer','id'=>2],
2 => ['product_type'=>'seller'],
3 => ['product_type'=>'seller'],
4 => ['product_type'=>'seller'],
5 => ['product_type'=>'seller'],
6 => ['product_type'=>'seller'],
7 => ['product_type'=>'manufacturer','id'=>3],
);
$manuCounter = 0;
$sellerCounter = 0;
$result = [];
foreach ($arr1 as $row) {
if ($row['product_type'] === 'manufacturer') {
$manuCounter += 4;
$result[$manuCounter] = $row;
} else {
++$sellerCounter;
if (!($sellerCounter % 4)) {
++$sellerCounter;
}
$result[$sellerCounter] = $row;
}
}
ksort($result);
var_export(array_values($result));
I have conditional function which their execution not running as per business meet which to be require transformation as with desired result. This function contain ranking and frequency which are part of multidimensional array and their execution of sum on based of some calculation
I want to add frequency on base of position of ranking which their execution partially run not accurate. could you please help me out.
<?php
$arr = array (
'AMXB 5321' => array(
array("course_title_code" => "AMB 5321",
"content" => "Course",
"total" => "303",
"count" => "85",
"ranking" => array(array(5),array(2,4,5)),
"frequency" => array(array(5),array(1,2,11))),
array("course_title_code" => "AMB 5321",
"content" => "Succeed in the course",
"total" => "300",
"count" => "85",
"ranking" => array(array(3,5),array(3,4,5)),
"frequency" => array(array(1,4),array(1,2,11))
)
));
array_walk($arr, function(&$v,$k){
foreach($v as $key => &$s){
$s['ranking'] = implode(',',range(1,5));
foreach($s['frequency'] as $key => &$value){
$temp = $value;
$value = (count($value) == 5) ? $value : array_merge(array_fill(0, 5 - count($value), 0), $temp);
if($key == 1){
for($i=0;$i<count($value);$i++){
$value[$i] += $s['frequency'][$key-1][$i];
}
$temFormat = implode(',',$s['frequency'][1]);
}
}
unset($s['frequency']);
$s['frequency'] = $temFormat;
}
});
echo "<pre>";
print_r($arr);
?>
Desired Result
Array
(
[AMXB 5321] => Array
(
[0] => Array
(
[course_title_code] => AMB 5321
[content] => Course
[total] => 303
[count] => 85
[ranking] => 1,2,3,4,5
[frequency] => 0,1,0,2,16
)
[1] => Array
(
[course_title_code] => AMB 5321
[content] => Succeed in the course
[total] => 300
[count] => 85
[ranking] => 1,2,3,4,5
[frequency] => 0,0,2,2,15
)
)
)
You can do that like this:
function calRanking($ranks, $freqs) {
$res = array_fill(0, 5, 0); // create array of 0
foreach($ranks as $k => $v) {
foreach(array_map(null, $v, $freqs[$k]) as $e) {
$res[$e[0]-1]+= $e[1]; //add the frequency according to rank key
}
}
return $res;
}
foreach($a as &$v) { // for each calculate and implode
$v["frequency"] = implode(',', calRanking($v["ranking"], $v["frequency"]));
$v["ranking"] = implode(',',range(1,5));
}
I assumed user with a lot of answer and reputation may build the question in more suite-able way for future learning...
Live example: 3v4l
Reference: array-fill, array-map
I have an array with user id and transaction details sorted based on transaction. Now I need to find the position of key value
Array
(
[0] => Array
(
[userid] => 3
[transaction] => 1878
)
[1] => Array
(
[userid] => 2
[transaction] => 416
)
[2] => Array
(
[userid] => 5
[transaction] => 353
)
[3] => Array
(
[userid] => 4
[transaction] => 183
)
)
When I give user id 4 then it should return 3
First, use array_column() to fetch all userid, then use array_search() to retrieve array key.
$searchId = 4;
echo array_search( $searchId, array_column($array, 'userid') ) );
I might just iterate the outer array here and the check the key values:
$pos = -1;
for ($i = 0; $i < count($array); $i++) {
if ($array[$i]['userid'] == 4) {
$pos = $i;
}
}
if ($pos == -1) {
echo "user not found";
}
else {
echo "user found at position " . $pos;
}
I prefer Calos's answer.
The code below imitates JavaScript's Array.prototype.findIndex to achieve what you need. Using this approach you can have more flexibility in searching the array by using a callback function, similar to how JavaScript has done it.
You can also easily reuse it in other parts of your code.
$data = [
[
'userid' => 3,
'transaction' => 1878
],
[
'userid' => 2,
'transaction' => 416
],
[
'userid' => 5,
'transaction' => 353
],
[
'userid' => 4,
'transaction' => 183
]
];
function findIndex($array, $method){
foreach($array as $index => $value){
if ($method($value, $index, $array) === true){
return $index;
}
}
}
echo findIndex($data, function($arr){
return $arr['userid'] == 5;
});
It's trivial to build your own map:
<?php
$items =
[
'foo' =>
[
'id' => 23,
],
'bar' =>
[
'id' => 47
]
];
foreach($items as $k=>$v)
$ids_keys[$v['id']] = $k;
echo $ids_keys[47];
Output:
bar
I'm processing the final results of competitions and its general report on the best trainer and which place the trainer should get.
I have already prepared associative arrays below. The key represents trainer's id and the value represents the number of medals in a category (gold, silver, bronze) that his/her athletes got.
[gold] => Array
(
[777777] => 4
[333333] => 2
[555555] => 1
[999999] => 1
)
[silver] => Array
(
[999999] => 3
[777777] => 3
[333333] => 2
)
[bronze] => Array
(
[333333] => 6
[777777] => 4
[999999] => 2
)
Next array associates trainer's id with its name:
[trainers] => Array
(
[333333] => Trainer 4
[777777] => Trainer 1
[999999] => Trainer 2
[555555] => Trainer 3
)
I have stuck processing the data above into final results like this. Any ideas on how it could be done elegantly? The problem is that the data is never constant and the size of the array is always different.
Any help would be greatly appreciated.
Here is code sample:
$gold, $silver, $bronze, $trainers are arrays with information you provided.
$out = [];
foreach($trainers as $trainerId=> $trainerName){
$out[] = array(
'id'=>$trainerId,
'name'=>$trainerName,
'gold'=>isset($gold[$trainerId])?$gold[$trainerId]:0,
'silver'=>isset($silver[$trainerId])?$silver[$trainerId]:0,
'bronze'=>isset($bronze[$trainerId])?$bronze[$trainerId]:0,
);
}
uasort($out, function($a, $b){
// Here: sort by your algorithm. Here is example:
if($a['gold'] != $b['gold']){
return $b['gold'] - $a['gold'];
}
if($a['silver'] != $b['silver']){
return $b['silver'] - $a['silver'];
}
return $b['bronze'] - $a['bronze'];
});
$placeId = 1;
foreach($out as &$info){
$info['place'] = $placeId++;
}
unset($info);
foreach($out as $info){
echo "{$info['place']} place goes to - {$info['name']} ({$info['id']}) as he/she got {$info['gold']} gold medals, {$info['silver']} silver and {$info['bronze']} bronze";
}
Here is another way to do it with metrics:
$gold = array
(
'777777' => 4,
'333333' => 2,
'555555' => 1,
'999999' => 1
);
$silver = array
(
'999999' => 3,
'777777' => 3,
'333333' => 2
);
$bronze = array
(
'333333' => 6,
'777777' => 4,
'999999' => 2
);
$trainers = array
(
'333333' => 'Trainer 4',
'777777' => 'Trainer 1',
'999999' => 'Trainer 2',
'555555' => 'Trainer 3'
);
$metrics = [
'gold' => 3,
'silver'=> 2,
'bronze' => 1];
$results = [];
foreach ($metrics as $arrName => $metric)
{
foreach (${$arrName} as $trainerId => $medals)
{
$results[$trainerId] = ( isset($results[$trainerId]) ) ? $results[$trainerId]+$medals * $metric : $medals * $metric;
}
}
// sorting scores (by value)
arsort($results);
// print scores
var_dump($results);
// print final results
$placeOut = '';
foreach ($results as $trainerId => $score) {
$placeOut .= $trainers[$trainerId].": he/she has ";
foreach ($metrics as $medalName => $metric) {
$placeOut .= (${$medalName}[$trainerId] > 0 ? ${$medalName}[$trainerId] : 0)." ".$medalName.", ";
}
$placeOut .= "\n";
}
echo "<pre>".$placeOut."</pre>";
?>