php re-sorting an array - php

I have an array of entries that are separated by year and month like below. I need to sort this array so that entries are "grouped" by year and then by category.
Array
(
[Oct 2011] => Array
(
[0] => Array
(
[title] => CAD Drawing Updates
[file] => /gm-June2010-driver.pdf
[category] => Windows
)
)
[Sep 2011] => Array
(
[0] => Array
(
[title] => title
[file] => /gm-June2010-driver.pdf
[category] => Windows
)
[1] => Array
(
[title] => edges
[file] => /gm-June2010-driver.pdf
[category] =>Walling
)
[2] => Array
(
[title] => Specification update
[file] => /gm-June2010-driver.pdf
[category] => Windows
)
)
)
So this is the sort of thing I'm after.
Array
(
[Oct 2011] => Array
(
[Windows] => Array
(
[0] => Array
(
[title] => CAD Drawing Updates
[file] => /gm-June2010-driver.pdf
)
)
)
[Sep 2011] => Array
(
[Windows] => Array
(
[0] => Array
(
[title] => title
[file] => /gm-June2010-driver.pdf
[category] => Windows
)
[1] => Array
(
[title] => Specification update
[file] => /gm-June2010-driver.pdf
[category] => Windows
)
)
[Walling] => Array
(
[0] => Array
(
[title] => edges
[file] => /gm-June2010-driver.pdf
[category] => Curtain Walling
)
)
)
)
I'm not sure if this is a job for the sort functions, any help would be appreciated, thanks.

No, that can't be done with the sort function, you need to create a new array by iterating through your original array with nested foreach loops.
$newArr = array();
foreach($arr as $month => items) {
foreach($items as $data) {
$newArr[$month][$data["category"]][] = $data;
}
}

You don't want to sort but just to rename keys. PHP Arrays do not support to rename keys directly, so you need to create a new array first that is build with the new keys while maintaining the existing values.
At the end you can replace the original array with the new one:
$rekeyed = array();
foreach($array as $monthYear => &$value)
{
$r = sscanf($monthYear, '%s %d', $month, $year);
if ($r != 2) throw new Exception(sprintf('Invalid Key "%s".', $monthYear));
$categories = array();
foreach($value as &$item)
{
$category =& $item['category'];
unset($item['category']);
$categories[$category][] =& $item;
}
unset($item);
$value =& $categories;
unset($categories);
$rekeyed[$year][$month] =& $value;
}
unset($value);
$array =& $rekeyed;
unset($rekeyed);
print_r($array);
Output:
Array
(
[2011] => Array
(
[Oct] => Array
(
[Windows] => Array
(
[0] => Array
(
[title] => CAD Drawing Updates
[file] => /gm-June2010-driver.pdf
)
)
)
[Sep] => Array
(
[Windows] => Array
(
[0] => Array
(
[title] => title
[file] => /gm-June2010-driver.pdf
)
)
[Walling] => Array
(
[0] => Array
(
[title] => edges
[file] => /gm-June2010-driver.pdf
)
)
)
)
)
Demo

I think the function you should be looking at is uasort.
http://www.php.net/manual/en/function.uasort.php
First do a sort on the main array, the for each element in the array, do a usort on the child array.

Related

Transforming multidimensional arrays

