Numerically sorting arrays output [duplicate] - php

This question already has answers here:
How do I Sort a Multidimensional Array in PHP [duplicate]
(10 answers)
Closed 8 years ago.
I'm trying to sort the output value that's returned by greatest to least.
Here's an example of the array:
array(
'ACTION' => 'getsupportdepartments',
'RESULT' => 'success',
'TOTALRESULTS' => 2,
'DEPARTMENTS' => array(
'DEPARTMENT' => array(
'ID' => 2,
'NAME' => 'Sales',
'AWAITINGREPLY' => 5,
'OPENTICKETS' => 5
),
'DEPARTMENT1' => array(
'ID' => 1,
'NAME' => 'Support',
'AWAITINGREPLY' => 15,
'OPENTICKETS' => 15
The code I'm using is:
if(!empty($_GET['sort'])) {
$tmpArray = array();
foreach($arr['WHMCSAPI']['DEPARTMENTS'] as $key => $value) {
$tmpArray[$value['AWAITINGREPLY']] = $value;
}
$arr['WHMCSAPI']['DEPARTMENTS'] = $tmpArray;
($_GET['sort'] == 'desc') ? krsort($arr['WHMCSAPI']['DEPARTMENTS']) : ksort($arr['WHMCSAPI']['DEPARTMENTS']);
}
($_GET['sort'] == 'desc') ? krsort($arr['WHMCSAPI']['DEPARTMENTS']) : ksort($arr['WHMCSAPI']['DEPARTMENTS']);
foreach($arr['WHMCSAPI']['DEPARTMENTS'] as $department) {
echo $department['NAME'].' - '.$department['AWAITINGREPLY'].'<br />';
}
echo $exc;
However the order of the output from AWAITINGREPLY is not sorting.

No need to reinvent the wheel, there's already a function called usort:
online demo
function sortDepts(array &$arr,$sort="desc")
{
if($sort=="desc")
usort($arr["DEPARTMENTS"],function($a,$b){
return $b["AWAITINGREPLY"]-$a["AWAITINGREPLY"];
});
else
usort($arr["DEPARTMENTS"],function($a,$b){
return $a["AWAITINGREPLY"]-$b["AWAITINGREPLY"];
});
return $arr;
}
Note that anonymous function works in PHP>=5.3. If you need to support 5<=PHP<5.3, you'll need to declare the function first.
Edit:
For 5 < PHP < 5.3:
online demo
function asc($a,$b)
{
return $a["AWAITINGREPLY"]-$b["AWAITINGREPLY"];
}
function desc($a,$b)
{
return $b["AWAITINGREPLY"]-$a["AWAITINGREPLY"];
}
function sortDepts(array &$arr,$sort="desc")
{
if($sort=="desc")
usort($arr["DEPARTMENTS"],"desc");
else
usort($arr["DEPARTMENTS"],"asc");
return $arr;
}
Edit #2:
In case you still need the key (DEPARTMENT,DEPARTMENT1,etc.), use uasort instead of usort (no other code change is required).

Related

Fastest return TRUE if all values of a column of a multidimensional array is_numeric

Are there faster methods to check the existence of a number (not null) in one
column of a multidimensional array in php (for instance, number9)?
Attempt:
The if statement below seems to be working okay.
$arr=Array(
[0] => Array
(
[date] => 2019-01-16
[number1] => 20.4
[number2] => 20.54
[number3] => 19.71
[number4] => 19.73
[number5] => 70849266
[number6] => 70849266
[number7] => -0.65
[number8] => -3.189
[number9] => 20.0902
[string1] => Jan 16
[number10] => 0.047796070100903
)
.
.
.
[21] => Array
(
[date] => 2019-01-17
[number1 => 19.49
[number2] => 20.51
[number3] => 19.02
[number4] => 20.25
[number5] => 85018421
[number6] => 85018421
[number7] => 0.52
[number8] => 2.636
[number9] => 19.7988
[string1] => Jan 17
[number10] => 0.075411577270313
)
);
function isNumeric($var){
if (is_numeric($var)){
return true;
} else {
return false;
}
}
if((array_walk(array_column($arr, "number8"), 'isNumeric'))!=1)
Here are my ideas.
First is to just filter the array for numeric only values and compare to the original:
function with_array_filter($arr) {
return $arr == array_filter($arr, 'is_numeric');
}
The second example uses casting to a float and then back to string, keep in mind that this is not going to be accurate for very big numbers, however seems to be the fastest (if you care about that at all):
function is_numeric_array_with_cast($arr) {
foreach ($arr as $b) {
if ($b != (string)(float)$b) {
return false;
}
}
return true;
}
However probably the simplest solution is to just foreach the array inside a function and return early:
function is_numeric_array_with_func($arr) {
foreach ($arr as $b) {
if (!is_numeric($b)) {
return false;
}
}
return true;
}
Benchmarked with an array of 20 elements over 100000 iterations on PHP 7.2:
$falseArray = [
'1',
2.5,
-10,
32,
11.0,
2.5,
100101221,
345,
-10,
'-10',
'+10',
'10',
12,
PHP_INT_MAX,
PHP_INT_MAX + 1.4e5,
'-10',
null,
'a',
'5',
5
];
Matt Fryer
Time: 4.8473789691925
is_numeric_array_with_func
Time: 4.0416791439056
is_numeric_array_with_cast
Time: 3.2953300476074
with_array_filter
Time: 3.99729180336
AS I said in the comments:
The if statement below seems to be working okay
However, given the code you posed I doubt that: lets look at it.
function isNumeric($var){ ... }
if(array_walk(array_column($arr, "number8"), 'isNumberic'))!=1
The first and most obvious things are these 2
isNumberic vs isNumeric, which is a fatal undefined function error (spelling/typo).
)!=1 then this is outside of the actual condition, or put another way if(...){ !=1 }
Let's assume those are just typos in the question. Even if your code was free of the 2 "defects" I mentioned above you would still have this problem, array_walk works by reference and simply returns true (always). Pass by reference updates the "Original" variable without returning a copy of it (in the case of array_walk)
http://php.net/manual/en/function.array-walk.php
Return Values
Returns TRUE.
Which of course just makes your condition pass no matter what. So you should always test both the passing and the failing of the condition (As I did by placing some "canned" bad data in there). This way I know for 100% sure exactly how my code behaves.
Others have posted how to correct this, but not what you did wrong. But just for the sake of completeness I will post an answer anyway.
$arr = array (
0 =>
array (
'date' => '2019-01-16',
'number1' => 20.4,
'number2' => 20.54,
'number3' => 19.71,
'number4' => 19.73,
'number5' => 70849266,
'number6' => 70849266,
'number7' => -0.65,
'number8' => -3.189,
'number9' => 20.0902,
'string1' => 'Jan16',
'number10' => 0.047796070100903
),
array (
'date' => '2019-01-16',
'number1' => 20.4,
'number2' => 20.54,
'number3' => 19.71,
'number4' => 19.73,
'number5' => 70849266,
'number6' => 70849266,
'number7' => -0.65,
'number8' => 'foo',#intentially not number
'number9' => 20.0902,
'string1' => 'Jan16',
'number10' => 0.047796070100903
),
);
$col = array_column($arr, "number8");
$col1 = array_filter($col, 'is_numeric');
if($col != $col1){
echo "not the same\n";
}
Output:
not the same
Sandbox
I should mention, there is no "need" to count these, as PHP can compare complex objects for equality. As we are comparing the same "root" array ($col in this example) with itself after (possibly) removing some elements, if no elements were removed then both arrays should be not only the same length but also "Identical".
Also if you want to do it in one line (inside the condition) you can do this:
if( ($col = array_column($arr, "number8")) && $col != array_filter($col, 'is_numeric')){
echo "not the same\n";
}
It's a bit harder to read, and pay attention to $col = array_column assignment.
Use a foreach loop:
$bool = true;
foreach ($arr as $row)
{
if (!is_numeric($row['number8']))
{
$bool = false;
break;
}
}
Thanks a million everyone, for your great answers!
On my PC, I tried your four functions in PHP 5.5.38 with ~5000 iterations and total times are:
"is_numeric_array_with_cast total time is 0.44153618812561"
"with_array_filter total time is 0.21628260612488"
"is_numeric_array_with_func total time is 0.14269280433655"
"is_numeric_matt_fryer total time is 0.155033826828"
$t1=$t2=$t3=$t4=0;
foreach($arrs as $k=>$arr){
$s1=microtime(true);
is_numeric_array_with_cast(array_column($arr, "number8"));
$e1=microtime(true)-$s1;
$t1=$t1+$e1;
$s2=microtime(true);
with_array_filter(array_column($arr, "number8"));
$e2=microtime(true)-$s2;
$t2=$t2+$e2;
$s3=microtime(true);
is_numeric_array_with_func(array_column($arr, "number8"));
$e3=microtime(true)-$s3;
$t3=$t3+$e3;
$s4=microtime(true);
is_numeric_matt_fryer(array_column($arr, "number8"),"number8");
$e4=microtime(true)-$s4;
$t4=$t4+$e4;
}
function is_numeric_array_with_cast($arr) {
foreach ($arr as $b) {
if ($b != (string)(float)$b) {
return false;
}
}
return true;
}
function with_array_filter($arr) {
return $arr == array_filter($arr, 'is_numeric');
}
function is_numeric_array_with_func($arr) {
foreach ($arr as $b) {
if (!is_numeric($b)) {
return false;
}
}
return true;
}
function is_numeric_matt_fryer($arr,$str){
$bool = true;
foreach ($arr as $row)
{
if (!is_numeric($row[$str]))
{
$bool = false;
}
}
return $bool;
}

Return last set of array from PHP recursive function [duplicate]

This question already has answers here:
How to use return inside a recursive function in PHP
(4 answers)
Closed 9 months ago.
I am trying to filter set of array through recursive function but can not return it when filtered is done.
function get_the_answer($number, $array=array()){
$returnArray = array();
$QS = array(1=>'C', 2=>'B', 3=>'C', 4=>'D');
$SL = array(
1 => array(
'cb1' => array('A','B', 'D'),
'cb2' => array('B','C', 'D'),
'cb3' => array('A','C', 'B'),
),
2 => array(
'cb1' => array('B','C','D'),
'cb2' => array('A','C','D'),
'cb3' => array('A','B','D'),
),
3 => array(
'cb1' => array('E','C','D'),
'cb2' => array('B','E','D'),
'cb3' => array('A','C','D'),
),
4 => array(
'cb1' => array('A','D','E'),
'cb2' => array('A','C','D'),
'cb3' => array('B','C','D'),
)
);
if($number<=4){
if(empty($array)){
$returnedValue = return_filtered_company($SL[$number], $QS[$number]);
} else {
$returnedValue = return_filtered_company($array, $QS[$number]);
}
//print_r($returnedValue); -> receiving values nicely, need the last set of array which is 'cb3'
$returnArray[] = $returnedValue;
$increment = $number+1;
if($increment<=4){
$result=array_intersect_key($SL[$increment], $returnedValue);
get_the_answer($increment, $result);
}
}
return $returnArray;
}
//function for company calculation
function return_filtered_company($companyArray, $answer){
$foundCB=array();
if(is_array($companyArray) && count($companyArray)){
foreach ($companyArray as $name => $arrayVal){
if (in_array($answer, $arrayVal)) {
$foundCB[$name]=$name;
}
}
}
return $foundCB;
}
I am calling answer function like this way:
$ans = get_the_answer(1);
echo '<pre>';
print_r($ans);
echo '</pre>';
Everything is working fine but it is returning value which happened in 1st recursion not the last one. Answer should be cb3
Someone please help to find out what is/are the best solution to return last occurrence of recursion.
Replacing line 42 get_the_answer($increment, $result); with return get_the_answer($increment, $result); solves it.
See the screenshot

PHP Find a value in a key and get the value of another key in a multi-dimentional array

Might be a newbie question but I've been trying to figure this problem and it's doing my head in.
I have the following array :
[0] => Array
(
[provisionalBookingRoomID] => 1
[totalSpecificRoomCount] => 2
)
[1] => Array
(
[provisionalBookingRoomID] => 2
[totalSpecificRoomCount] => 5
)
I need a php function that searches through the array for the value of 'provisionalBookingRoomID' and returns the value of 'totalSpecificRoomCount'
basically something like the following
getProvisionalTotalRoomsCount($currentRoom, $arrayOfRooms);
// getProvisionalTotalRoomsCount('1', $arrayOfRooms) should return 2;
Any ideas?
Check this:
getProvisionalTotalRoomsCount($currentRoom, $arrayOfRooms){
foreach($arrayOfRooms as $key=>$value){
if($value['provisionalBookingRoomID'] == $currentRoom){
return $value['totalSpecificRoomCount'];
}
}
}
For anyone looking for a generic function :
function findValueInArray($array, $searchValue, $searchKey, $requiredKeyValue) {
foreach($array as $key=>$value){
if($value[$searchKey] == $searchValue){
return $value[$requiredKeyValue];
}
}
}
// Usage : findValueInArray($provisionalBookedRoomsArray, '1', 'provisionalBookingRoomID', 'totalSpecificRoomCount');
If you are likely to work with more than one value, you could build a new array with a 1->1 map for those attributes.
<?php
$items = array(
array(
'name' => 'Foo',
'age' => 23
),
array(
'name' => 'Bar',
'age' => 47
)
);
// Php 7
$name_ages = array_column($items, 'name', 'age');
echo $name_ages['Foo']; // Output 23
// Earlier versions:
$name_ages = array();
foreach($items as $value)
{
$name_ages[$value['name']] = $value['age'];
}
echo $name_ages['Foo']; // Output 23
$value = 0;
$array = array(array("provisionalBookingRoomID"=>1,"totalSpecificRoomCount"=>2),array("provisionalBookingRoomID"=>2,"totalSpecificRoomCount"=>5));
array_map(
function($arr) use (&$value) {
if($arr['provisionalBookingRoomID']==1) {
$value = $arr['totalSpecificRoomCount'];
}
},$array
);
echo $value;

call_user_func_array with array_multisort [duplicate]

This question already has answers here:
Sort array using array_multisort() with dynamic number of arguments/parameters/rules/data
(5 answers)
Closed 2 years ago.
I have the problem with sort direction. I try to sort multi-dimensional array with direction. I can't use array_multisort() directly, because I don't know how many parametrs will be. I use call_user_func_array('array_multisort', $params); And it works, but I can't set sort direction (SORT_ASC,SORT_DESC). How can I set sort direction for call_user_func_array('array_multisort', $params);?
Here is my code, you can try it
function get_fields($data, $order_by) {
$order_row = preg_split("/[\s,]+/", $order_by);
for ($i=0;$i<count($order_row);$i++) {
foreach ($data as $key => $row) {
$tmp[$i][$key] = $row[$order_row[$i]];
}
}
return $tmp;
}
function ordering($data, $order_by) {
$tmp = get_fields($data, $order_by);
$params = array();
foreach($tmp as &$t){
$params[] = &$t;
}
$params[1] = array("SORT_DESC","SORT_DESC","SORT_DESC","SORT_DESC"); // like that no warning but no sorting
$params[] = &$data;
call_user_func_array('array_multisort', $params);
return array_pop($params);
}
$data = array (
array('id' => 1,'name' => 'Barack','city' => 9),
array('id' => 7,'name' => 'boris','city' => 2),
array('id' => 3,'name' => 'coris','city' => 2),
array('id' => 3,'name' => 'coris','city' => 2)
);
$order_by = "city desc, name";
echo "<br>ORDER BY $order_by<br>";
$ordered = ordering($data, $order_by);
echo "<pre>";
var_dump($ordered);
echo "</pre>";
I want to do a sort like MySQL ORDER BY city DESC, name. It's my goal.
To be able to sort an array multiple times and achieve a result like ORDER BY city DESC, name ASC you need a function that does a stable sort.
As far as I know PHP doesn't have one so you have to sort it once with a comparator function like this
$data = array (
array('id' => 3,'name' => 'coris','city' => 2),
array('id' => 1,'name' => 'Barack','city' => 9),
array('id' => 7,'name' => 'boris','city' => 2),
array('id' => 3,'name' => 'coris','city' => 2),
);
$order_by = array(
'city' => array('dir' => SORT_DESC, 'type' => SORT_NUMERIC),
'name' => array('dir' => SORT_ASC, 'type' => SORT_STRING),
);
function compare($row1,$row2) {
/* this function should determine which row is greater based on all of the criteria
and return a negative number when $row1 < $row2
a positive number when $row1 > $row2
0 when $row1 == $row2
*/
global $order_by;
foreach($order_by as $field => $sort) {
if($sort['type'] != SORT_NUMERIC) {
// strings are compared case insensitive and assumed to be in the mb_internal_encoding
$cmp = strcmp(mb_strtolower($row1[$field]), mb_strtolower($row2[$field]));
} else {
$cmp = doubleval($row1[$field]) - doubleval($row2[$field]);
}
if($sort['dir'] != SORT_ASC) $cmp = -$cmp;
if($cmp != 0) return $cmp;
}
return 0;
}
usort($data,'compare');
I had the same problem. It seems that call_user_func_array() can't handle the constants.
I've solved this problem by dynamically building an argument string and evaluating this string:
$args = array($arr1, $arr2);
$order = array(SORT_ASC, SORT_DESC);
$evalstring = '';
foreach($args as $i=>$arg){
if($evalstring == ''){ $evalstring.= ', '; }
$evalstring.= '$arg';
$evalstring.= ', '.$order[$i];
}
eval("array_multisort($evalstring);");
I know eval() is evil and this is not a clean way, but it works ;-)
It is working for me :
$arrayThatNeedToSort = array('data..');
$multiSortprop = array(['data.....']=> SORT_DESC,['data.....'] => SORT_ASC)
$properties = array();
foreach ($multiSortprop as $sortArr => $sortArg) {
array_push($properties,$sortArr);
array_push($properties,$sortArg);
}
array_push($properties,$arrayThatNeedToSort);
array_multisort(...$properties);
var_dump(end($properties));

Transpose 2D array structure [duplicate]

This question already has answers here:
Transposing multidimensional arrays in PHP
(12 answers)
Closed 6 months ago.
I don't think this can be done (with out my own function), however I'll ask any way.
I have a array in PHP like
array(
'item1' => array(
'Hello',
'Good',
),
'item2' => array(
'World',
'Bye',
),
)
The individual fields come from a web form
I want to rearrange it to the following
array(
array(
'item1' => 'Hello',
'item2' => 'World',
),
array(
'item1' => 'Good',
'item2' => 'Bye',
),
)
Make an array of objects from the field arrays
I could write a function to do this.
However I was wondering can one of the built-in array functions achieve this for me?
There is no built in function to do this - but here is a user-defined one that will:
function convert_array ($array) {
$result = array();
foreach ($array as $key => $inner) {
for ($i = 0; isset($inner[$i]); $i++) {
$result[$i][$key] = $inner[$i];
}
}
return $result;
}
See it working
Most certainly not the most efficient, but as close to "one function" as you'll get. ;)
array_map(function ($i) use ($array) { return array_combine(array_keys($array), $i); }, call_user_func_array('array_map', array_merge(array(function () { return func_get_args(); }), $array)));
See http://codepad.viper-7.com/xIn3Oq.
No. There is no built in function that would do this.

Categories