Get Rank of data from array - php

I have an array
$data = array(
'sam' => 40,
'james' => 40,
'sunday' => 39,
'jude' => 45
);
I want to rank the names in the array by value and return their rank order as in the array below:
$final_data = array(
'sam' => 2,
'james' => 2,
'sunday' => 4,
'jude' => 1
);
Since jude has the highest value, they get the value 1. sam and james are tied for second, so they both get 2 (3 is skipped since no one is in third place). sunday has the 4th highest score, so they get 4

You can loop the array in reverse order and count the position/rank like this:
$data = array('sam'=>40, 'james'=>40, 'sunday'=>39, 'jude'=>45);
arsort($data); // sort reverse order
$i = 0;
$prev = "";
$j = 0;
foreach($data as $key => $val){ // loop array
if($val != $prev){ // if values are the same as previous
$i++; // add one
$i += $j; // add counter of same values
$j=0;
}else{
$j++; // this is if two values after eachother are the same (sam & james)
}
$new[$key] = $i; // create new array with name as key and rank as position
$prev = $val; // overwrite previous value
}
var_dump($new);
output:
array(4) {
["jude"] => 1
["sam"] => 2
["james"] => 2
["sunday"] => 4
}
https://3v4l.org/4YvH9
With three values 40 it returns:
array(5) {
["jude"] => 1
["sam"] => 2
["james"] => 2
["tom"] => 2
["sunday"] => 5
}

Related

PHP: Accumulate values in associative array

I would like to accumulate values in an associative array depending on part of the key.
I have tested this foreach loop which works fine in an indexed array:
$b = array();
foreach ($a as $key => $value) {
if ($key > 0) {
$b[$key] = $b[$key - 1] + $value;
}
}
I can't get it to work in an associative array, though...
$a (excerpt)
Array (
[2014-04-22|Paul] => 0
[2014-04-28|Paul] => 2
[2014-05-13|Paul] => 0
[2014-06-03|Paul] => 1
[2014-06-12|Paul] => 0
[2014-08-11|Paul] => 1
[2014-08-28|Paul] => 3
[2012-05-09|John] => 1
[2012-08-29|John] => 2
[2012-09-05|John] => 0
[2012-09-13|John] => 1
)
$b (desired result)
Array (
[2014-04-22|Paul] => 0
[2014-04-28|Paul] => 2
[2014-05-13|Paul] => 2
[2014-06-03|Paul] => 3
[2014-06-12|Paul] => 3
[2014-08-11|Paul] => 4
[2014-08-28|Paul] => 7
[2012-05-09|John] => 1
[2012-08-29|John] => 3
[2012-09-05|John] => 3
[2012-09-13|John] => 4
)
In the desired result each value is of 'Paul' and 'John' (and more) is accumulated to the previous one.
You can do this by keeping track of the name parts of the keys, and resetting the total count when you get to a new name:
$b = array();
$lastname = '';
foreach ($a as $key => $value) {
list($d, $n) = explode('|', $key);
if ($n == $lastname) {
$total += $value;
}
else {
$total = $value;
}
$b[$key] = $total;
$lastname = $n;
}
print_r($b);
Output:
Array (
[2014-04-22|Paul] => 0
[2014-04-28|Paul] => 2
[2014-05-13|Paul] => 2
[2014-06-03|Paul] => 3
[2014-06-12|Paul] => 3
[2014-08-11|Paul] => 4
[2014-08-28|Paul] => 7
[2012-05-09|John] => 1
[2012-08-29|John] => 3
[2012-09-05|John] => 3
[2012-09-13|John] => 4
)
Demo on 3v4l.org
By using array_walk, check if current name is same as next name, as per that I am resetting value by next.
$result = [];
$tempKey = '';
$arr = array_walk($arr, function($item, $key) use(&$result, &$tempKey, &$total){
list($date, $name) = explode("|", $key); // explode key
if(empty($tempKey) || $tempKey != $name){ // if empty or if conflict
$tempKey = $name; // tempKey for reference
$total = $item; // total reset to current value
}else{
$total += $item; // adding for same name
}
$result[$key] = $total; // putting calculated value
});
print_r($result);
Output
Array
(
[2014-04-22|Paul] => 0
[2014-04-28|Paul] => 2
[2014-05-13|Paul] => 2
[2014-06-03|Paul] => 3
[2014-06-12|Paul] => 3
[2014-08-11|Paul] => 4
[2014-08-28|Paul] => 7
[2012-05-09|John] => 1
[2012-08-29|John] => 3
[2012-09-05|John] => 3
[2012-09-13|John] => 4
)
Demo.

