call_user_func_array with array_multisort [duplicate] - php

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));

Related

PHP: Find last occurence of string in array

I need to find the last found element of a specific value from an array. I giving an example in php of what I'm actually seeking for.
$Data = array(
'0' => 'car',
'1' => 'bike',
'2' => 'bus',
'3' => 'bike',
'4' => 'boat'
);
$key = array_search('bike', $Data) // it returns $key = 1 as result which the first element matched inside the array.
I want $key = 3 which is the last matched element.
Any suggestion appreciated.
PHP code demo
<?php
ini_set("display_errors", 1);
$Data = array(
'0' => 'car',
'1' => 'bike',
'2' => 'bus',
'3' => 'bike',
'4' => 'boat'
);
$toSearch="bike";
$index=null;
while($key=array_search($toSearch, $Data))
{
$index=$key;
unset($Data[$key]);
}
echo $index;
Here is the more simple and highly performace way. For it only calculate once, you can access it many time. The live demo.
$data = array_flip($Data);
echo $data['bike'];
after the flip, only keep the last element of the same elements. Here is the print_r($data)
Array
(
[car] => 0
[bike] => 3
[bus] => 2
[boat] => 4
)
We can use array_reverse to reverse array.
$key = array_search('bike', array_reverse($Data,true));
It will return 3.
you can use krsort to sort the array by key.
krsort($Data);
$key = array_search('bike', $Data);
echo $key;
Working example: https://3v4l.org/fYOgN
For this I am created one function it is very easy to use. You can pass only array and parameters.
function text_to_id($value, $arr_master) {
$id_selected = 0;
$search_array = $arr_master;
if (in_array($value, $search_array)) {
$id_selected = array_search($value, $search_array);
// pr($id_selected);exit;
}
if (!$id_selected) {
foreach ($search_array as $f_key => $f_value) {
if (is_array($f_value)) {
if (in_array($value, $f_value)) {
$id_selected = $f_key;
break;
}
} else if ($value == $f_value) {
$id_selected = $f_key;
break;
}
else;
}
}
return $id_selected;
}
this function use like this
$variable = text_to_id('bike', $your_array);

How to compare and filter 2 arrays

first array like this
$zones_array1 = array();
$zones_array1[] = array('id' => 'Alabama', 'text' => 'Alabama');
$zones_array1[] = array('id' => 'Alaska', 'text' => 'Alaska');
$zones_array1[] = array('id' => 'Arizona', 'text' => 'Arizona');
$zones_array1[] = array('id' => 'Arkansas', 'text' => 'Arkansas');
second array like this
$zones_array2 = array();
$zones_array2[] = array('id' => 'Alaska', 'text' => 'Alaska');
$zones_array2[] = array('id' => 'Arizona', 'text' => 'Arizona');
i want filter these two array and i want final result as array like this
first array like this
$zones_array3 = array();
$zones_array3[] = array('id' => 'Alabama', 'text' => 'Alabama');
$zones_array3[] = array('id' => 'Arkansas', 'text' => 'Arkansas');
please help me
You can use php 'in_array' to check weather an element exists inside other array or not. In you case the array is multidimensional so stored all the id's inside a newly created array and then compared the given array with that.
$check_array = array();
foreach ($zones_array1 as $arr1){
$check_array[] = $arr1['id'];
}
$zones_array3 = array();
foreach ($zones_array2 as $arr2){
if (!in_array($arr2['id'], $check_array))
{
$zones_array3[] = $arr2;
}
}
echo '<pre>';
print_r($zones_array3);
Simply try:
function udiffCompare($a, $b)
{
return $a['id'] == $b['id'] ? 0 : -1;
}
$arrdiff = array_udiff($zones_array1, $zones_array2, 'udiffCompare');
echo '<pre>';
print_r($arrdiff);
array_udiff() compares each element of the first array-argument against all the elements of the second array-argument using the provided callback function. If the callback returns zero for any of the comparisons then the element of the array in the first argument will not be present in the returned array of the function.
You will try it :
function unique_multidim_array($array, $key){
$temp_array = array();
$i = 0;
$key_array = array();
foreach($array as $val){
if(!in_array($val[$key],$key_array)){
$key_array[$i] = $val[$key];
$temp_array[$i] = $val;
}
$i++;
}
return $temp_array;
}
$zones_array1 = array_merge($zones_array2, $zones_array3);
$zones_array1 = unique_multidim_array($zones_array1, 'id');
print_r($zones_array1);
Please try this
array_push($zones_array1,$zones_array2);
print_r(array_unique($zones_array1));
I am not sure.

PHP sort array of arrays [duplicate]

