I have an array like this
array(2) {
["sys_ID"]=> string(32) "ab0ce921dba8a810f6db3892399619d9" ["sites"]=> array(5) {
[0]=> array(2) {
["sys_ID"]=> string(32) "448ce5a1dba8a810f6db3892399619ba" ["service"]=> string(4) "IDMB" } [1]=> array(2) {
["sys_ID"]=> string(32) "448ce5a1dba8a810f6db3892399619ba" ["service"]=> string(4) "ODMB" } [2]=> array(2) {
["sys_ID"]=> string(32) "598ce5a1dba8a810f6db3892399619bc" ["service"]=> string(4) "IDMB" } [3]=> array(2) {
["sys_ID"]=> string(32) "876ce5a1dba8a810f6db38923996199f" ["service"]=> string(4) "IDMB" } [4]=> array(2) {
["sys_ID"]=> string(32) "876ce5a1dba8a810f6db38923996199f" ["service"]=> string(4) "ODMB" } } }
If there is a duplicate ['sys_ID'] I want to change the first ['service'] => "IDMB,ODMB" then delete the duplicate value so there is only 1. So the above would become
array(2) {
["sys_ID"]=> string(32) "ab0ce921dba8a810f6db3892399619d9" ["sites"]=> array(5) {
[0]=> array(2) {
["sys_ID"]=> string(32) "448ce5a1dba8a810f6db3892399619ba" ["service"]=> string(4) "IDMB,ODMB" } [1]=> array(2) {
["sys_ID"]=> string(32) "598ce5a1dba8a810f6db3892399619bc" ["service"]=> string(4) "IDMB" } [2]=> array(2) {
["sys_ID"]=> string(32) "876ce5a1dba8a810f6db38923996199f" ["service"]=> string(4) "IDMB,ODMB" } ]} }
The first array was made getting POST values;
<?php
foreach ($_POST['services'] as $item) {
$parts = explode(',', $item);
$siteID = $parts[1];
$services = $parts[0];
$data['sites'][] = [
'sys_ID' => $siteID,
'service' => $services
];
}
?>
Change the way you generate your array to something like this:
<?php
$data = [
'sites' => [],
];
foreach ($_POST['services'] as $item) {
// Gather your data
$parts = explode(',', $item);
$siteID = $parts[1];
$services = $parts[0];
// Set flag to remember whether you need to add it
$add = true;
// Loop through existing data and check if the sys_ID already exists
foreach ($data['sites'] as &$dataDetails) {
// It does ... so just append your service
if ($dataDetails['sys_ID'] === $siteID) {
$add = false;
$dataDetails['service'] .= ',' . $services;
break;
}
}
// Couldn't find the sys_ID => Add new entry
if ($add) {
$data['sites'][] = [
'sys_ID' => $siteID,
'service' => $services
];
}
}
?>
It will be better to fill the array properly from the beginning; however I will still post a solution. We have the array defined like this:
$arr = array(
'sys_ID' => 'ab0ce921dba8a810f6db3892399619d9',
'sites' => array(
0 => array(
'sys_ID' => "448ce5a1dba8a810f6db3892399619ba",
'service'=> "IDMB"
),
1 => array(
'sys_ID' => "448ce5a1dba8a810f6db3892399619ba",
'service'=> "ODMB"
),
2 => array(
'sys_ID'=> "598ce5a1dba8a810f6db3892399619bc",
'service'=> "IDMB"
),
3 => array(
'sys_ID'=> "876ce5a1dba8a810f6db38923996199f",
'service'=> "IDMB"
),
4 => array(
'sys_ID'=> "876ce5a1dba8a810f6db38923996199f",
'service'=> "ODMB"
)
)
);
To merge the elements like described, you can do the following loop:
// For each site (1)...
for($i=0; $i<count($arr["sites"])-1; $i++){
// Take each of sites starting from the next one to the end (2)
for($j=$i+1; $j<count($arr["sites"]); $j++){
// If there is a match in sys_ID beteen (1) and (2)
if($arr["sites"][$i]['sys_ID'] == $arr["sites"][$j]['sys_ID']){
// Merge the service into (1) with comma separation
$arr["sites"][$i]['service'] = $arr["sites"][$i]['service'].",".$arr["sites"][$j]['service'];
// then delete (2)
array_splice($arr["sites"],$j,1);
$j--;
}
}
}
Note that this will reindex the numebers (1,2,3...). If you wish to preserve them, you can use unset() instead.
Related
I want to clone a Google Drive folder & all of its contents via Google's PHP API. After a bit of digging, posting, & then re-reading, I understand that the Drive API (V3) clearly states:
"Folders cannot be copied."
So, my plan is to:
Recursively traverse a respective Google Drive Folder ID & output its contents as an array
Create a new destination Drive Folder
Loop through the aforementioned array & based on the MIME type A) if a folder, create a new folder in my destination folder from my source parent folder B) if it's not a folder, copy the file C) loop again to move files / folders in my destination folder based on the original parents value(s).
What I have:
The API call all set up and a first pass of a recursive function that lists all of the folders / files
Where I'm getting stuck:
The tree/array I'm building creates a multidmensional/nested array, when I want it to be a single dimensional array
Every time I try to flatten my array to 'one level' it flattens everything down too much
My Question:
How can I create a recursive function that traverses all files/folders, but outputs into an array without nesting?
My code:
// Getting the Drive Files
// $copy_from_id = My 'source' Drive Folder
$driveService = new Google_Service_Drive($client);
$optParams = array(
'fields' => 'nextPageToken, files(*)',
'q' => "'$copy_from_id' in parents"
);
$files_arr = array();
$results = $driveService->files->listFiles($optParams);
if (count($results->getFiles()) !== 0) {
$files = $results->getFiles();
$files_arr = $this->recursivelyGetGDriveFolderContents($driveService, $files);
return $files_arr;
/*
* The code below flattens things 'too much',
* i.e. it's only a 1:1 array, when I really one a 'one-
* level-deep' multidimensional array where the values are an
* array. Plus I feel like I'm getting something wrong in my
* recursive function to begin with and should probably try
* to fix it there as opposed to processing again aftewards.
*/
// $result = array();
// array_walk_recursive($files_arr,function($v) use (&$result){ $result[] = $v; });
}
// My recursive function
function recursivelyGetGDriveFolderContents($driveService, $files){
$files_arr = array();
foreach ($files as $file) {
$file_id = $file->getId();
$file_name = $file->getName();
$file_type = $file->getMimeType();
$file_parents = $file->getParents();
$files_arr[] = array(
'file_name' => $file_name,
'file_type' => $file_type,
'file_parents' => $file_parents,
'file_id' => $file_id,
);
if($file_type == 'application/vnd.google-apps.folder'){
$optParams = array(
'fields' => 'nextPageToken, files(*)',
'q' => "'$file_id' in parents"
);
$results = $driveService->files->listFiles($optParams);
if (count($results->getFiles()) !== 0) {
$files_sub = $results->getFiles();
// I feel like this is the problem, this is causing nesting when I don't want it to
$files_arr[] = $this->recursivelyGetGDriveFolderContents($driveService, $files_sub);
}
}
}
return $files_arr;
}
My current array
array(4) {
[0]=>
array(4) {
["file_name"]=>
string(4) "D1L1"
["file_type"]=>
string(36) "application/vnd.google-apps.document"
["file_parents"]=>
array(1) {
[0]=>
string(33) "###"
}
["file_id"]=>
string(44) "###"
}
[1]=>
array(4) {
["file_name"]=>
string(4) "F2L1"
["file_type"]=>
string(34) "application/vnd.google-apps.folder"
["file_parents"]=>
array(1) {
[0]=>
string(33) "###"
}
["file_id"]=>
string(33) "###"
}
[2]=>
array(2) {
[0]=>
array(4) {
["file_name"]=>
string(4) "D1L2"
["file_type"]=>
string(36) "application/vnd.google-apps.document"
["file_parents"]=>
array(1) {
[0]=>
string(33) "###"
}
["file_id"]=>
string(44) "###"
}
[1]=>
array(4) {
["file_name"]=>
string(4) "F1L2"
["file_type"]=>
string(34) "application/vnd.google-apps.folder"
["file_parents"]=>
array(1) {
[0]=>
string(33) "###"
}
["file_id"]=>
string(33) "###"
}
}
[3]=>
array(4) {
["file_name"]=>
string(4) "F1L1"
["file_type"]=>
string(34) "application/vnd.google-apps.folder"
["file_parents"]=>
array(1) {
[0]=>
string(33) "###"
}
["file_id"]=>
string(33) "###"
}
}
My desired array
array(4) {
[0]=>
array(4) {
["file_name"]=>
string(4) "D1L1"
["file_type"]=>
string(36) "application/vnd.google-apps.document"
["file_parents"]=>
array(1) {
[0]=>
string(33) "1QsJXE_YfGIREOeD7VamzKn9_qsF__hFj"
}
["file_id"]=>
string(44) "1uyQbe4IHRppwuqWOAatiJtygISAXEqFZtyOKzp7qNW8"
}
[1]=>
array(4) {
["file_name"]=>
string(4) "F2L1"
["file_type"]=>
string(34) "application/vnd.google-apps.folder"
["file_parents"]=>
array(1) {
[0]=>
string(33) "1QsJXE_YfGIREOeD7VamzKn9_qsF__hFj"
}
["file_id"]=>
string(33) "1GY34PRVDU5yFXSGjYtwg_g7DoWdXWxab"
}
[2]=>
array(4) {
["file_name"]=>
string(4) "D1L2"
["file_type"]=>
string(36) "application/vnd.google-apps.document"
["file_parents"]=>
array(1) {
[0]=>
string(33) "1GY34PRVDU5yFXSGjYtwg_g7DoWdXWxab"
}
["file_id"]=>
string(44) "1oFLiM9TKHB2JQQOrFMBIQGjQnexRfFADk-x1ro07nKg"
}
[3]=>
array(4) {
["file_name"]=>
string(4) "F1L2"
["file_type"]=>
string(34) "application/vnd.google-apps.folder"
["file_parents"]=>
array(1) {
[0]=>
string(33) "1GY34PRVDU5yFXSGjYtwg_g7DoWdXWxab"
}
["file_id"]=>
string(33) "1TCUuNkPPzy1L-q5hS-obUdMlGrtDELAJ"
}
[4]=>
array(4) {
["file_name"]=>
string(4) "F1L1"
["file_type"]=>
string(34) "application/vnd.google-apps.folder"
["file_parents"]=>
array(1) {
[0]=>
string(33) "1QsJXE_YfGIREOeD7VamzKn9_qsF__hFj"
}
["file_id"]=>
string(33) "1JsI9kgESKQbKpaTWUwLDSFpj1VJPUuXL"
}
}
You have to remove $files_arr = array(); from your function because you have already initialize out of function and dont assign return value to $files_array of your recursive function.
your function will look like that
function recursivelyGetGDriveFolderContents($driveService, $files){
foreach ($files as $file) {
$file_id = $file->getId();
$file_name = $file->getName();
$file_type = $file->getMimeType();
$file_parents = $file->getParents();
$files_arr[] = array(
'file_name' => $file_name,
'file_type' => $file_type,
'file_parents' => $file_parents,
'file_id' => $file_id,
);
if($file_type == 'application/vnd.google-apps.folder'){
$optParams = array(
'fields' => 'nextPageToken, files(*)',
'q' => "'$file_id' in parents"
);
$results = $driveService->files->listFiles($optParams);
if (count($results->getFiles()) !== 0) {
$files_sub = $results->getFiles();
//i have remove array assign
$this->recursivelyGetGDriveFolders($driveService, $files_sub);
}
}
}
return $files_arr;
}
I made a few tweaks and have things 'working'. Specifically:
I'm adding child folder items to an array that passes into the function via arugment instead of setting it within the function (i.e. populating an array outside of the function as opposed to populating an array set within the funciton)
I added an inner loop to add recursive child content items to the array as opposed to adding the entire recursive function output to the array
To prevent duplicates I'm making sure I set the file id as the array key
I feel like my solution is unelegant and nonperformant (improvements definitely welcome!), but I did test until 5 folder levels deep and it's working the way I need it to.
My updated code:
// Getting the Drive Files
// $copy_from_id = My 'source' Drive Folder
$driveService = new Google_Service_Drive($client);
$optParams = array(
'fields' => 'nextPageToken, files(*)',
'q' => "'$copy_from_id' in parents"
);
$files_arr = array();
$results = $driveService->files->listFiles($optParams);
if (count($results->getFiles()) !== 0) {
$files = $results->getFiles();
$files_arr = $this->recursivelyGetGDriveFolderContents($driveService, $files);
return $files_arr;
}
// My recursive function
function recursivelyGetGDriveFolderContents($files_arr, $driveService, $files){
// Instead of setting my main array in this function I pass data into it via the function argument
foreach ($files as $file) {
$file_id = $file->getId();
$file_name = $file->getName();
$file_type = $file->getMimeType();
$file_parents = $file->getParents();
$return_file = array(
'file_name' => $file_name,
'file_type' => $file_type,
'file_parents' => $file_parents,
'file_id' => $file_id,
);
$files_arr[$file_id] = $return_file;
if($file_type == 'application/vnd.google-apps.folder'){
$optParams = array(
'fields' => 'nextPageToken, files(*)',
'q' => "'$file_id' in parents"
);
$results = $driveService->files->listFiles($optParams);
if (count($results->getFiles()) !== 0) {
$files_sub = $results->getFiles();
$files_sub_arr = $this->recursivelyGetGDriveFolderContents($files_arr, $driveService, $files_sub);
// I added this inner loop to add each child to the main parent array so that I didn't have nested arrays
foreach($files_sub_arr as $file_sub){
$file_sub_id = $file_sub['file_id'];
$files_arr[$file_sub_id] = $file_sub;
}
}
}
}
return $files_arr;
}
For example, I have this array:
$bills = array(
array("bill_id"=>"1", "product_id"=>"1", "total"=>"10"),
array("bill_id"=>"2", "product_id"=>"2", "total"=>"20"),
array("bill_id"=>"3", "product_id"=>"1", "total"=>"30"),
array("bill_id"=>"4", "product_id"=>"1", "total"=>"40"),
array("bill_id"=>"5", "product_id"=>"2", "total"=>"50")
);
We need to add the totals of each produdct into a single array, i.e. What is the best clean fast way to generate the following array from the above one:
$products = array(
array("product_id"=>"1", "total"=>"80"),
array("product_id"=>"2", "total"=>"70")
);
the fastest way to sum this is index array, something like this
$products = array();
foreach ($bills as $bill) {
$key = $bill['product_id'];
if (isset($products[$key])) {
$products[$key]['total'] += $bill['total'];
} else {
$products[$key] = $bill;
}
}
var_dump($products);
output
array(2) {
[1]=>
array(3) {
["bill_id"]=>
string(1) "1"
["product_id"]=>
string(1) "1"
["total"]=>
int(80)
}
[2]=>
array(3) {
["bill_id"]=>
string(1) "2"
["product_id"]=>
string(1) "2"
["total"]=>
int(70)
}
}
to browse the invoice list
foreach($products as $key=>$bill) {
var_dump($bill);
}
Thie simplest approach is a single-pass loop.
$byProduct = [];
foreach($bills as $bill)
{
$key = $bill['product_id'];
if (!isset($byProduct[$key])) {
$byProduct[$key] = [
'product_id' => $key,
'total' => 0
];
}
$byProduct[$key]['total'] += $bill['total'];
}
Result of var_dump($byProduct):
array(2) {
[1] =>
array(2) {
'product_id' =>
string(1) "1"
'total' =>
int(80)
}
[2] =>
array(2) {
'product_id' =>
string(1) "2"
'total' =>
int(70)
}
}
Another approach is to use array_walk but it is pretty much the same in terms of complexity:
$byProduct = [];
array_walk($bills, function(&$bill) use (&$byProduct) {
$key = $bill['product_id'];
if (!isset($byProduct[$key])) {
$byProduct[$key] = [
'product_id' => $key,
'total' => 0
];
}
$byProduct[$key]['total'] += $bill['total'];
});
i create function who return normal array :
function get_list_array () {
$list_object = get_list_objects();
foreach ( $list_object as $every_object) {
$list_array[] = array (
"wprm_$every_object->name" => array (
'name' => _x("$every_object->label", , 'test'),
'singular_name' => _x("$every_object->name", , 'test'),));
}
return $list_array ;
}
var_dump ($list_array);
array(2) {
[0]=> array(1) { ["object_1"]=> array(2) {
["name"]=> string(10)
"name_object1" ["singular_name"]=> string(15) "singular_name_object1" } }
[1]=> array(1) { ["object_2"]=> array(2) {
["name"]=> string(4)
"name_object2" ["singular_name"]=> string(10) "singular_name2" } } }
And i want the get in place just the associative array like this:
array ("object_1" => array (["name"]=> string(10) "name_object1"
["singular_name"]=> string(15) "singular_name_object1" } ,
"object_2" => array(2) {
["name"]=> string(4)
"name_object2" ["singular_name"]=> string(10) "singular_name2" } } }
any idea how i can modify my function in order the get the second output.
You're wrapping the array you actually want into another array by doing this:
$list_array[] = array(
"wprm_$every_object->name" => array(
Instead, you should simply assign the new array to $list_array directly:
$list_array["wprm_$every_object->name"] = array(
Also, please think about how you indent your code, because wow. Your function could look like this:
function get_list_array () {
$list_object = get_list_objects();
foreach ($list_object as $every_object) {
$list_array["wprm_$every_object->name"] = array(
'name' => _x("$every_object->label", , 'test'),
'singular_name' => _x("$every_object->name", , 'test'),
);
}
return $list_array;
}
I've got this array, and I want to loop through it and add up the values prices that are on the same OrderDate. The other values like the Discount code I want to add as a sub-array.
array(3) {
[0]=>
array(4) {
["OrderDate"]=>
string(10) "2018-01-01"
["DiscountCode"]=>
NULL
["TotalRevenue"]=>
string(9) "147618.76"
["Discount_Revenue"]=>
string(8) "13453.77"
}
[1]=>
array(4) {
["OrderDate"]=>
string(10) "2018-01-01"
["DiscountCode"]=>
string(6) "SALE38"
["TotalRevenue"]=>
string(8) "364.92"
["Discount_Revenue"]=>
string(8) "4083.64"
}
[2]=>
array(4) {
["OrderDate"]=>
string(10) "2018-01-01"
["DiscountCode"]=>
string(9) "WELCOME20"
["TotalRevenue"]=>
string(6) "113.83"
["Discount_Revenue"]=>
string(6) "113.83"
}
}
So it should then look like:
array(3) {
[0]=>
array(4) {
["OrderDate"]=>
string(10) "2018-01-01"
["DiscountCodes"]=> array {
[0] => "DISCOUNT"
[1] => "SALE38"
[2] => "WELCOME20"
)
["TotalRevenue"]=>
string(9) "147618.76"
["Discount_Revenue"]=>
string(8) "13453.77"
}
}
I believe I have fixed it using this loop adding to the array if the key exists. Not sure if this is the most efficient way to do it though?
foreach ($results as $k => $result){
if( array_key_exists($result['OrderDate'], $arr)){
$arr[$result['OrderDate']]['price'] += $result['TotalRevenue'];
$arr[$result['OrderDate']]['new'] = false;
} else {
$arr[$result['OrderDate']] = array(
'price' => $result['TotalRevenue'],
'new' => true
);
}
}
I've come to my own solution if anyone else needs it.
$arr = array();
foreach ($results as $k => $result){
if( array_key_exists($result['OrderDate'], $arr)){
$arr[$result['OrderDate']]['Total_Revenue'] += $result['TotalRevenue'];
$arr[$result['OrderDate']]['Discount_Revenue'] += $result['Discount_Revenue'];
isset($result['DiscountCode']) ? $arr[$result['OrderDate']]['Discount_Code'][] = $result['DiscountCode'] : '';
$arr[$result['OrderDate']]['new'] = false;
} else {
$arr[$result['OrderDate']] = array(
'Total_Revenue' => $result['TotalRevenue'],
'Discount_Revenue' => $result['Discount_Revenue'],
'new' => true
);
isset($result['DiscountCode']) ? $arr[$result['OrderDate']]['Discount_Code'][] = $result['DiscountCode'] : '';
}
}
i'm trying to sort an array by the value of a sub-key in DESC order but I'm stuck.
I've could make it with ksort but it was in ascending order..
Here's my array :
array_by_lang => array(
[no] => array(
[3-1] => array(//some informations),
[3-10] => array(//informations),
[3-7] => array(//informations),
[5-1] => array(//informations)
)
)
what i want to obtain is something like :
array_by_lang => array(
[no] => array(
[5-1] => array(//informations),
[3-10] => array(//some informations),
[3-7] => array(//informations),
[3-1] => array(//informations)
)
)
Is that possible ? Thanks a lot
I think, you need "reversing natural sort by key". Just with array_multisort and array_reverse (see also natsort):
$array_by_lang = array(
'no' => array(
'3-1' => array('info_1'),
'3-10' => array('info_2'),
'3-7' => array('info_3'),
'5-1' => array('info_4'),
)
);
array_multisort(array_keys($array_by_lang['no']),SORT_NATURAL, $array_by_lang['no']);
$array_by_lang['no'] = array_reverse($array_by_lang['no']); // reverse natural order - "DESC"
var_dump($array_by_lang);
Output
array(1) {
["no"]=>
array(4) {
["5-1"]=>
array(1) {
[0]=>
string(6) "info_4"
}
["3-10"]=>
array(1) {
[0]=>
string(6) "info_2"
}
["3-7"]=>
array(1) {
[0]=>
string(6) "info_3"
}
["3-1"]=>
array(1) {
[0]=>
string(6) "info_1"
}
}
}
This might help -
$a = array(
'3-1' => array('//some informations'),
'3-10' => array('//informations'),
'3-7' => array('//informations'),
'5-1' => array('//informations')
);
## Array for keys
$temp= array();
foreach(array_keys($a) as $v) {
$t = explode('-', $v);
$temp[$t[0]][] = $t[1];
}
## Sort the keys
foreach($temp as &$ar) {
rsort($ar);
}
krsort($temp);
## Final array
$final= array();
foreach($temp as $k => $f) {
foreach($f as $v) {
$key = $k . '-' . $v;
$final[$key] = $a[$key];
}
}
var_dump($final);
Output
array(4) {
["5-1"]=>
array(1) {
[0]=>
string(14) "//informations"
}
["3-10"]=>
array(1) {
[0]=>
string(14) "//informations"
}
["3-7"]=>
array(1) {
[0]=>
string(14) "//informations"
}
["3-1"]=>
array(1) {
[0]=>
string(19) "//some informations"
}
}
DEMO