I am having a problem appending a few options to an array of modules. I am using Opencart and trying to extend a module by adding an image. To do this and ensure that the code will not break anything in the future I wanted to add to the array instead of replace it.
This is the code I have so far:
if (isset($this->request->post['special_module'])) {
$modules = $this->request->post['special_module'];
} elseif ($this->config->get('special_module')) {
$modules = $this->config->get('special_module');
}
$this->load->model('tool/image');
foreach ($modules as $module) {
if (isset($module['image']) && file_exists(DIR_IMAGE . $module['image'])) {
$image = $module['image'];
} else {
$image = 'no_image.jpg';
}
array_push($module, array(
'image' => $image,
'thumb' => $this->model_tool_image->resize($image, 100, 100)
));
}
print_r($modules);exit;
$this->data['modules'] = $modules;
Print Array, no image or thumb:
Array
(
[0] => Array
(
[image_width] => 307
[image_height] => 234
[layout_id] => 1
[position] => column_right
[status] => 1
[sort_order] => 1
)
)
When I do array_push do I need to assign this back to the array?
$module is being overwritten by the foreach() loop every time it iterates. So your push is basically a null-op, becaus foreach will destroy the previous $module (that you pushed to) with the next $module value coming out of $modules. You'd need something more like this:
foreach($modules as &$module) {
...
$module['image'] = $image;
$module['thumb'] = ...;
}
The & before $module in the foreach turns it into a reference, so any modifications to $module within the loop will modify the original element in $modules, rather than a copy which would get trashed on every iteration.
$module, in your foreach loop is a copy of the contents. You will need to access it by reference, or push back into the actual array $modules.
Try modifying the foreach signature to the following:
foreach ($modules as &$module) {
try using array_merge instead of array_push
array_merge($module, array(
'image' => $image,
'thumb' => $this->model_tool_image->resize($image, 100, 100)
));
edit:
also, as print_r outputs the correct should be array_merge($module[0], array(...));
Related
I have a loop of the following kind:
foreach ($values as $key => $value) {
$attrs = array('NAME' => $key);
myproc ($attrs);
......
}
Where in myproc the first parameter is defined by reference:
function myproc (& attrs) { .... }
myproc adds the passed value to some structure.
The trouble with this is that at loop end, all the arrays added to the generated structure contains the same value, the last value extracted from the loop.
I tried also something like this :
foreach ($values as $key => $value) {
$attrs = array('NAME' => $key);
$copy = $attrs;
myproc ($copy);
......
}
but the result is the same. I'not allowed to modify the procedure. Any suggestions?
Based on the comment below your question, it seems that the problem is that you are passing a reference and this reference gets updated in the loop, leading to updates in the object you are generating in your function.
To avoid this, you need to unset the variable after the function call so that the link between the value in your object and the referenced variable is broken:
foreach ($values as $key => $value) {
$attrs = array('NAME' => $key);
myproc ($attrs);
// unset the variable so that newer values of it will have no effect
// on the object generated in `myproc`
unset($attrs);
......
}
Also see the manual.
<?php
foreach(['red','pink','green'] as $colour) {
$attrs = ['colour' => $colour];
if(colourToAction($attrs)) {
$results[] = $attrs;
}
}
var_export($results);
function colourToAction(&$attrs) {
$actions = ['red'=>'stop','amber'=>'wait', 'green' => 'go'];
if(isset($attrs['colour']) && isset($actions[$attrs['colour']])){
$attrs['action'] = $actions[$attrs['colour']];
return true;
}
}
Output:
array (
0 =>
array (
'colour' => 'red',
'action' => 'stop',
),
1 =>
array (
'colour' => 'green',
'action' => 'go',
),
)
I've tried to display this information tons of times, i've looked all over stackoverflow and just can't find an answer, this isn't a duplicate question, none of the solutions on here work. I've a json array which is stored as a string in a database, when it's taken from the database it's put into an array using json_decode and looks like this
Array
(
[0] => Array
(
[0] => Array
(
)
[1] => Array
(
[CanViewAdminCP] => Array
(
[Type] => System
[Description] => Grants user access to view specific page
[Colour] => blue
)
)
)
)
However, when i try to loop through this, it just returns nothing, I've tried looping using keys, i've tried foreach loops, nothing is returning the values, I'm looking to get the Array key so "CanViewAdminCP" and then the values inside that key such as "Type" and "Description".
Please can anybody help? thankyou.
Use a recursive function to search for the target key CanViewAdminCP recursively, as follows:
function find_value_by_key($haystack, $target_key)
{
$return = false;
foreach ($haystack as $key => $value)
{
if ($key === $target_key) {
return $value;
}
if (is_array($value)) {
$return = find_value_by_key($value, $target_key);
}
}
return $return;
}
Example:
print_r(find_value_by_key($data, 'CanViewAdminCP'));
Array
(
[Type] => System
[Description] => Grants user access to view specific page
[Colour] => blue
)
Visit this link to test it.
You have a 4 level multidimensional array (an array containing an array containing an array containing an array), so you will need four nested loops if you want to iterate over all keys/values.
This will output "System" directly:
<?php echo $myArray[0][1]['CanViewAdminCP']['Type']; ?>
[0] fetches the first entry of the top level array
[1] fetches the second entry of that array
['CanViewAdminCP'] fetches that keyed value of the third level array
['Type'] then fetches that keyed value of the fourth level array
Try this nested loop to understand how nested arrays work:
foreach($myArray as $k1=>$v1){
echo "Key level 1: ".$k1."\n";
foreach($v1 as $k2=>$v2){
echo "Key level 2: ".$k2."\n";
foreach($v2 as $k3=>$v3){
echo "Key level 3: ".$k3."\n";
}
}
}
Please consider following code which will not continue after finding the first occurrence of the key, unlike in Tommassos answer.
<?php
$yourArray =
array(
array(
array(),
array(
'CanViewAdminCP' => array(
'Type' => 'System',
'Description' => 'Grants user access to view specific page',
'Colour' => 'blue'
)
),
array(),
array(),
array()
)
);
$total_cycles = 0;
$count = 0;
$found = 0;
function searchKeyInMultiArray($array, $key) {
global $count, $found, $total_cycles;
$total_cycles++;
$count++;
if( isset($array[$key]) ) {
$found = $count;
return $array[$key];
} else {
foreach($array as $elem) {
if(is_array($elem))
$return = searchKeyInMultiArray($elem, $key);
if(!is_null($return)) break;
}
}
$count--;
return $return;
}
$myDesiredArray = searchKeyInMultiArray($yourArray, 'CanViewAdminCP');
print_r($myDesiredArray);
echo "<br>found in depth ".$found." and traversed ".$total_cycles." arrays";
?>
So I have the following situation. In my project folder I got a 'data' folder that contains .json files. These .json files also are structured in nested folders.
Something like:
/data
/content
/data1.json
/data2.json
/project
/data3.json
I'd like to create a function that recursively crawls through the data folder and stores all .json files in one multidimensional array, which makes it relatively easy to add static data for use for my project. So the expected result should be:
$data = array(
'content' => array(
'data1' => <data-from-data1.json>,
'data2' => <data-from-data2.json>
),
'project' => array(
'data3' => <data-from-data3.json>
)
);
UPDATE
I have tried the following, but this only returns the first level:
$data = array();
$directoryArray = scandir('./data');
foreach($directoryArray as $key => $value) {
$data[$key] = $value;
}
Is there a neat way to achieve this?
You should use RecursiveIteratorIterator. Skip some directories like . and .. . After this script loop other subdirectories.
//just to remove extension filename
function removeExtension($filename){
return preg_replace('/\\.[^.\\s]{3,4}$/', '', $filename);
}
$startpath= 'data';
$ritit = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($startpath), RecursiveIteratorIterator::CHILD_FIRST);
$result = [];
foreach ($ritit as $splFileInfo) {
if ($splFileInfo->getFilename() == '.') continue;
if ($splFileInfo->getFilename() == '..') continue;
if ($splFileInfo->isDir()){
$path = [removeExtension($splFileInfo->getFilename()) => []];
}else{
$path = [removeExtension($splFileInfo->getFilename()) => json_decode(file_get_contents($splFileInfo->getPathname(), $splFileInfo->getFilename()))];
}
for ($depth = $ritit->getDepth() - 1; $depth >= 0; $depth--) {
$path = [$ritit->getSubIterator($depth)->current()->getFilename() => $path];
}
$result = array_merge_recursive($result, $path);
}
print_r($result);
My json files contain:
data1.json: {"foo": "foo"}
data2.json: {"bar": "bar"}
data3.json: {"foobar": "foobar"}
The result is:
Array
(
[content] => Array
(
[data1] => stdClass Object
(
[foo] => foo
)
[data2] => stdClass Object
(
[bar] => bar
)
)
[project] => Array
(
[data3] => stdClass Object
(
[foobar] => foobar
)
)
)
You do not really have to use RecursiveIteratorIterator. As a programmer you should always know how to deal with recursive data structures, may it be an xml content, a folder tree or else. You may write a recursive function to handle such tasks.
Recursive functions are functions which call themselves to process through data with multiple layers or dimensions.
For example, scanFolder function below is designed to process contents of a directory, and it calls itself when it is encountered with a sub-directory.
function scanFolder($path)
{
echo "scanning dir: '$path'";
$contents = array_diff(scandir($path), ['.', '..']);
$result = [];
foreach ($contents as $item) {
$fullPath = $path . DIRECTORY_SEPARATOR . $item;
echo "processing '$fullPath'";
// process folder
if (is_dir($fullPath)) {
// process folder contents
$result[$item] = scanFolder($fullPath);
} else {
// for this specific program, you should perform a check here to see if the file is a json
// collect the result
$result[$item] = json_decode(file_get_contents($fullPath));
}
}
return $result;
}
IMO, this is a cleaner and more expressive way to accomplish this task and I wonder what others have to say about this statement.
I think that you can use RecursiveDirectoryIterator, there is an documentation about this class.
I have a following directory structure
test
directory_in_test
directory_in_directory_in_test
directory2_in_test
directory_in_directory2_in_test
abc.php
index.php
I am trying to make a function that will give a multidimensional array of sub-directories. Required output something like :
[directories] => Array(
[test] => Array(
[directory_in_test] => Array(
[directory_in_directory_in_test] => null
)
[directory2_in_test] => Array(
[directory_in_directory2_in_test] => null
)
)
)
I have tried to used RecursiveIteratorIterator with RecursiveDirectoryIterator but it give a one-level array of directories and files which is far from my requirement. Here is the code and result i have
code
<?php
public function findDirectories($path = '', $like = '')
{
$path = (is_dir($path)) ? $path : getcwd();
$directories = array();
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path));
foreach ($iterator as $directory) {
if($directory->isDir())
$directories[] = $directory->getPathName();
}
return $directories;
}
Result on printing $directories
Array
(
[0] => D:\xampp\htdocs\raheelwp\file-resolver\tests\.
[1] => D:\xampp\htdocs\raheelwp\file-resolver\tests\..
[2] => D:\xampp\htdocs\raheelwp\file-resolver\tests\directory2_in_test\.
[3] => D:\xampp\htdocs\raheelwp\file-resolver\tests\directory2_in_test\..
[4] => D:\xampp\htdocs\raheelwp\file-resolver\tests\directory2_in_test\directory_in_directory2_in_test\.
[5] => D:\xampp\htdocs\raheelwp\file-resolver\tests\directory2_in_test\directory_in_directory2_in_test\..
[6] => D:\xampp\htdocs\raheelwp\file-resolver\tests\directory_in_test\.
[7] => D:\xampp\htdocs\raheelwp\file-resolver\tests\directory_in_test\..
[8] => D:\xampp\htdocs\raheelwp\file-resolver\tests\directory_in_test\direcotry_in_directory_in_test\.
[9] => D:\xampp\htdocs\raheelwp\file-resolver\tests\directory_in_test\direcotry_in_directory_in_test\..
)
<?php
$it = new RecursiveDirectoryIterator(".", RecursiveDirectoryIterator::SKIP_DOTS);
$it = new RecursiveIteratorIterator($it);
$files = new RecursiveArrayIterator(array());
foreach ($it as $fi) {
$it = $files;
$dirs = explode('/', $fi->getPath());
foreach ($dirs as $path) {
if (isset($it[$path])) {
$it = $it[$path];
} else {
$it[$path] = new RecursiveArrayIterator();
}
}
$it[$fi->getFileName()] = $fi->getFileName();
}
$a = array();
createArray($a, $files);
print_r($a);
function createArray(&$a, $it) {
foreach ($it as $k => $tmp) {
if (is_string($tmp)) {
$a[] = $tmp;
} else {
$a[$k] = array();
createArray($a[$k], $tmp);
}
}
}
The code is fairly simple, and split in two parts even though it could easily be created in just one part. The first part will split the directories into separate RecursiveArrayIterators, so you keep the "iterator" capabilities to do all kind of other stuff with it. This is often useful when you are using the SPL iterators to begin with.
The second part, the createArray function basically uses an array reference to point to the "current" directory. Since it will be a multidimensional array, we do not have to worry about "where" in the array we actually are (it could be the 1st level, it might as well be the 100th level if your directory structure goes that deep). It just checks if the given element is a string, if so, it's a file, otherwise it's a directory so we recursively call the createArray again.
Might be easier solutions, but I reckon most of them uses a basic array-reference system nevertheless.
This question already has answers here:
How do you remove an array element in a foreach loop?
(6 answers)
Closed 6 years ago.
I have a foreach loop set up to go through my array, check for a certain link, and if it finds it removes that link from the array.
My code:
foreach($images as $image)
{
if($image == 'http://i27.tinypic.com/29yk345.gif' ||
$image == 'http://img3.abload.de/img/10nx2340fhco.gif' ||
$image == 'http://i42.tinypic.com/9pp2456x.gif')
{
unset($images[$image]);
}
}
But it doesn't remove the array entires. It's probably something to do with $images[$image], as that's not the key of the array entry, only the content? Is there a way to do this without incorporating a counter?
Thanks.
EDIT: Thanks guys, but now I have another problem where the array entries don't actually get deleted.
My new code:
foreach($images[1] as $key => $image)
{
if($image == 'http://i27.tinypic.com/29yk345.gif')
$image == 'http://img3.abload.de/img/10nx2340fhco.gif' ||
$image == 'http://i42.tinypic.com/9pp2456x.gif')
{
unset($images[$key]);
}
}
$images is actuallty a two-dimensional array now hence why I need $images[1]. I have checked and it successfully goes around the array elements, and some elements do actually have some of those URLs in that I wish to delete, but they're not getting deleted. This is my $images array:
Array
(
[0] => Array
(
[0] => useless
[1] => useless
[2] => useless
[3] => useless
[4] => useless
)
[1] => Array
(
[0] => http://i27.tinypic.com/29yk345.gif
[1] => http://img3.abload.de/img/10nx2340fhco.gif
[2] => http://img3.abload.de/img/10nx2340fhco.gif
[3] => http://i42.tinypic.com/9pp2456x.gif
)
)
Thanks!
foreach($images as $key => $image)
{
if(in_array($image, array(
'http://i27.tinypic.com/29ykt1f.gif',
'http://img3.abload.de/img/10nxjl0fhco.gif',
'http://i42.tinypic.com/9pp2tx.gif',
))
{
unset($images[$key]);
}
}
Try that:
foreach ($images[1] as $key => &$image) {
if (yourConditionGoesHere) {
unset($images[1][$key])
}
}
unset($image); // detach reference after loop
Normally, foreach operates on a copy of your array so any changes you make, are made to that copy and don't affect the actual array.
So you need to unset the values via $images[$key];
The reference on &$image prevents the loop from creating a copy of the array which would waste memory.
To answer the initial question (after your edit), you need to unset($images[1][$key]);
Now some more infos how PHP works:
You can safely unset elements of the array in foreach loop, and it doesn't matter if you have & or not for the array item. See this code:
$a=[1,2,3,4,5];
foreach($a as $key=>$val)
{
if ($key==3) unset($a[$key]);
}
print_r($a);
This prints:
Array
(
[0] => 1
[1] => 2
[2] => 3
[4] => 5
)
So as you can see, if you unset correct thing within the foreach loop, everything works fine.
You can use the index of the array element to remove it from the array, the next time you use the $list variable, you will see that the array is changed.
Try something like this
foreach($list as $itemIndex => &$item) {
if($item['status'] === false) {
unset($list[$itemIndex]);
}
}
$image is in your case the value of the item and not the key. Use the following syntax to get the key too:
foreach ($images as $key => $value) {
/* … */
}
Now you can delete the item with unset($images[$key]).
One solution would be to use the key of your items to remove them -- you can both the keys and the values, when looping using foreach.
For instance :
$arr = array(
'a' => 123,
'b' => 456,
'c' => 789,
);
foreach ($arr as $key => $item) {
if ($item == 456) {
unset($arr[$key]);
}
}
var_dump($arr);
Will give you this array, in the end :
array
'a' => int 123
'c' => int 789
Which means that, in your case, something like this should do the trick :
foreach($images as $key => $image)
{
if($image == 'http://i27.tinypic.com/29yk345.gif' ||
$image == 'http://img3.abload.de/img/10nx2340fhco.gif' ||
$image == 'http://i42.tinypic.com/9pp2456x.gif')
{
unset($images[$key]);
}
}
foreach($images as $key=>$image)
{
if($image == 'http://i27.tinypic.com/29ykt1f.gif' ||
$image == 'http://img3.abload.de/img/10nxjl0fhco.gif' ||
$image == 'http://i42.tinypic.com/9pp2tx.gif')
{ unset($images[$key]); }
}
!!foreach($images as $key=>$image
cause $image is the value, so $images[$image] make no sense.
You would also need a
$i--;
after each unset to not skip an element/
Because when you unset $item[45], the next element in the for-loop should be $item[45] - which was [46] before unsetting. If you would not do this, you'd always skip an element after unsetting.
Sorry for the late response, I recently had the same problem with PHP and found out that when working with arrays that do not use $key => $value structure, when using the foreach loop you actual copy the value of the position on the loop variable, in this case $image. Try using this code and it will fix your problem.
for ($i=0; $i < count($images[1]); $i++)
{
if($images[1][$i] == 'http://i27.tinypic.com/29yk345.gif' ||
$images[1][$i] == 'http://img3.abload.de/img/10nx2340fhco.gif' ||
$images[1][$i] == 'http://i42.tinypic.com/9pp2456x.gif')
{
unset($images[1][$i]);
}
}
var_dump($images);die();