I want to rank the keys of an associative array in php based upon their values. (top to down as 1, 2, 3....). Keys having same value will have same rank.
Here function getRanks() is meant to return an array containing keys and the ranks (number).
I expect it to return like this (this is sorted value wise in descending)
Array
(
[b] => 1
[a] => 2
[d] => 3
[c] => 3
[e] => 4
)
There is issue in assigning the ranks (values) in the $ranks array which is to be returned.
What am I doing wrong? Do these loops even do something?
Code:
$test = array('a'=> 50, 'b'=>60, 'c'=>20, 'd'=>20, 'e'=>10);
$json = json_encode($test);
print_r(getRanks($json));
function getRanks($json) {
$tmp_arr = json_decode($json, TRUE);
$ranks = array();
uasort($tmp_arr, function($a, $b){
return $a == $b ? 0 : $a > $b ? -1 : 1; //descending
});
$keys = array_keys($tmp_arr); //after sorting
$ranks = array_fill_keys($keys, 0); //copy keys
$ranks[$keys[0]] = 1; //first val => rank 1
//------- WORKS FINE UNTIL HERE ------------------
// need to fix the ranks assignment
for($i=1; $i<count($keys)-1; $i++) {
for($j=$i; $j < count($keys)-1; $j++) {
if($tmp_arr[$keys[$j]] == $tmp_arr[$keys[$j+1]]) {
$rank[$keys[$j]] = $i;
}
}
}
return $ranks;
}
Your approach seems unnecessarily complicated. In my version I kept the json-related copying part of it but finished it off in a simpler way:
function getRanks($json) {
$tmp_arr = json_decode($json, TRUE);
asort($tmp_arr);. // sort ascending
$i=0; $lv=null;$ranks = array();
foreach ($tmp_arr as $k=>$v) {
if ($v>$lv){ $i++; $lv=$v;}
$ranks[$k]=$i;
}
return $ranks;
}
See the demo here: https://rextester.com/LTOA23372
In a slightly modified version you you can also do the ranking in a descending order, see here: https://rextester.com/HESQP10053
I've also tried with another approach.
I think it may not be the good solution because of high memory & CPU time consumption.
For small arrays (in my case) it works fine.
(I've posted because it may be an answer)
It creates array of unique values and fetches ranks accordingly.
$test = array('a'=> 50, 'b'=>60, 'c'=>20, 'd'=>20, 'e'=>10);
$json = json_encode($test);
print_r(getRanks($json));
function getRanks($json) {
$tmp_arr = json_decode($json, TRUE);
arsort($tmp_arr);
$uniq_vals = array_values(array_unique($tmp_arr)); // unique values indexed numerically from 0
foreach ($tmp_arr as $k => $v) {
$tmp_arr[$k] = array_search($v, $uniq_vals) + 1; //as rank will start with 1
}
return $tmp_arr;
}
This is the simple thing that you can do it by using php array function check example given below.
<?php
$fruits = array("d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple");
asort($fruits);
foreach ($fruits as $key => $val) {
echo "$key = $val\n";
}
?>
//Returns 10 question from questions table
$result = mysqli_query($con,"SELECT question FROM questions ORDER BY rand() LIMIT 10' ");
while($row = mysqli_fetch_row($result))
{
$que[]=$row[0];
}
Now I need to store this whole set of $que[] in a session variable. (i.e 10 questions)
Something like this
$_SESSION['question'] = $que[];
$my_array[] = $_SESSION['question'];
so that $my_array[0] returns first question, $my_array[1] returns second questions and like that.
(Thanx for the help in advance)
Assign
$_SESSION['question'] = $que;
print_r($_SESSION['question'][0]); will give you first question.
You are almost correct, you only need the [] when adding to the array.
$_SESSION['question'] = $que;
Make sure that you have a session going first, placing this at the top of your script will start a session if one doesn't already exist:
if( !isset( $_SESSION ) ) {
session_start();
}
To pull it back up:
$array = $_SESSION['question']; //Assigns session var to $array
print_r($array); //Prints array - Cannot use echo with arrays
Final Addition
To iterate over the array, you can typically use for, or foreach. For statements really only work well when your array keys are incremental (0, 1, 2, 3, etc) without any gaps.
for( $x = 0, $max = count($array); $x < $max; ++$x ) {
echo $array[$x];
}
foreach( $array as &$value ) {
echo $value;
}
Both have been written in mind for performance. Very important to know that when using a reference (&$value, notice the &) that if you edit the reference, the original value changes. When you do not use by reference, it creates a copy of the value. So for example:
//Sample Array
$array = array( '0' => 5, '1' => 10 );
//By Reference
foreach( $array as &$value ) {
$value += 2; //Add 2 to each value
echo $value; //Echos 7 and 12, respectively
}
print_r( $array ); //Now equals array( '0' => 7, '1' => 12 )
//Normal Method
foreach( $array as $value ) {
$value += 2; //Add 2 to each value
echo $value; //Echos 7 and 12, respectively
}
print_r( $array ); //Still equals array( '0' => 5, '1' => 10 )
References are faster, but not if you are planing on modifying the values while keeping the original array intact.
use
session_start();
$_SESSION['question'] = $que;
&que = array(an array of your 10m question s);
when your want to call it on another page to get a line up of your questions, use
while (list($key, $value) = each($_SESSION)) {
#Echo the questions using $key
echo "Here is a list of your questions";
echo "<br/>";
while (list($key2, $value2) = each($_SESSION)) {
#$value2 show's name for the indicated ID
#$key2 refers to the ID
echo "<br/>";
echo "Question: ".$value2." ";
echo "<br/>";
}
echo "<br/>";
}
OR you can also use
print_r;
I have an array:
$names = [
"Ayush" , "Vaibhav", "Shivam",
"Hacker", "Topper", "ABCD",
"NameR", "Tammi", "Colgate",
"Britney", "Bra", "Kisser"
];
And I have another variable
$addthis = "ADDTHIS";
How to make an array from these two so that after every three items in $names, the value of $addthis is added. So, I want this array as result from these two.
$result = [
"Ayush", "Vaibhav", "Shivam", "ADDTHIS",
"Hacker", "Topper", "ABCD", "ADDTHIS",
"NameR", "Tammi", "Colgate", "ADDTHIS",
"Britney", "Bra", "Kisser"
];
"Oneliner", just for fun:
$new = array_reduce(
array_map(
function($i) use($addthis) { return count($i) == 3 ? array_merge($i, array($addthis)) : $i; },
array_chunk($names, 3)
),
function($r, $i) { return array_merge($r, $i); },
array()
);
Maybe an easier to understand solution:
// the parameters
$names = array( "Ayush" , "Vaibhav", "Shivam", "Hacker", "Topper",
"ABCD", "NameR", "Tammi", "Colgate", "Britney",
"Bra", "Kisser");
$addThis = 'ADDTHIS';
$every = 3;
// how often we need to add this
$count = ceil(count($names) / $every) - 1;
for ($i = 0; $i < $count; $i++) {
array_splice($names, $i * ($every + 1) + $every, 0, $addThis);
}
array_splice is exactly for modifying arrays (removing or adding items), preserves existing keys and is not doing any other operation on the array.
While other answers are for sure valid as well this should be the fastest and cleanest solution.
Another oneliner ;)
$result = call_user_func_array(
'array_merge', array_map(function($v) use ($addthis) {
return $v + [4 => $addthis];
}, array_chunk($names, 3))
); array_pop($result);
Demo
I have the ANSWER for anyone curious, I've just done a function that randomizes an array.
function randomize($array){
$randomized=array(); //start a new array
foreach ($array as $key=>$val){ ///loop thru the array we randomize
if($key % 2 == 0){ ////if the key is even
array_push($randomized,$val);///push to back
}else{ ////if the key is odd
array_unshift($randomized,$val); ///push to front
}
}
return $randomized;
}///randomize
Basically what we are doing is creating a new randomized array, looping through the array we pass, tracking an interator and as we loop thru if the key is even we send that array value to the back, if its odd we send that to the front.
I concur with #iRaS's answer that it will be more direct/efficient to make iterated element insertions in a loop. I interpret the question as requiring an element to be inserted after every 10 elements -- in other words, if the array has exactly 20 elements, then 2 elements should be inserted. One after the 10nth element, then one after the 20th element.
By iterating from the back of the array, you can avoid keeping track of previously injected elements (which effectively push out the desired position of subsequently injected elements). I didn't use this approach while crafting a dynamic approach for a similar question.
Use a modulus-based calculation to determine the last insertion position, then decrement the position variable by the $every variable.
To test the accuracy of my snippet, just add and remove elements in the input array.
Code: (Demo)
$names = [
"Ayush" , "Vaibhav", "Shivam",
"Hacker", "Topper", "ABCD",
"NameR", "Tammi", "Colgate",
"Britney"//, "Bra"//, "Kisser"
];
$addThis = 'ADDTHIS';
$every = 3;
for (
$count = count($names), $pos = $count - ($count % $every);
$pos > 0;
$pos -= $every
) {
array_splice($names, $pos, 0, $addThis);
}
var_export($names);
Output:
array (
0 => 'Ayush',
1 => 'Vaibhav',
2 => 'Shivam',
3 => 'ADDTHIS',
4 => 'Hacker',
5 => 'Topper',
6 => 'ABCD',
7 => 'ADDTHIS',
8 => 'NameR',
9 => 'Tammi',
10 => 'Colgate',
11 => 'ADDTHIS',
12 => 'Britney',
)
P.S. If you don't want to add the extra tail element (the array shouldn't end with an "ADDTHIS" element), then use this code as the first parameter of the for():
$count = count($names), $pos = $count - (($count % $every) ?: $every);
$result = array();
$cnt = 0;
foreach ($names AS $val) {
$result[] = $val;
if ($cnt >=3) {
$result[] = $addthis;
$cnt = 0;
}
$cnt++;
}
Loop through and use modulo for checking for 3. element:
After that use splice to insert an element between two element
foreach($result as $k=>$value){
if(($k+1)%3==0){
array_splice($arrayvariable, $k+1, 0, "ADDTHIS");
}
}
One thing you do not want to do is assume that every key in your array is numeric and that it accurately represents the offset of each element. This is wrong, because PHP arrays are not like traditional arrays. The array key is not the offset of the element (i.e. it does not determine the order of elements) and it does not have to be a number.
Unfortunately, PHP arrays are ordered hashmaps, not traditional arrays, so the only way to insert a new element in the middle of the map is to create a brand new map.
You can do this by using PHP's array_chunk() function, which will create a new array of elements, each containing up to a designated number of elements, form your input array. Thus we create an array of arrays or chunks of elements. This way you can iterate over the chunks and append them to a new array, getting your desired effect.
$names = array( "Ayush" , "Vaibhav", "Shivam", "Hacker", "Topper",
"ABCD", "NameR", "Tammi", "Colgate", "Britney",
"Bra", "Kisser");
$addthis = "ADDTHIS";
$result = array();
foreach (array_chunk($names, 3) as $chunk) { // iterate over each chunk
foreach ($chunk as $element) {
$result[] = $element;
}
// Now push your extra element at the end of the 3 elements' set
$result[] = $addthis;
}
If you wanted to preserve keys as well you can do this....
$names = array( "Ayush" , "Vaibhav", "Shivam", "Hacker", "Topper",
"ABCD", "NameR", "Tammi", "Colgate", "Britney",
"Bra", "Kisser");
$addthis = "ADDTHIS";
$result = array();
foreach (array_chunk($names, 3, true) as $chunk) { // iterate over each chunk
foreach ($chunk as $key => $element) {
$result[$key] = $element;
}
// Now push your extra element at the end of the 3 elements' set
$result[] = $addthis;
}
This perserves both order of the elements as well as keys of each element. However, if you don't care about the keys you can simply use the first example. Just be careful that numeric keys in order will cause you a problem with the second approach since the appended element in this example is assuming the next available numeric key (thus overwritten on the next iteration).
I would like to know how to get the sum of some key values of multi-dimensional arrays, knowing that some keys are variables; here is an example of the situation :
The array could be written like this :
$array[$dim1][$dim2][$dim3][$dim4] = $variable_value;
$dim1, 2, 3 and 4 are arrays with dimensions, and we don't know the name of the $dim1, 2, 3 and 4.
We want the sum of all $variable_value of each dimensions, but we can't do array_sum($array[$dim1][$dim2][$dim3][$dim4]) because the $dim are not known.
The algorithm I need to find must permit me to apply filters on the sums, like "get the sum of all the $variable_value where $dim3 = $variableX...", so a function like this :
function array_sum_filter($array, $dimension, [$filter_on_key_value])
Any ideas?
use for/foreach loops for looping through the multidimensional array and an if statement to check if $dim3 = $variableX...
You can try this using RecursiveArrayIterator and RecursiveIteratorIterator
$sum = 0;
$specific = 0;
$array = array();
$array["A"]["B"]["C"]["D"] = 5;
$array["A"]["B"]["K"]["D"] = 3;
$array["A"]["C"] = 2;
$array_obj = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
foreach ( $array_obj as $key => $value ) {
$sum += $value;
if ($key == "D")
$specific += $value;
}
echo $sum, PHP_EOL;
echo $specific, PHP_EOL;
Output
10
8
I have an array of temperature data by hour. Some hours have zero data instead of a temp. When graphing using Google Charts, the zero causes the line graph to plummet. My temporary fix was to replace the zero values with null, causing a break in the line graph. The ideal solution would be to take the values on either side of the zero, and average them. The array is in order by hour. Help?
$array = array(
"1AM" => "65",
"2AM" => "66",
"3AM" => "68",
"4AM" => "68",
"5AM" => "68",
"6AM" => "0",
"7AM" => "70",
"8AM" => "71",
"9AM" => "71",
"10AM" => "73",
);
Here's my script replacing the 0's with nulls:
$array = array ();
foreach($parsed_json->history->observations as $key => $value) {
$temp = (int)$value->tempi;
if ($temp==0) {
str_replace(0, null, $temp);
}
$hour = $value->date->hour;
$array[$hour] = $temp;
};
This Example would work great if the data was mine, but alas, it's from a JSON feed.
Would I use an array_walk() sort of deal? How would I reference the current place in the array? Any help is appreciated!
I would scratch out the null portion, and just foreach-loop through the final array.
So, change your current code to:
$array = array ();
foreach($parsed_json->history->observations as $key => $value) {
$temp = (int)$value->tempi;
}
$hour = $value->date->hour;
$array[$hour] = $temp;
And add this below it:
foreach($array as $hour => $temp){
if($temp == "0"){
$numHour = $hour[0];
$hourPlus = ($numHour + 1) . "AM";
$hourMinus = ($numHour - 1) . "AM";
$valuePlus = $array[$hourPlus];
$valueMinus = $array[$hourMinus];
$average = ($valuePlus + $valueMinus)/2;
$array[$hour] = $average;
}
}
?>
This of course assumes that the values on either side of the zero are also not zero. You may want to add a check for that somewhere in there.
Tested and proven method.
Couldn't you do something along the lines of:
str_replace(0, ((($key-1)+($key+1))/2), $temp);
Where $key is array position, take the value before 0 and after 0 add them and divide them by 2 to get the average.
I'll let you sort out what happens if first, last or consecutive values are 0.
$the_keys=array_keys($array);
foreach($the_key as $index=>$key)
{
if($array[$key]==0)
{
$array[$key]=($array[$the_key[$index-1]]+$array[$the_key[$index+1]]/2);
}
}