Increment values of an array using a for loop and a foreach

I have done a lot of research on google about it but I don't know how to achieve that in PHP. Actually I have an array with values and for each values I would like to get 5 new values incremented of 1.
Exemple :
$valueseg = array( 0 => 2510 , 1 => 1700);
I would like to get this result :
$valueseg = array( 0 => 2510, 1 => 2511, 2 => 2512, 3 => 2513, 4 => 2514 , 5 => 2515, 6 => 1700, , 7 => 1701, , 8 => 1702, , 9 => 1703, 10 => 1704 , 11 => 1705);
I tried to do that with a foreach and a for loop but I can't retrieve an array.
$tabasc = array();
foreach($valueseg as $key => $value){
for($i = 0; $i < 5; $i++){
$tabasc[$i] = $value + $i ;
}
}
With an echo inside the for loop I have a result inline like this : 2478247924802481248225102511251225132514
And if I try to do a var_dump of $tabasc it shows me values incremented of 1 only for the first value of $valueseg.
Do you know how to do that ?
Thanks
Do it like below:-
<?php
$valueseg = array( 0 => 2510 , 1=> 1700);
$final_array = array(); //create a new array
foreach($valueseg as $values){ // iterate over your original array
for($i=1;$i<=5;$i++){ // loop 5 times
$final_array[] = $values+$i; //add incremented $i value to original value and assign to new array
}
}
print_r($final_array); // print new array
Output:- https://eval.in/829546

Ranking/Position on value of array in PHP

