Sort an associative array in php with multiple condition - php

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

Related

Group arrays by key value

I have a problem approaching an issue i have, i need to group arrays by key value
I have 3 foreach functions
foreach ($report_phonecall as $key=>$value) {
$phonecalls[$value['datum']] = $value['broj'];
};
foreach ($report_meeting as $key=>$value) {
$meetings[$value['datum']] = $value['broj'];
}
foreach ($report_notes as $key=>$value) {
$notes[$value['datum']] = $value['broj'];
}
That give me array
$phonecall = Array ( [2016-07-13] => 2 [2016-07-14] => 1 [2016-07-19] =>1 )
$meetings = Array ( [2016-07-13] => 1 [2016-07-14] => 1 )
$notes = Array ( [2016-07-19] => 1 )
I need to merge them into 1 array foreach date like this
Array(2016-07-13 => array([phonecalls]=>2, [meetings]=>1, [notes]=>0)) 2016-07-14 => array([phonecalls]=>1, [meetings]=> 1, [notes]=>0).... etc
I want to group/sort them by key value.
Going by
$group_reports[$value[key]] = $value['broj'][$phonecalls][$meetings][$notes]
Im not sure how to define it
How about like this?
$phonecall = ['2016-07-13' => 2, '2016-07-14' => 1, '2016-07-19' => 1];
$meetings = ['2016-07-13' => 1, '2016-07-14' => 1];
$notes = ['2016-07-19' => 1];
// Get *all* possible dates
$keys = array_unique(array_keys($phonecall+$meetings+$notes));
foreach($keys as $key) {
$final[$key] = [
'phonecalls' => isset($phonecall[$key]) ? $phonecall[$key] : 0,
'meetings' => isset($meetings[$key]) ? $meetings[$key] : 0,
'notes' => isset($notes[$key]) ? $notes[$key] : 0
];
}
Please use below code for merge array
$finalArr = array();
foreach($phonecall as $key=>$val){
$finalArr[$key]['phonecalls'] = $val;
$finalArr[$key]['meetings'] = 0;
$finalArr[$key]['notes'] = 0;
}
foreach($meetings as $key=>$val){
if(array_key_exists($key, $finalArr)){
$finalArr[$key]['meetings'] = $val;
} else {
$finalArr[$key]['phonecalls'] = 0;
$finalArr[$key]['meetings'] = $val;
$finalArr[$key]['notes'] = 0;
}
}
foreach($notes as $key=>$val){
if(array_key_exists($key, $finalArr)){
$finalArr[$key]['notes'] = $val;
} else {
$finalArr[$key]['phonecalls'] = 0;
$finalArr[$key]['meetings'] = 0;
$finalArr[$key]['notes'] = $val;
}
}

Is there something like keypath in an associative array in PHP?

I want to dissect an array like this:
[
"ID",
"UUID",
"pushNotifications.sent",
"campaigns.boundDate",
"campaigns.endDate",
"campaigns.pushMessages.sentDate",
"pushNotifications.tapped"
]
To a format like this:
{
"ID" : 1,
"UUID" : 1,
"pushNotifications" :
{
"sent" : 1,
"tapped" : 1
},
"campaigns" :
{
"boundDate" : 1,
"endDate" : 1,
"pushMessages" :
{
"endDate" : 1
}
}
}
It would be great if I could just set a value on an associative array in a keypath-like manner:
//To achieve this:
$dissected['campaigns']['pushMessages']['sentDate'] = 1;
//By something like this:
$keypath = 'campaigns.pushMessages.sentDate';
$dissected{$keypath} = 1;
How to do this in PHP?
You can use :
$array = [
"ID",
"UUID",
"pushNotifications.sent",
"campaigns.boundDate",
"campaigns.endDate",
"campaigns.pushMessages.sentDate",
"pushNotifications.tapped"
];
// Build Data
$data = array();
foreach($array as $v) {
setValue($data, $v, 1);
}
// Get Value
echo getValue($data, "campaigns.pushMessages.sentDate"); // output 1
Function Used
function setValue(array &$data, $path, $value) {
$temp = &$data;
foreach(explode(".", $path) as $key) {
$temp = &$temp[$key];
}
$temp = $value;
}
function getValue($data, $path) {
$temp = $data;
foreach(explode(".", $path) as $ndx) {
$temp = isset($temp[$ndx]) ? $temp[$ndx] : null;
}
return $temp;
}
function keyset(&$arr, $keypath, $value = NULL)
{
$keys = explode('.', $keypath);
$current = &$arr;
while(count($keys))
{
$key = array_shift($keys);
if(!isset($current[$key]) && count($keys))
{
$current[$key] = array();
}
if(count($keys))
{
$current = &$current[$key];
}
}
$current[$key] = $value;
}
function keyget($arr, $keypath)
{
$keys = explode('.', $keypath);
$current = $arr;
foreach($keys as $key)
{
if(!isset($current[$key]))
{
return NULL;
}
$current = $current[$key];
}
return $current;
}
//Testing code:
$r = array();
header('content-type: text/plain; charset-utf8');
keyset($r, 'this.is.path', 39);
echo keyget($r, 'this.is.path');
var_dump($r);
It's a little rough, I can't guarantee it functions 100%.
Edit: At first you'd be tempted to try to use variable variables, but I've tried that in the past and it doesn't work, so you have to use functions to do it. This works with some limited tests. (And I just added a minor edit to remove an unnecessary array assignment.)
In the meanwhile, I came up with (another) solution:
private function setValueForKeyPath(&$array, $value, $keyPath)
{
$keys = explode(".", $keyPath, 2);
$firstKey = $keys[0];
$remainingKeys = (count($keys) == 2) ? $keys[1] : null;
$isLeaf = ($remainingKeys == null);
if ($isLeaf)
$array[$firstKey] = $value;
else
$this->setValueForKeyPath($array[$firstKey], $value, $remainingKeys);
}
Sorry for the "long" namings, I came from the Objective-C world. :)
So calling this on each keyPath, it actually gives me the output:
fields
Array
(
[0] => ID
[1] => UUID
[2] => pushNotifications.sent
[3] => campaigns.boundDate
[4] => campaigns.endDate
[5] => campaigns.pushMessages.endDate
[6] => pushNotifications.tapped
)
dissectedFields
Array
(
[ID] => 1
[UUID] => 1
[pushNotifications] => Array
(
[sent] => 1
[tapped] => 1
)
[campaigns] => Array
(
[boundDate] => 1
[endDate] => 1
[pushMessages] => Array
(
[endDate] => 1
)
)
)

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

php array_multisort() by 2 field

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

Categories