php array_multisort() by 2 field - php

I use array_multisort() function to sort an array by a field value. what I need is to sort it by 2 field values, by date and by time.
here is the structure of the array:
Array
(
[0] => Array
(
[id_art] => 5292
[free_art] => 0
[apero_art] => 0
[name_cat] => Teatro
[date_dat] => 2010-11-24
[date2_dat] => 0000-00-00
[name_spa] => Cinema Teatro
[title_int] => Il piacere dell'onestÃ
[id_cat] => 2
[time_tim] => 20:30:00
[intro_int] => Una produzione Teatro Eliseo di Roma - ChiassoCultura
[image_art] => noimage.png
)
[1] => Array
(
[id_art] => 4983
[free_art] => 0
[apero_art] => 0
[name_cat] => Cinema
[date_dat] => 2011-04-20
[date2_dat] => 2011-04-20
[name_spa] => Cinema Morettina
[title_int] => Inland Empire
[id_cat] => 1
[time_tim] => 20:30:00
[intro_int] => Rassegna dedicata a David Lynch
[image_art] => noimage.png
)
[2] => Array
(
[id_art] => 4983
[free_art] => 0
[apero_art] => 0
[name_cat] => Cinema
[date_dat] => 2011-04-22
[date2_dat] => 2011-04-22
[name_spa] => Cinema Iride
[title_int] => Inland Empire
[id_cat] => 1
[time_tim] => 17:00:00
[intro_int] => Rassegna dedicata a David Lynch
[image_art] => noimage.png
)....
What I do now is:
function array_sort_by_column(&$arr, $col, $dir = SORT_ASC) {
$sort_col = array();
foreach ($arr as $key=> $row) {
$sort_col[$key] = $row[$col];
}
array_multisort($sort_col, $dir, $arr);
}
How can I easily make in one time the double sort?
Thanks.

If I understand what you need to do, you can use usort:
usort($array, function($a, $b) {
if ($a['date_cat'] > $b['date_cat']) return 1;
if ($a['date_cat'] < $b['date_cat']) return -1;
if ($a['time_tim'] > $b['time_tim']) return 1;
if ($a['time_tim'] < $b['time_tim']) return -1;
return 0;
});
The way your function works it a bit twisty, to me.
You extract a column of your array to create an array to sort linked to the original array. It took several readings and a php manual check to understand your code (too bad), you should have (to me) detach the creation of the index array into another function prior than calling the sort.
If you need to have dynamic colums selection maybe you can leverage on closures (PHP 5.3+ ?):
function your_sort($orderby, $array) {
return usort($array, function ($a, $b) use ($orderby) {
foreach($orderby as $field) {
if ($a[$field] > $b[$field]) return 1;
if ($a[$field] < $b[$field]) return -1;
}
return 0;
});
}
And then you can call the function like this:
your_sort(array('date_cat','time_tim'), $array);

Usign some code from php.net comment and other adjustment, I wrote a function that accept these argument:
$a = array
$orderby = an order string written in sql language
$children_key = name of the eventual column where eventual children node are saved
Here the function:
function array_multiorderby( $data, $orderby, $children_key=false )
{
// parsing orderby
$args = array();
$x = explode( ' ', str_replace( ',', ' ', $orderby ) );
foreach( $x as $item )
{
$item = trim( $item );
if( $item=='' ) continue;
if( strtolower($item)=='asc' ) $item = SORT_ASC;
else if( strtolower($item)=='desc' ) $item = SORT_DESC;
$args[] = $item;
}
// order
foreach ($args as $n => $field)
{
if (is_string($field))
{
$tmp = array();
foreach ($data as $key => $row)
$tmp[$key] = $row[$field];
$args[$n] = $tmp;
}
}
$args[] = &$data;
call_user_func_array('array_multisort', $args);
$data = array_pop($args);
// order children
if( $children_key )
{
foreach( $data as $k=>$v ) if( is_array($v[$children_key]) ) $data[$k][$children_key] = array_multiorderby( $v[$children_key], $children_key, $orderby );
}
// return
return $data;
}
Application to your specific case:
$array = array_multiorderby( $array, "date_dat DESC, time_tim DESC" );

Related

Create a hierarchical list of array elements

I have a an array of products. Each product contains its category and subcategories listed in a hierarchical order:
Array
(
[product_id_1] => Array
(
[0] => Men
[1] => Sunglasses
[2] => Luxury
[3] => Ray-ban
)
[product_id_2] => Array
(
[0] => Women
[1] => Lenses
[2] => Casual
[3] => Gucci
)
[product_id_3] => Array
(
[0] => Men
[1] => Sunglasses
[2] => Casual
[3] => Prada
)
[...]
)
I want to create an unordered hierarchical HTML menu like so:
-Men
--Sunglasses
---Luxury
----Ray-ban
---Casual
----Prada
-Women
--Lenses
---Casual
----Gucci
The function should strip out repetitive categories and subcategories. This script returns the array of products that I've posted at the top:
<?php
function displayNestedMenu( $posts, $taxonomies ) {
foreach ( $posts as $post ) {
foreach ( $taxonomies as $key => $taxonomy ) {
$push = wp_get_object_terms( $post->ID, $taxonomy );
if ( !empty( $push ) ) {
$list[$post->ID][] = $push[0]->name;
}
}
}
return $list;
}
print_r( displayNestedMenu( $posts, $taxonomies ) );
?>
I imagine the solution should invoke the function inside the function but after trying a couple of methods I haven't succeeded yet. Any suggestions are appreciated!
Here is a simple idea :
$array = array(
'product_id_1' => array(
'Men',
'Sunglasses',
'Luxury',
'Ray-ban'
),
'product_id_2' => array(
'Women',
'Lenses',
'Casual',
'Gucci',
),
'product_id_3' => array(
'Men',
'Sunglasses',
'Casual',
'Prada'
)
);
The idea is to recreate the keys depending on the parent category, after that we sort them using ksort():
function tree($array){
$newArray = array();
foreach ($array as $arr) {
foreach ($arr as $key => $row) {
if ($key > 0) {
$index = array();
for ($i = 0; $i <= $key; $i++)
$index[] = $arr[$i];
$index = implode('_', $index);
} else
$index = $row;
$newArray[$index] = $row;
}
}
ksort($newArray);
return $newArray;
}
Then display the HTML :
$products = tree($array);
$i = 0;
echo '<ul style="list-style-type:none">';
foreach ($products as $key => $row) {
if(strcmp($row, $key) == 0 && $i != 0)
echo '</ul><br><ul style="list-style-type:none">';
++$i;
$level = count(explode('_', $key));
$padding = 15 * (--$level);
echo
'<li style="padding-left:' . $padding . 'px">
<span style="border-left:1px dashed black;border-bottom:1px dashed black;"> ' . $row . '</span>
</li>';
}
echo '</ul>';
PHP has powerful array features: string-indexed arrays can help provide solutions to problems like this one.
For the array conversion step:
$hrchy=array();
foreach($products AS $product){//$products is as per your first array, at start…
hrchy_ins($hrchy,$product);
}
function hrchy_ins(array &$hierarchy,array $product){//$hierarchy should be passed by reference…
if(\count($product)>0){//Condition necessary to implement base case, avoiding infinite recursion
if(!isset($hierarchy[$product[0]])){$hierarchy[$product[0]]=array();}//Conditional execution ignores duplicates…
if(\count($product)>1){hrchy_ins($hierarchy[$product[0]],\array_slice($product,1));}//Condition may not be strictly necessary (see condition above!)
} }
We might now use a recursive approach for a further HTML-writing step (the recursion secret sauce = a simple recursive function including a branch-on-condition for the base-case):
function prod_list(array $hierarchy){
if(\count($hierarchy)===0){
return '';
}else{
$list='';
$list.='<ul>';
foreach($hierarchy AS $cat => $children){
$list.='<li>'.$cat;
$list.=prod_list($children);//Recursive step…
$list.='</li>';
}
$list.='<ul>';
return $list;
}
}
Finally, after defining the function, we invoke it:
echo(prod_list($hrchy));
Disclaimer: I have not tested this code.
You could transform the array in a static way, as the structure you describe always has four parts;
$hierarchy = array();
foreach($products as $product_id => $product) {
list($gender, $category, $type, $brand) = $product;
$hierarchy[$gender][$category][$type][$brand][] = $product_id;
}

Pad short input arrays with their last element value to ensure equal lengths and transpose arrays into one array

I have the following Arrays:
$front = array("front_first","front_second");
$inside = array("inside_first", "inside_second", "inside_third");
$back = array("back_first", "back_second", "back_third","back_fourth");
what I need to do is combine it so that an output would look like this for the above situation. The output order is always to put them in order back, front, inside:
$final = array(
"back_first",
"front_first",
"inside_first",
"back_second",
"front_second",
"inside_second",
"back_third",
"front_second",
"inside_third",
"back_fourth",
"front_second",
"inside_third"
);
So basically it looks at the three arrays, and whichever array has less values it will reuse the last value multiple times until it loops through the remaining keys in the longer arrays.
Is there a way to do this?
$front = array("front_first","front_second");
$inside = array("inside_first", "inside_second", "inside_third");
$back = array("back_first", "back_second", "back_third","back_fourth");
function foo() {
$args = func_get_args();
$max = max(array_map('sizeof', $args)); // credits to hakre ;)
$result = array();
for ($i = 0; $i < $max; $i += 1) {
foreach ($args as $arg) {
$result[] = isset($arg[$i]) ? $arg[$i] : end($arg);
}
}
return $result;
}
$final = foo($back, $front, $inside);
print_r($final);
demo: http://codepad.viper-7.com/RFmGYW
Demo
http://codepad.viper-7.com/xpwGha
PHP
$front = array("front_first", "front_second");
$inside = array("inside_first", "inside_second", "inside_third");
$back = array("back_first", "back_second", "back_third", "back_fourth");
$combined = array_map("callback", $back, $front, $inside);
$lastf = "";
$lasti = "";
$lastb = "";
function callback($arrb, $arrf, $arri) {
global $lastf, $lasti, $lastb;
$lastf = isset($arrf) ? $arrf : $lastf;
$lasti = isset($arri) ? $arri : $lasti;
$lastb = isset($arrb) ? $arrb : $lastb;
return array($lastb, $lastf, $lasti);
}
$final = array();
foreach ($combined as $k => $v) {
$final = array_merge($final, $v);
}
print_r($final);
Output
Array
(
[0] => back_first
[1] => front_first
[2] => inside_first
[3] => back_second
[4] => front_second
[5] => inside_second
[6] => back_third
[7] => front_second
[8] => inside_third
[9] => back_fourth
[10] => front_second
[11] => inside_third
)
Spreading the column data from multiple arrays with array_map() is an easy/convenient way to tranpose data. It will pass a full array of elements from the input arrays and maintain value position by assigning null values where elements were missing.
Within the custom callback, declare a static cache of the previously transposed row. Iterate the new transposed row of data and replace any null values with the previous rows respective element.
After transposing the data, call array_merge(...$the_transposed_data) to flatten the results.
Code: (Demo)
$front = ["front_first", "front_second"];
$inside = ["inside_first", "inside_second", "inside_third"];
$back = ["back_first", "back_second", "back_third", "back_fourth"];
var_export(
array_merge(
...array_map(
function(...$cols) {
static $lastSet;
foreach ($cols as $i => &$v) {
$v ??= $lastSet[$i];
}
$lastSet = $cols;
return $cols;
},
$back,
$front,
$inside
)
)
);
Output:
array (
0 => 'back_first',
1 => 'front_first',
2 => 'inside_first',
3 => 'back_second',
4 => 'front_second',
5 => 'inside_second',
6 => 'back_third',
7 => 'front_second',
8 => 'inside_third',
9 => 'back_fourth',
10 => 'front_second',
11 => 'inside_third',
)

PHP: Count the appearance of particular value in array

I am wondering if I could explain this.
I have a multidimensional array , I would like to get the count of particular value appearing in that array
Below I am showing the snippet of array . I am just checking with the profile_type .
So I am trying to display the count of profile_type in the array
EDIT
Sorry I've forgot mention something, not something its the main thing , I need the count of profile_type==p
Array
(
[0] => Array
(
[Driver] => Array
(
[id] => 4
[profile_type] => p
[birthyear] => 1978
[is_elite] => 0
)
)
[1] => Array
(
[Driver] => Array
(
[id] => 4
[profile_type] => d
[birthyear] => 1972
[is_elite] => 1
)
)
)
Easy solution with RecursiveArrayIterator, so you don't have to care about the dimensions:
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
$counter = 0
foreach ($iterator as $key => $value) {
if ($key == 'profile_type' && $value == 'p') {
$counter++;
}
}
echo $counter;
Something like this might work...
$counts = array();
foreach ($array as $key=>$val) {
foreach ($innerArray as $driver=>$arr) {
$counts[] = $arr['profile_type'];
}
}
$solution = array_count_values($counts);
I'd do something like:
$profile = array();
foreach($array as $elem) {
if (isset($elem['Driver']['profile_type'])) {
$profile[$elem['Driver']['profile_type']]++;
} else {
$profile[$elem['Driver']['profile_type']] = 1;
}
}
print_r($profile);
You may also use array_walk($array,"test") and define a function "test" that checks each item of the array for 'type' and calls recursively array_walk($arrayElement,"test") for items of type 'array' , else checks for the condition. If condition satisfies, increment a count.
Hi You can get count of profuke_type==p from a multi dimensiona array
$arr = array();
$arr[0]['Driver']['id'] = 4;
$arr[0]['Driver']['profile_type'] = 'p';
$arr[0]['Driver']['birthyear'] = 1978;
$arr[0]['Driver']['is_elite'] = 0;
$arr[1]['Driver']['id'] = 4;
$arr[1]['Driver']['profile_type'] = 'd';
$arr[1]['Driver']['birthyear'] = 1972;
$arr[1]['Driver']['is_elite'] = 1;
$arr[2]['profile_type'] = 'p';
$result = 0;
get_count($arr, 'profile_type', 'd' , $result);
echo $result;
function get_count($array, $key, $value , &$result){
if(!is_array($array)){
return;
}
if($array[$key] == $value){
$result++;
}
foreach($array AS $arr){
get_count($arr, $key, $value , $result);
}
}
try this..
thanks

Sort an associative array in php with multiple condition

Consider following array
$details = array(
array('lname'=>'A', 'fname'=>'P','membkey'=>700,'head'=>'y'),
array('lname'=>'B', 'fname'=>'Q','membkey'=>540,'head'=>'n'),
array('lname'=>'C', 'fname'=>'R','membkey'=>700,'head'=>'n'),
array('lname'=>'D', 'fname'=>'S','membkey'=>540,'head'=>'y'),
array('lname'=>'E', 'fname'=>'T','membkey'=>700,'head'=>'n')
);
Here I would like to sort with head and membkey. Top element of same membkey element should have 'head=y' and echoed as,
$details = array(
array('lname'=>'A', 'fname'=>'P','membkey'=>700,'head'=>'y'),
array('lname'=>'E', 'fname'=>'T','membkey'=>700,'head'=>'n'),
array('lname'=>'C', 'fname'=>'R','membkey'=>700,'head'=>'n'),
array('lname'=>'D', 'fname'=>'S','membkey'=>540,'head'=>'y'),
array('lname'=>'B', 'fname'=>'Q','membkey'=>540,'head'=>'n')
);
I tried it as follows
function orderbymemberKey( $a, $b ){
if ( $a[membkey] == $b[membkey] )
return 0;
return($a[membkey] < $b[membkey] )? -1 :1;
}
usort( $details, orderbymemberKey );
and it successfully order by membkey.
Any suggestions please.
You're half way there (though you were sorting backwards for membkey based on your example):
function order_by_member_key($a, $b)
{
if ($a['membkey'] == $b['membkey'])
{
// membkey is the same, sort by head
if ($a['head'] == $b['head']) return 0;
return $a['head'] == 'y' ? -1 : 1;
}
// sort the higher membkey first:
return $a['membkey'] < $b['membkey'] ? 1 : -1;
}
usort($details, "order_by_member_key");
Is this array being pulled from a database? Because, if so, you should be able to make use of ORDER BY clauses to clean it up outside of php.
<?php
$membkey = array();
$head = array();
foreach ($details as $key => $row) {
$membkey[$key] = $row['membkey'];
$head[$key] = $row['head'];
}
array_multisort($membkey, SORT_DESC, $head, SORT_DESC, $details);
print_r($details);
Or, an even more generic solution:
function sort_by($array) {
$arguments = func_get_args();
$array = array_pop($arguments);
$variables = array();
foreach ($arguments as $index => $key) {
$variables[] = '$arguments['.$index.']';
if ($index % 2 == 0) {
$arguments[$index] = array();
foreach ($array as $row) $arguments[$index][] = $row[$key];
}
}
// call_user_func_array will not work in this case
eval('array_multisort('.implode(', ', $variables).', $array);');
return $array;
}
print_r(sort_by('membkey', SORT_DESC, 'head', SORT_DESC, $details));
Ugly but someone wrote a function on php.net:
http://php.net/manual/en/function.sort.php
<?php
$array[0]['name'] = 'Chris';
$array[0]['phone'] = '3971095';
$array[0]['year'] = '1978';
$array[0]['address'] = 'Street 1';
$array[1]['name'] = 'Breanne';
$array[1]['phone'] = '3766350';
$array[1]['year'] = '1990';
$array[1]['address'] = 'Street 2';
$array[2]['name'] = 'Dusty';
$array[2]['phone'] = '1541120';
$array[2]['year'] = '1982';
$array[2]['address'] = 'Street 3';
function multisort($array, $sort_by, $key1, $key2=NULL, $key3=NULL, $key4=NULL, $key5=NULL, $key6=NULL){
// sort by ?
foreach ($array as $pos => $val)
$tmp_array[$pos] = $val[$sort_by];
asort($tmp_array);
// display however you want
foreach ($tmp_array as $pos => $val){
$return_array[$pos][$sort_by] = $array[$pos][$sort_by];
$return_array[$pos][$key1] = $array[$pos][$key1];
if (isset($key2)){
$return_array[$pos][$key2] = $array[$pos][$key2];
}
if (isset($key3)){
$return_array[$pos][$key3] = $array[$pos][$key3];
}
if (isset($key4)){
$return_array[$pos][$key4] = $array[$pos][$key4];
}
if (isset($key5)){
$return_array[$pos][$key5] = $array[$pos][$key5];
}
if (isset($key6)){
$return_array[$pos][$key6] = $array[$pos][$key6];
}
}
return $return_array;
}
//usage (only enter the keys you want sorted):
$sorted = multisort($array,'year','name','phone','address');
print_r($sorted);
//output:
Array ( [0] => Array ( [year] => 1978 [name] => Chris [phone] => 3971095 [address] => Street 1 ) [2] => Array ( [year] => 1982 [name] => Dusty [phone] => 1541120 [address] => Street 3 ) [1] => Array ( [year] => 1990 [name] => Breanne [phone] => 3766350 [address] => Street 2 ) )

PHP: rename multidimensional array's keys

I have a multidimensional array with strings as keys. I want to perform a function (to manipulate the strings) on those keys and then write to a new array (i.e. leave the original array unchanged).
Example:
$oldArr = array(
"foo_old" => array("moo_old" => 1234, "woo_old" => 5678);
"bar_old" => array("car_old" => 4321, "tar_old" => 8765);
);
Becomes:
$newArr = array(
"foo_new" => array("moo_new" => 1234, "woo_new" => 5678);
"bar_new" => array("car_new" => 4321, "tar_new" => 8765);
);
This is just an example, the actual array has more levels/dimensions. Oh and my function doesn't replace "_old" with "_new", again, just an example.
I hope I made some sense, thanks in advance!
Edit: I added a function for printing out the changed array. You may include the code on a website and it will show the result. New edited code:
// array initialisation
oldArr = array();
$subArr1 = array();
$subArr2 = array();
$subArr1["moo_old"]=1234;
$subArr1["woo_old"]=5678;
$subArr2["car_old"]=4321;
$subArr2["tar_old"]=8765;
$oldArr["foo_old"]=$subArr1;
$oldArr["bar_old"]=$subArr2;
$oldArr; // make a copy of the array
// function which replaces recursivly the keys of the array
function renameArrayKeys( $oldArr ) {
$copyArr = $oldArr;
if( is_array( $oldArr) && count( $oldArr ) ) {
foreach ( $oldArr as $k => $v ) {
unset($copyArr[$k]); // removes old entries
$newKey = str_replace( '_old', '_new', $k );
if( is_array( $v ) ) {
$copyArr[ $newKey ] = renameArrayKeys( $v );
}
else {
$copyArr[ $newKey ] = $v;
}
}
return $copyArr;
}
}
// prints out the keys and values of the changed array
function printout($arr ){
foreach ($arr as $k => $val ) {
echo $k."=>".$val." | ";
if( is_array( $val ) ) {
printout( $val );
}
}
}
// calls the above functions
$changedArr = renameArrayKeys($oldArr);
printout($changedArr);
I'm probably slightly late, but recursion is the way forward with this!
$replace_from = "_old"; //can also be array i.e. array("foo_old", "bar_old")
$replace_to = "_new"; //can also be an array i.e. array("foo_new", "bar_new")
$oldArr = array(
"foo_old" => array("moo_old" => 1234, "woo_old" => 5678),
"bar_old" => array("car_old" => 4321, "tar_old" => 8765),
);
function replace($arr){
global $replace_from, $replace_to;
$newArr = array();
foreach($arr as $key => $value){
$newArr[str_replace($replace_from,$replace_to,$key)] = (is_array($value)) ? replace($value) : $value;
}
return $newArr;
}
print_r (replace($oldArr));
Something like this:
function renameKeys( $arr )
{
if( is_array( $arr ) && count( $arr ) ) {
foreach ( $arr as $k => $v ) {
$nk = str_replace( '_old', '_new', $k );
if( is_array( $v ) ) {
$v = renameKeys( $v );
}
$arr[ $nk ] = $v;
unset( $arr[$k] );
}
}
return $arr;
}
$oldArr = array(
"foo_old" => array("moo_old" => 1234, "woo_old" => 5678) ,
"bar_old" => array("car_old" => 4321, "tar_old" => 8765)
);
$nArr = renameKeys( $oldArr );
print_r( $nArr );
Closure version. This doesn't mess up the namespace.
<?php
$from = '_old';
$to = '_new';
$old_arr = array(
'foo_old' => array('moo_old' => 1234, 'woo_old' => 5678),
'bar_old' => array('car_old' => 4321, 'tar_old' => 8765),
);
$func = function ($arr) use (&$func, $from, $to) {
$new_arr = array();
foreach($arr as $k => $v){
$new_arr[str_replace($from, $to, $k)] = is_array($v) ? $func($v) : $v;
}
return $new_arr;
};
print_r($func($old_arr));

Categories