I have an array with these values, Array ( [0] => 12 [1] => 17 [2] => 5 [3] => 27 [4] => 5 ) and I need to find out ranking/position of each value
ie,
for 27 - Rank 1
for 17 - Rank 2
for 12 - Rank 3
for 5 - Rank 4
for 5 - Rank 4 (duplicate value in the array)
and these need to be in the same order as the array, so that output should be as follow
12 - Rank 3
17 - Rank 2
5 - Rank 4
27 - Rank 1
5 - Rank 4
This should work (including duplicate values having the same rank):
$values = array();
$values[0] = 5;
$values[1] = 12;
$values[2] = 19;
$values[3] = 9;
$values[4] = 5;
$ordered_values = $values;
rsort($ordered_values);
foreach ($values as $key => $value) {
foreach ($ordered_values as $ordered_key => $ordered_value) {
if ($value === $ordered_value) {
$key = $ordered_key;
break;
}
}
echo $value . '- Rank: ' . ((int) $key + 1) . '<br/>';
}
try this
$array = Array ( "0" => 12 , "1" => 17 , "2" => 5, "3" => 27, "4" => 5 );
$i=1;
foreach($array as $key=>$values)
{
$max = max($array);
echo "\n".$max." rank is ". $i."\n";
$keys = array_search($max, $array);
unset($array[$keys]);
if(sizeof($array) >0)
if(!in_array($max,$array))
$i++;
}
Demo
Just came across this topic while looking for a nice solution for myself. Just drop this here for the future. What I'm using is the following function:
/* Assign rank to each value of the array $in.
* Args:
* in (array): Array containing as set of numeric values.
*
* Returns:
* Returns an array of the same length with ranks. Highest
* values of $in get rank 1, lower values get higher ranks.
* The same values are attributed to the same ranks.
*/
function array_rank( $in ) {
# Keep input array "x" and replace values with rank.
# This preserves the order. Working on a copy called $x
# to set the ranks.
print "input\n";
print_r($in);
$x = $in; arsort($x);
print "sorted\n";
print_r($x);
# Initival values
$rank = 0;
$hiddenrank = 0;
$hold = null;
foreach ( $x as $key=>$val ) {
# Always increade hidden rank
$hiddenrank += 1;
# If current value is lower than previous:
# set new hold, and set rank to hiddenrank.
if ( is_null($hold) || $val < $hold ) {
$rank = $hiddenrank; $hold = $val;
}
# Set rank $rank for $in[$key]
$in[$key] = $rank;
}
print "ranking result\n";
print_r($in);
return $in;
}
$a = array(140,180,180,100);
array_rank( $a );
The print statements (development) return the following:
input
Array
(
[0] => 140
[1] => 180
[2] => 180
[3] => 100
)
sorted
Array
(
[2] => 180
[1] => 180
[0] => 140
[3] => 100
)
ranking result
Array
(
[0] => 3
[1] => 1
[2] => 1
[3] => 4
)
In case you want to have a reverse ranking (lower values are better) simply replace arsort with asort. In my case I am ranking points, the higher the better. The array (140,180,180,100) gets the ranks (3,1,1,4). Players with the same amount of points get the same rank (in this case rank 1) while rank 2 is left out.
You can probably sort the array by the values:
$ranked = sort($arrayValues);
after this, you can do a for loop, looping through the $ranked array, then printing the arrays in order:
for($i = 0; $i < length($ranked); $i++){
for($j = 0; $j < length($arrayValues); $j++){
if($arrayValues[$i] == $sorted[$j]){
echo $arrayValues[$i] . ' has rank ' . $j;
}
}
}
one problem you have is that you want some return value that has 2 times an identical value and key (5=>rank4). So you cannot do an array with ranks as key nor with values as key. In this example i simply made a 2 dimensional array.
<?php
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 1);
// your original input
$original = array(12, 17, 5, 27, 5);
// strip duplicates as they are ranked equally
$rankings = array_unique($original);
// apply some sorting so that the ranks are now given by the keys.
rsort($rankings);
// now just use the origincal array and lookup the rankings for each value
$return = array();
foreach($original as $value)
{
$rankedValue = array();
$rankedValue['value'] = $value;
$rankedValue['rank'] = array_search($value, $rankings) + 1;
$return[] = $rankedValue;
}
var_dump($return);

Intersection of arrays in PHP. Finding matching Pairs