This question already has answers here:
How can I sort arrays and data in PHP?
(14 answers)
Closed 8 years ago.
I have an array of arrays in PHP that I created like the following:
$wp_players = array();
while ($wp_player = mysql_fetch_array($wp_player_query))
{
$wp_player_ranking = mysql_query(get_ranking_sql($wp_player['id'])) or die(mysql_error());
$wp_ranking = mysql_fetch_array($wp_player_ranking);
array_push($wp_players, array('first_name' => $wp_player['first_name'],
'last_name' => $wp_player['last_name'],
'school_name' => $wp_player['school_name'],
'1st' => $wp_ranking['1st'],
'2nd' => $wp_ranking['2nd'],
'3rd' => $wp_ranking['3rd'],
'4th' => $wp_ranking['4th'],
'5th' => $wp_ranking['5th'],
'total' => ($wp_ranking['1st'] + $wp_ranking['2nd'] + $wp_ranking['3rd'] + $wp_ranking['4th'] + $wp_ranking['5th'])));
}
What I want to do now is have $wp_players array sorted by the 'total' key that's inside each of its elements. Since the Array is not flat, and is an array of arrays, what's the best way to do this in PHP?
array_multisort() will accomplish precisely what you're looking to achieve:
$totals = array();
foreach ($wp_players as $key => $row) {
$totals[$key] = $row['total'];
}
array_multisort($totals, SORT_DESC, $wp_players);
This function will be what you want, this function if from my development framework, It suits for many situations, has order control and reserveKey control parameters.
<?php
function sortByField($arr, $fieldName, $flag = 'desc', $reserveKey = true)
{
$indexArr = array();
foreach ($arr as $idx => $item)
{
$indexArr[$idx] = $item[$fieldName];
}
if ('desc' == $flag)
{
arsort($indexArr);
}
else
{
asort($indexArr);
}
$result = array();
foreach ($indexArr as $idx => $field)
{
if($reserveKey)
{
$result[$idx] = $arr[$idx];
}
else
{
$result[] = $arr[$idx];
}
}
return $result;
}
usage
$wp_players = sortByField($wp_players, 'total');

Multisort a 2 deep array

Consider the following multisort method. In this case I have a array of items with a specific start date. Example array is shown:
0 -> array('title' => 'hello',
'attributes' => array('id' => 4, 'startdate' => '2013-06-11')),
1 -> array('title' => 'hello second entry',
'attributes' => array('id' => 6, 'startdate' => '2013-04-11'))
You can see that the 2nd entry should come before the first. Using my call currently will not work because It only checks to depth 1 of the array.
$albums = $this->multiSort($items, "SORT_ASC", 'startdate', true);
How would be the best way to modify this method to have a depth search on the items in the array. Even better would be to be able to specific the depth key. I would like to avoid having to add additional parameters to the method.
I could call the method like so and then write a for loop to get the key data, but having nested for loops is not something I want to do.
$albums = $this->multiSort($items, "SORT_ASC", array('attributes', 'startdate') , true);
What is the best way to optimize this method for my case?
public function multiSort($data, $sortDirection, $field, $isDate) {
if(empty($data) || !is_array($data) || count($data) < 2) {
return $data;
}
foreach ($data as $key => $row) {
$orderByDate[$key] = ($isDate ? strtotime($row[$field]) : $row[$field]);
}
if($sortDirection == "SORT_DESC") {
array_multisort($orderByDate, SORT_DESC, $data);
} else {
array_multisort($orderByDate, SORT_ASC, $data);
}
return $data;
}
UPDATED. This allows you to pass in a string for field that is delimited and is a path to your desired field.
$items = Array();
$items[0] = array('title' => 'hello',
'attributes' => array('id' => 4, 'startdate' => '2013-06-11'));
$items[1] = array('title' => 'hello second entry',
'attributes' => array('id' => 6, 'startdate' => '2013-04-11'));
function multiSort($data, $sortDirection, $field, $isDate) {
if(empty($data) || !is_array($data) || count($data) < 2) {
return $data;
}
// Parse our search field path
$parts = explode("/", $field);
foreach ($data as $key => $row) {
$temp = &$row;
foreach($parts as $key2) {
$temp = &$temp[$key2];
}
//$orderByDate[$key] = ($isDate ? strtotime($row['attributes'][$field]) : $row['attributes'][$field]);
$orderByDate[$key] = ($isDate ? strtotime($temp) : $temp);
}
unset($temp);
if($sortDirection == "SORT_DESC") {
array_multisort($orderByDate, SORT_DESC, $data);
} else {
array_multisort($orderByDate, SORT_ASC, $data);
}
return $data;
}
$albums = multiSort($items, "SORT_ASC", 'attributes/startdate', true);
print_r($albums);
Ouput:
Array
(
[0] => Array
(
[title] => hello second entry
[attributes] => Array
(
[id] => 6
[startdate] => 2013-04-11
)
)
[1] => Array
(
[title] => hello
[attributes] => Array
(
[id] => 4
[startdate] => 2013-06-11
)
)
)

How to sort this array by type (dir first and then files)

I wanna know how to sort arrays like this:
$array[] = Array (
'name' => '/home/gtsvetan/public_html/presta/cms.php'
'type' => 'text/x-php'
'size' => 1128
'lastmod' => 1339984800
);
$array[] = Array (
'name' => '/home/gtsvetan/public_html/presta/docs/'
'type' => 'dir'
'size' => 0
'lastmod' => 1329253246
);
I wanna to sort it first by type (folders first and then files) and then alphabetical. But I don't know how to sort it.
Best regards,
George!
you can use usort()
In compare function you do two comparisions on name & type - something like below:
function compare_f($a,$b) {
if($a['type']=='dir'&&$b['type']!='dir') return 1;
if($a['type']!='dir'&&$b['type']=='dir') return -1;
if(substr($a['name'],-1,1)=='/') $a['name']=substr($a['name'],0,-1);
if(substr($b['name'],-1,1)=='/') $b['name']=substr($b['name'],0,-1);
$af_array=explode('/',$a['name']);
$a_name=$af_array[count($af_array)-1];
$bf_array=explode('/',$b['name']);
$b_name=$bf_array[count($bf_array)-1];
if($a_name>$b_name)
return 1;
return -1;
}
usort($array,'compare_f');
You can do something like this..
$dir = array();
$file = array();
$dir = array();
$file = array();
foreach($array as $b=>$v) {
if($v['type'] == "dir") {
$dir[] = $v;
} else {
$file[] = $v;
}
}
$combined = array_merge($dir, $file);
Feel free to adjust it.

Categories