I'm iterating over a folder and formatting its contents in a certain way.
I've to form an array from this set of strings:
home--lists--country--create_a_country.jpg
home--lists--country--list_countries.jpg
profile--edit--account.jpg
profile--my_account.jpg
shop--orders--list_orders.jpg
The array needs to look like this:
<?php
array(
'home' => array(
'lists' => array(
'country' => array(
'create_a_country.jpg',
'list_countries.jpg'
)
)
),
'profile' => array(
'edit' => array(
'account.jpg'
),
'my_account.jpg'
),
'shop' => array(
'orders' => array(
'list_orders.jpg',
)
);
The thing is, the depth of the array could be infinitely deep depending on how many '--' dividers the file name has. Here's what I've tried (assuming each string is coming from an array:
$master_array = array();
foreach($files as $file)
{
// Get the extension
$file_bits = explode(".", $file);
$file_ext = strtolower(array_pop($file_bits));
$file_name_long = implode(".", $file_bits);
// Divide the filename by '--'
$file_name_bits = explode("--", $file_name_long);
// Set the file name
$file_name = array_pop($file_name_bits).".".$file_ext;
// Grab the depth and the folder name
foreach($file_name_bits as $depth => $folder)
{
// Create sub-arrays as the folder structure goes down with the depth
// If the sub-array already exists, don't recreate it
// Place $file_name in the lowest sub-array
// .. I'm lost
}
}
Can anyone shed some light on how I might do this? All insight appreciated.
w001y
Try this:
$files=array("home--lists--country--create_a_country.jpg","home--lists--country--list_countries.jpg","profile--edit--account.jpg","profile--my_account.jpg","shop--orders--list_orders.jpg");
$master_array=array();
foreach($files as $file)
{
$file=explode("--",$file);
$cache=end($file);
while($level=prev($file))
{
$cache=array($level=>$cache);
}
$master_array=array_merge_recursive($master_array,$cache);
}
print_r($master_array);
Live demo
Related
I have a varying array for a playlist, containing media/source URLs for each item. Like this:
$playlist = array(
array(
"title" => "something",
"sources" => array(
array(
"file" => "https://url.somedomain.com/path/file1.mp3"
)
),
"description" => "somedesc",
"image" => "http://imagepath/",
"file" => "https://url.somedomain.com/path/file1.mp3"
),
array(
"title" => "elsewaa",
"sources" => array(
array(
"file" => "https://url.somedomain.com/someother/file2.mp3"
)
),
"description" => "anotherdesc",
"image" => "http://someotherimagepath/",
"file" => "https://url.somedomain.com/someother/file2.mp3"
)
);
How do I find and replace the values in the file keys to 'randomise' the choice of subdomain?
For example, if the file key contains url.foo.com, how do I replace the url.foo.com portion of the array value with either differentsubdomain.foo.com or anotherplace.foo.com or someotherplace.foo.com?
I was kindly offered a solution for a single string in this question/answer that used str_replace (thanks Qirel!), but I need a solution that tackles the above array configuration specifically.
All the nesting in the array does my head in!
Is it possible to adapt Qirel's suggestion somehow?
$random_values = ['differentsubdomain.foo.com', 'anotherplace.foo.com', 'someotherplace.foo.com'];
$random = $random_values[array_rand($random_values)];
// str_replace('url.foo.com', $random, $file);
If you are just asking how to access members in nested arrays, I think you want this:
$random_values = ['differentsubdomain.foo.com', 'anotherplace.foo.com', 'someotherplace.foo.com'];
// Iterate through the array, altering the items by reference.
foreach ($playlist as &$item) {
$random_key = array_rand($random_values);
$new_domain = $random_values[$random_key];
$item['file'] = str_replace('url.foo.com', $new_domain);
$item['sources'][0]['file'] = str_replace('url.foo.com', $new_domain);
}
Here's an example using recursion to replace the subdomains in any keys named file with a random one.
function replaceUrlHost(&$array, $hostDomain, $subdomains)
{
foreach ($array as $key => $value) {
if (is_array($value)) {
$array[$key] = replaceUrlHost($value, $hostDomain, $subdomains);
continue;
}
if ($key !== 'file') {
continue;
}
$hostname = parse_url($value, PHP_URL_HOST);
if (strpos($hostname, $hostDomain) === false) {
continue;
}
$array[$key] = str_replace(
$hostname,
$subdomains[array_rand($subdomains)] . '.' . $hostDomain,
$value
);
}
return $array;
}
// usage
$subdomains = ['bar', 'baz', 'bing', 'bop'];
$out = replaceUrlHost($playlist, 'somedomain.com', $subdomains);
I have an array like so:
$cars = array(
'type' => array(
'brand' => array(
'car' => 'Honda',
),
),
);
And I also have a string like so:
$path = "type][brand][car";
I'd like to return a value of car from $cars array using $path string, but of course this won't work:
echo $cars[$path];
The output I'd like to have is: "Honda". How this should be done?
Here is basicly what I understood you want to achieve in a simple function, that uses the parents array to get a nested value:
<?php
$cars = array(
'type' => array(
'brand' => array(
'car' => 'Honda',
),
),
);
$parents = array('type', 'brand', 'car');
// you could also do:
// $path = "type][brand][car";
// $parents = explode("][", $path);
function GetCar($cars, $parents) {
foreach($parents as $key) {
$cars = $cars[$key];
// echo $key."<br>";
}
return $cars;
}
var_dump(GetCar($cars, $parents)); // OUTPUT: string(5) "Honda"
echo GetCar($cars, $parents); // OUTPUT: Honda
A snippet: https://3v4l.org/OKrQN
I still think, that there is a better solution for what you need in a bigger picture (that I don't know)
Here is the correct answer, I am not just saying that as I've done this many times before. And I have really analyzed the problem etc...
$array = array(
'type' => array(
'brand' => array(
'car' => 'Honda',
),
),
);
$path = "type][brand][car";
function transverseGet($path, array $array, $default=null){
$path = preg_split('/\]\[/', $path, -1, PREG_SPLIT_NO_EMPTY);
foreach($path as $key){
if(isset($array[$key])){
$array = $array[$key];
}else{
return $default;
}
}
return $array;
}
print_r(transverseGet($path, $array));
Output
Honda
Sandbox
The trick is, every time you find a key from the path in the array you reduce the array to that element.
if(isset($array[$key])){
$array = $array[$key];
At the end you just return whatever is left, because your out of path parts to run so you can assume its the one you want.
If it doesn't find that key then it returns $default, you can throw an error there if you want.
It's actually pretty simple to do, I have this same setup for set,get,isset - key transversal
Like a Ninja ()>==<{>============>
If you require to use that specific structure you could do something like this:
$path = "type][brand][car";
$path_elements = explode("][", $path);
This way you get an array with each of the components required, and you'll have to do something like:
echo $cars[$path_elements[0]][$path_elements[1]][$path_elements[2]];
Of course that is a little bit too static and needs the same 3 level element structure.
May be this can resolve the problem :
strong text$cars = array( 'type' => array(
'brand' => array(
'car' => 'Honda',
), ), );
$path = "type][brand][car";
preg_match_all('/[a-z]+/', $path,$res);
$re = $res[0];
echo $cars[$res[0][$res[1]][$res[2]];
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.
This is my simple looper code
foreach( $cloud as $item ) {
if ($item['tagname'] == 'nicetag') {
echo $item['tagname'];
foreach( $cloud as $item ) {
echo $item['desc'].'-'.$item['date'];
}
} else
//...
}
I need to use if method in this looper to get tags with same names but diferent descriptions and dates. The problem is that I dont know every tag name becouse any user is allowed to create this tags.
Im not really php developer so I'm sory if it's to dummies question and thanks for any answers!
One possible solution is to declare a temporary variable that will hold tagname that is currently looped through:
$currentTagName = '';
foreach( $cloud as $item ) {
if ($item['tagname'] != $currentTagName) {
echo $item['tagname'];
$currentTagName = $item['tagname'];
}
echo $item['desc'] . '-' . $item['date'];
}
I presume that your array structure is as follows:
$cloud array(
array('tagname' => 'tag', 'desc' => 'the_desc', 'date' => 'the_date'),
array('tagname' => 'tag', 'desc' => 'the_desc_2', 'date' => 'the_date_2'),
...
);
BUT
This solution raises a problem - if your array is not sorted by a tagname, you might get duplicate tagnames.
So the better solution would be to redefine your array structure like this:
$cloud array(
'tagname' => array (
array('desc' => 'the_desc', 'date' => 'the_date'),
array('desc' => 'the_desc_2', 'date' => 'the_date_2')
),
'another_tagname' => array (
array('desc' => 'the_desc_3', 'date' => 'the_date_3'),
...
)
);
and then you can get the data like this:
foreach ($cloud as $tagname => $items) {
echo $tagname;
foreach($items as $item) {
echo $item['desc'] . '-' . $item['date'];
}
}
I have an array consisting of many other arrays, which might also consist of other arrays. Its basically like a navigation hierarchy, one menu link can be a menu with sub menus and so on.
The structure of $mainarray is like this:
'childarray1' => array(
'link' => array(
..
'mykey' => 'valueofinterest'
),
'below' => array() of childarrays
),
'childarray2' => array(
'link' => array(
..
'mykey' => 'somevalue'
)
),
'childarray3' => array(
'link' => array(
..
'mykey' => 'someothervalue'
),
'below' => array() of childarrays
)
Each childarray can have 2 direct child keys, 'links' and optionally 'below'. Within links there is always a key 'mykey', which is the only key that I need to check. If a child array has ['links']['mykey'] == 'valueofinterest', I'd like to have this element returned, like $sub = $mainarray['child1']['below']['child11']['below']['childofinterest'].
'below' means that the childarray has childs itself which can also have below arrays (sub menu..).
My hude problem is that the childarray I try to find can be in any other childarrays'S 'below' key, I dont know the depth (its not too deep, though it can vary). I've tried to mess with foreach loops and while loops and combining those, I just cant figure it out how to get the child array. I want to do it like this:
$value = 'xxx';
$sub = return_sub_menu($value);
function return_sub_menu($value) {
$array = $mainarray();
$sub = array();
// find the child array which's ['link']['mykey'] == $value;
// $sub is now something like:
// 'childarray321' => array(
// 'link' => array(
// ..
// 'mykey' => 'xxx'
// ),
// 'below' => array() of childarrays which i NEEED :)
//
// )
return $sub;
}
I've tried to walk recursively but cant figure out how to return the element :(
function recursiveSearch($array, $value){
foreach($array as $sub){
if ($sub['link']['mykey'] == $value)
return $sub ;
if (!empty($sub['below'])){
$returned = recursiveSearch($sub['below'], $value);
if ($returned !== null)
return $returned ;
}
}
return null ;
}
$sub = recursiveSearch($array, "valueofinterest");
//Returns array with ['link']['mykey'] == $value ;
var_dump($sub);
UPDATE V2
Fixed the function, so it works now
Try like this,
if (array_key_exists('keyvalue', $array)) {
$subarray = $array['keyvalue'];
return $subarray;
}
It will return sub array
here it is.
$finalarray = array_map(create_function('$yourarray', 'return $yourarray["arrayindex"];'), $actualarray );