I've got an array like this:
$a = array(
array(2 => 1, 4 => 2, 9 => 3),
array(3 => 7, 4 => 5, 7 => 3),
array(1 => 6, 4 => 5),
...
);
So the array contains a huge amount of sub arrays with integer key => integer value.
Now I want to find subarrays which share no keys or if they share a key the value of this key must be the same.
Example: $a[1] and $a[2] would match because $a[1][4] == $a[2][4] and no other keys match. But $a[0] and $a[1] would not match because $a[0][4] != $a[1][4].
The number of elements in the subarrays may vary.
Is there an efficient way to do this ? The only way I can think of is check each possible pair in a nested loop resulting in O(n^2).
If someone has an idea for a more meaningful title feel free to edit it.
Maybe code makes it more clear: (naive implementation)
$pairs = array();
for($i = 0; $i < count($a); $i++)
for($j = $i+1; $j < count($a); $j++)
if(array_intersect_key($a[$i], $a[$j]) == array_intersect_assoc($a[$i], $a[$j]))
$pairs[] = array($i, $j);
Alternative:
$matching = array();
for($i = 0; $i < count($a); $i++)
for($j = $i+1; $j < count($a); $j++)
if(array_intersect_key($a[$i], $a[$j]) == array_intersect_assoc($a[$i], $a[$j]))
list($matching[$i][], $matching[$j][]) = array($j, $i);
There might be ways to do it, but it somewhat depends on if you know how many matches are likely (or the general 'matchyness' of your data). If there's more matches than not it might be better to start with assuming everything matches and eliminating.
In any case, I think you can pre-process the data. I'm not sure if this is faster -- it really depends on the distribution of your data, but I'd start by trying something like this and work from there:
$a = array(
array(2 => 1, 4 => 2, 9 => 3),
array(3 => 7, 4 => 5, 7 => 3),
array(1 => 6, 4 => 5),
array(1 => 6, 4 => 5, 7 => 5),
array(2 => 1, 4 => 2, 9 => 3)
);
// 1 and 2 match, 2 and 3 match, 0 and 4 match
$keyData = array();
for ($i = 0; $i < count($a); $i++) {
foreach($a[$i] as $k => $v) {
if (!isset($keyData[$k])) {
$keyData[$k] = array();
}
if (!isset($keyData[$k][$v])) {
$keyData[$k][$v] = array();
}
$keyData[$k][$v][] = $i;
}
}
$potentialMatches = array();
foreach ($keyData as $key => $values) {
// Ignore single key/value pairs
if (count($values) > 1) {
foreach ($values as $value => $arrayIndices) {
for ($i = 0; $i < count($arrayIndices); $i ++) {
for ($j = $i + 1; $j < count($arrayIndices); $j ++) {
$potentialMatches[] = array($arrayIndices[$i], $arrayIndices[$j]);
}
}
}
}
}
// You might need to do this ...
/*
foreach ($potentialMatches as &$m) {
array_unique($m);
}
*/
$pairs = array();
foreach ($potentialMatches as $m) {
if(array_intersect_key($a[$m[0]], $a[$m[1]])
== array_intersect_assoc($a[$m[0]], $a[$m[1]])) {
$pairs[] = $m;
}
}
print_r($pairs);
Output:
Array
(
[0] => Array
(
[0] => 0
[1] => 4
)
[1] => Array
(
[0] => 1
[1] => 2
)
[2] => Array
(
[0] => 2
[1] => 3
)
)
EDIT
As I said in my comment, that doesn't catch arrays that don't share any keys -- which you consider a match. The code below does this, although I'm not sure if it's faster than the nested solution (and it's going to use a ton of memory)
// New test data to cover the case I missed
$a = array(
array(2 => 1, 4 => 2, 9 => 3),
array(3 => 7, 4 => 5, 7 => 3),
array(1 => 6, 4 => 5),
array(1 => 6, 4 => 5, 7 => 5),
array(2 => 1, 4 => 2, 9 => 3),
array(8 => 3)
);
// 1 and 2 match, 2 and 3 match, 0 and 4 match, 5 matches all
// First assume everything is a match, build an array of:
// indicies => array of potential matches
$potentialMatches = array_fill(0, count($a), array_keys($a));
// Build data about each key, the indicies that contain that key
// and the indicies for each value of that key
$keyData = array();
for ($i = 0; $i < count($a); $i++) {
foreach($a[$i] as $k => $v) {
if (!isset($keyData[$k])) {
$keyData[$k] = array();
}
if (!isset($keyData[$k][$v])) {
$keyData[$k][$v] = array();
}
$keyData[$k]['all'][] = $i;
$keyData[$k][$v][] = $i;
}
}
// print_r($keyData);
// Now go through the key data and eliminate indicies that
// can't match
foreach ($keyData as $key => $values) {
if (count($values) > 2) { // Ignore single key/value pairs
// Two indecies do not match if they appear in seperate value lists
// First get the list of all indicies that have this key
$all = array_unique($values['all']);
unset($values['all']);
// Now go through the value lists
foreach ($values as $value => $arrayIndices) {
// The indicies for this value cannot match the other
// indices in the system, i.e. this list
$cantMatch = array_diff($all, $arrayIndices);
// So remove the indicies that can't match from the potentials list
foreach ($arrayIndices as $index) {
$potentialMatches[$index] = array_diff($potentialMatches[$index], $cantMatch);
}
}
}
}
//print_r($potentialMatches);
// You said you didn't mind the output format, so that's probably enough
// but that array contains (x,x) which is pointless and both (x,y) and (y,x)
// so we can do one final bit of processing to print it out in a nicer way
$pairs = array();
foreach ($potentialMatches as $x => $matches) {
foreach ($matches as $y) {
if ( ($x < $y) ) {
$pairs[] = array($x, $y);
}
}
}
print_r($pairs);
Output
Array
(
[0] => Array
(
[0] => 0
[1] => 4
)
[1] => Array
(
[0] => 0
[1] => 5
)
[2] => Array
(
[0] => 1
[1] => 2
)
[3] => Array
(
[0] => 1
[1] => 5
)
[4] => Array
(
[0] => 2
[1] => 3
)
[5] => Array
(
[0] => 2
[1] => 5
)
[6] => Array
(
[0] => 3
[1] => 5
)
[7] => Array
(
[0] => 4
[1] => 5
)
)
if you are looking for adjacent matching,
$temp = null;
$last_result = array();
foreach($a as $key => $value){
if(is_null($temp)){
$temp = $value;
} else{
$result = array_intersect_assoc($temp, $value);
if(!empty($result))
array_push($last_result, $result);
$temp = $value;
}
}
print_r($last_result);
otherwise just use array_intersect_assoc
for a example you can do like this
$res = array_intersect_assoc($a[0],$a[1],$a[2]);
print_r($res);

Remove random items from PHP array

I need to reduce the size of this array to X, so i would like to remove X random items. Here's my PHP array:
Array
(
[helping] => 3
[me] => 2
[stay] => 1
[organized!] => 1
[votre] => 4
[passion,] => 1
[action,] => 1
[et] => 2
[impact!] => 1
[being] => 4
)
I tried array_rand() but it didn't keep the keys/values.
array_rand() returns a random key (or more) of the given array, unset using that:
$randomKey = array_rand($array, 1);
unset($array[$randomKey]);
array_rand() returns an array with keys from your original array.
You would need to delete the keys from your original array using a foreach-loop.
Like this:
// Suppose you need to delete 4 items.
$keys = array_rand($array, 4);
// Loop through the generated keys
foreach ($keys as $key) {
unset($array[$key]);
}
$foo = Array(
"helping" => 3,
"me" => 2,
"stay" => 1,
"organized!" => 1,
"votre" => 4,
"passion," => 1,
"action," => 1,
"et" => 2,
"impact!" => 1,
"being" => 4
);
$max = 5; //number of elements you wish to remain
for($i=0;$i<$max;$i++){ //looping through $max iterations, removing an element at random
$rKey = array_rand($foo, 1);
unset($foo[$rKey]);
}
die(print_r($foo));
Use this,
$array = array(); // Your array
$max = 3;
$keys = array_rand($array, count($array) - $max);
// Loop through the generated keys
foreach ($keys as $key) {
unset($array[$key]);
}
If you need to remove an unknown item number, you can do :
$myArr = [
'helping' => 3,
'me' => 2,
'stay' => 1,
'organized!' => 1,
'votre' => 4,
'passion,' => 1,
'action,' => 1,
'et' => 2,
'impact!' => 1,
'being' => 4,
];
$countToRemove = random_int(1, 2); // can return 1 or 2
$keysToRemove = array_rand($myArr, $countToRemove); // returns an int or array
// create the array to loop
foreach (\is_array($keysToRemove) ? $keysToRemove : [$keysToRemove] as $key) {
unset($myArr[$key]);
}
var_dump($myArr); die();
In this case it will randomly remove 1 or 2 items from the array.

Categories