As a stepping stone towards building a bit of json from which to build dependent dropdown, I'd like to transform this array...
Array
(
[0] => Array
(
[id] => 4
[project_id] => 2289
[task] => Drawing
)
[1] => Array
(
[id] => 5
[project_id] => 2289
[task] => Surveying
)
[2] => Array
(
[id] => 6
[project_id] => 2289
[task] => Meeting
)
[3] => Array
(
[id] => 1
[project_id] => 2282
[task] => Folding
)
[4] => Array
(
[id] => 2
[project_id] => 2282
[task] => Printing
)
[5] => Array
(
[id] => 3
[project_id] => 2282
[task] => Cutting
)
)
..to something like this...
Array
(
[0] = Array
(
[project_id] => 2289
[task] => Array
(
[0] => Drawing
[1] => Surveying
[2] => Meeting
)
)
[1] = Array
(
[project_id] => 2282
[task] => Array
(
[0] => Folding
[1] => Printing
[2] => Cutting
)
)
)
Using...
$newArray = array();
foreach ($array as $row)
{
$newArray[$row['project_id']][] = $row['task'];
}
...I'm able to get this...
Array
(
[2289] => Array
(
[0] => Drawing
[1] => Surveying
[2] => Meeting
)
[2282] => Array
(
[0] => Folding
[1] => Printing
[2] => Cutting
)
)
... but I've forgotten how to include the associative keys in the result
You can modify your foreach simply using a index:
$newArray = array();
$index = array();
foreach ($array as $row)
{
$found = array_search( $row['project_id'], $index );
if( $found === False )
{
$found = array_push( $newArray, array( 'project_id' => $row['project_id'] ) )-1;
$index[$found] = $row['project_id'];
}
$newArray[ $found ]['task'][] = $row['task'];
}
eval.in demo
When a new project_id key is found, it is added to $index array, so — searching for it at next loop — I can retrieve the index of corresponding multi-dimensional array.
Just assign them as you would like, the project id in an index, and task continually pushing it there:
$newArray = array();
foreach ($array as $row) {
$newArray[$row['project_id']]['project_id'] = $row['project_id'];
$newArray[$row['project_id']]['task'][] = $row['task'];
}
$newArray = array_values($newArray); // reindex
// removes `$row['project_id']` on each group
Note: Just use array_values to reset the grouping key that you used in the project id grouping.

How to remove branches that don't contain a certain value in a php array

I've spent the day playing with deceze's answer but I'm no closer to making it work. I may have part of it, but not sure how to get recursion in array_filter.
My Array looks like this (sample):
Array
(
[name] => root
[ChildCats] => Array
(
[0] => Array
(
[name] => Air Conditioning
[ChildCats] => Array
(
[0] => Array
(
[name] => Ducted Air Conditioning
[ChildCats] => Array
(
[0] => Array
(
[name] => Supply & Install
[ChildCats] => Array
(
[0] => Array
(
[name] => Daiken
[S] => 6067
)
)
)
[1] => Array
(
[name] => Supply Only
[ChildCats] => Array
(
[0] => Array
(
[name] => Mitsubishi
[S] => 6026
)
)
)
)
)
[1] => Array
(
[name] => Split System Air Conditioning
[ChildCats] => Array
(
[0] => Array
(
[name] => Supply & Install
[ChildCats] => Array
(
[0] => Array
(
[name] => Daiken
[S] => 6067
)
[1] => Array
(
[name] => Fujitsu Split Air Conditioning Systems
[S] => 6464
)
[2] => Array
(
[name] => Mitsubishi Electric Split Air Conditioning Systems
[S] => 6464
)
)
)
)
)
)
)
[1] => Array
(
[name] => Appliance / White Goods
[ChildCats] => Array
(
[0] => Array
(
[name] => Clearance
[S] => 6239
)
[1] => Array
(
[name] => Cooktops
[ChildCats] => Array
(
[0] => Array
(
[name] => Ceramic Cooktops
[S] => 6239
)
[1] => Array
(
[name] => Element Cooktops
[S] => 6067
)
[2] => Array
(
[name] => Gas Cooktops
[S] => 6239
)
[3] => Array
(
[name] => Induction Cooktops
[S] => 6239
)
)
)
)
)
Now lets say I try to extract just the parts of the array relevent to the following keypair:
S => 6067.
I'd like the result to look like:
Array
(
[name] => root
[ChildCats] => Array
(
[0] => Array
(
[name] => Air Conditioning
[ChildCats] => Array
(
[0] => Array
(
[name] => Ducted Air Conditioning
[ChildCats] => Array
(
[0] => Array
(
[name] => Supply & Install
[ChildCats] => Array
(
[0] => Array
(
[name] => Daiken
[S] => 6067
)
)
)
)
)
[1] => Array
(
[name] => Split System Air Conditioning
[ChildCats] => Array
(
[0] => Array
(
[name] => Supply & Install
[ChildCats] => Array
(
[0] => Array
(
[name] => Daiken
[S] => 6067
)
)
)
)
)
)
)
[1] => Array
(
[name] => Appliance / White Goods
[ChildCats] => Array
(
[0] => Array
(
[name] => Cooktops
[ChildCats] => Array
(
[0] => Array
(
[name] => Element Cooktops
[S] => 6067
)
)
)
)
)
)
)
What I cannot get my head arround is should I be creating a new array or using array filter.
Playing with deceze code I've got the search working using the following:
function recursive_assoc_in_array(array $haystack, array $needle, $childKey = 'ChildCats') {
if (array_intersect_assoc($haystack, $needle)) {
echo "Found via array_intersect_assoc ".$haystack[name]."\n";
return true;
}
foreach ($haystack[$childKey] as $child) {
if (recursive_assoc_in_array($child, $needle, $childKey)) return true;
}
return false;
}
But if I try to process with,
$array = array_filter($array, function (array $values) {
return recursive_assoc_in_array($values, array('S' => '6067'));
});
I get the original array which leads me to think I have to get recursion running on the array_filter query.
At this point I just go blank.
Additionally, the array keys will need to be reindexed on the produced new array. Any ideas?
--Additional 7/7/14
How about if I try to build a new array from the old one?
I'm trying:
function exploreArrayandAdd($Array) {
if ($Array['ChildCats']) {
foreach ($Array['ChildCats'] as $key => $value) {
$NewArray['ChildCats'][] = exploreArrayandAdd($value);
}
} else {
if ($Array['S'] == 6026) {
//echo "1";
return $Array;
}
}
}
But cannot work out how to pass the new array out of the function?
Tried removing branches that don't match using:
function exploreArray(&$Array) {
if ($Array['ChildCats']) {
foreach ($Array['ChildCats'] as $key => $value) {
$result = exploreArray($Array['ChildCats'][$key]);
if ($result === false)
unset($Array['ChildCats'][$key]);
}
} else {
// print_r($Array);
if ($Array['S'] == 6026) {
return true;
} else {
unset($Array);
return false;
}
}
//if ($NoChildCat==true) print_r($Array);
}
But I believe it is the wrong way as it does work at the bottom of the array but not back up towards the top as siblings make result true.
Also this won't reindex the array keys.
function recursive_array_filter(array $array, $childKey, callable $test) {
if (isset($array[$childKey]) && is_array($array[$childKey])) {
foreach ($array[$childKey] as $key => &$child) {
if (!$child = recursive_array_filter($child, $childKey, $test)) {
unset($array[$childKey][$key]);
}
}
if (!$array[$childKey]) {
unset($array[$childKey]);
}
}
return !empty($array[$childKey]) || $test($array) ? $array : [];
}
$array = recursive_array_filter($array, 'ChildCats', function (array $array) {
return array_intersect_assoc($array, ['S' => 6026]);
});
To express the algorithm in words: you descend down into the array first, following all ChildCats branches to their end. In each level you return the values as they are back to the caller if they match your test or if they have children, or you return an emptied array (you could also return false if you prefer). If some child turns out empty, you prune it with unset.
I have implemented the test as a callback function here for best reusability of the code.

Combine associative arrays php

How do I merge these associative arrays so that the indices ([0],[1]) are preserved and var_id, name and id are merged? I've tried array_combine and array_merge_recursive without succes.
Input
Array (
[0] => Array (
[var_id] => 43
)
[1] => Array (
[var_id] => 25
)
)
Array (
[0] => Array (
[name] => Tortoise
)
[1] => Array (
[name] => Black
)
)
Array (
[0] => Array (
[id] => 1907
)
[1] => Array (
[id] => 1908
)
)
Desired output
Array (
[0] => Array (
[var_id] => 43
[name] => Tortoise
[id] => 1907
)
[1] => Array (
[var_id] => 25
[name] => Black
[id] => 1908
)
)
Cheers,
Adnan
Assuming your three arrays are called $array1, $array2, and $array3 here's a loop that will do what you want:
foreach(array($array1, $array2, $array3) AS $array) {
foreach($array AS $key => $value) {
foreach($value AS $subkey => $subvalue) {
$final[$key][$subkey] = $subvalue;
}
}
}
Working example: http://3v4l.org/GY9oa
If you have an unknown number of input arrays to merge, it would be trivial to turn this into a function to handle that.

PHP Multidimensional sort?

I have a rather annoying array structure to work with and I need to sort it by any arbitrary key combination. 2 records are displayed below but multiple records with or without the same structure will be present when sorting is actioned.
Here are two records.
Array(
[0] => Array
(
[cid] => 1
[title] => Mr
[first_name] => Abet
[last_name] => Simbad
[emails] => Array
(
[374] => Array
(
[eid] => 374
[name] => ski lodge
[email] => simbad#skifree.com
)
[373] => Array
(
[eid] => 373
[name] => work
[email] => simbad#work.com
)
[375] => Array
(
[eid] => 375
[name] => personal
[email] => simbad#gmail.com
)
)
)
[1] => Array
(
[cid] => 2
[title] => Mrs
[first_name] => Angie
[last_name] => Stokes
[emails] => Array
(
[590] => Array
(
[eid] => 590
[name] => work
[email] => angie#gmail.com
)
)
)
So if I wanted to sort by email in ascending order in the emails array, how can I get the second complete record to come first in the result array? angie#gmail.com comes before simbad#....
Also Some records will not contain an emails array. They would be last in the result set.
Any help would be much appreciated.
The array shown is a cut down version but I have addresses, notes, phones and websites in the same annoying structure. Ideally I could sort with something like
$sort = array('emails','email')
$data = sort_data_func('ASC',$sort,$data);
But anything steps in the right direction will help. :)
Here's some code I have so far
$sort = array('emails','email');
foreach($contacts as $ckey => $c){
if(is_array($c[$sort[0]])){
foreach($c[$sort[0]] as $key1 => $sort0){
if($sort0[$sort[1]]!=''){
$res[$sort[0]][$ckey][$sort[1]][] = $sort0[$sort[1]];
}
}
}
}
print_r($res);
Which produces:
Array
(
[emails] => Array
(
[0] => Array
(
[0] => simbad#skifree.com
[1] => simbad#work.com
[2] => simbad#gmail.com
)
[1] => Array
(
[0] => angie#gmail.com
)
)
)
But I have no idea where to go from here.
EDIT
OK I have the records in the currect order now but how can I keep the initial record ID in the resulting array?
Here's what I'm using.
$direction=='ASC'
function cmp_asc($a, $b){
$key = current(array_keys($a));
sort($a[$key]);
$a[$key] = current($a[$key]);
sort($b[$key]);
$b[$key] = current($b[$key]);
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
function cmp_desc($a, $b){
$key = current(array_keys($a));
asort($a[$key]);
$a[$key] = current($a[$key]);
asort($b[$key]);
$b[$key] = current($b[$key]);
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
if($direction=='ASC'){
usort($res[$sort[0]], 'cmp_asc');
}else{
usort($res[$sort[0]], 'cmp_desc');
}
In
Array
(
[emails] => Array
(
[0] => Array
(
[email] => Array
(
[0] => simbad#skifree.com
[1] => simbad#work.com
[2] => simbad#gmail.com
)
)
[1] => Array
(
[email] => Array
(
[0] => angie#gmail.com
)
)
)
)
Out
Array
(
[emails] => Array
(
[0] => Array
(
[email] => Array
(
[0] => angie#gmnail.com
)
)
[1] => Array
(
[email] => Array
(
[0] => simbad#skifree.com
[1] => simbad#work.com
[2] => simbad#gmail.com
)
)
)
)
One of the usort functions, combined with a self-written comparison function that detects the order in which two elements should be sorted, should do the trick.

How can I flip an array when each value is also an array?

How would I go about flipping an array and establishing relationships between all the values and keys? For example:
I am trying to turn this:
Array (
[11913] => Array (
[0] => 4242
[1] => 3981
)
[9878] => Array (
[0] => 2901
[1] => 3981
)
[11506] => Array (
[0] => 3981
[1] => 2901
)
)
Into this:
Array (
[3981] => Array (
[0] => 11506
[1] => 9878
[2] => 11913
)
[2901] => Array (
[0] => 11506
[1] => 9878
)
[4242] => Array (
[0] => 11913
)
)
Are there any PHP functions that will already do this automatically? If not what would be a way of going about this? Can't seem to wrap my head around it.
Here you go.
$final_array = array();
foreach($initial_array as $key => $val){
foreach($val as $v){
$final_array[$v][] = $key;
}
}

Categories