I would like to append a string to the values of a nested associative array in PHP.
The following is my array
$styles = array(
'Screen' => array(
"master.css",
"jquery-jvectormap-1.0.css"),
'handheld' => array("mobile.css")
);
Looping over it to change it in the following way fails.
foreach($media as $medium => $filename)
foreach($filenames as &$filename)
$filename = "/styles/".$filename;
Prefixing $medium with & just causes a syntax error.
This also fails.
function prepend($prefix,$string)
{
return $prefix.$string;
}
foreach($media as &$medium)
$medium = array_map(prepend("/styles"),$medium);
What is the simplest way to prefix "/styles/" to those css filenames given this data structure?
Try this:
$styles = array(
'Screen' => array(
"master.css",
"jquery-jvectormap-1.0.css"),
'handheld' => array("mobile.css")
);
foreach($styles as $k=>$v){
foreach($v as $a=>$b){
$styles[$k][$a] = '/styles/'.$b;
}
}
print_r($styles);
Try this:
foreach($styles as $medium => &$filenames) {
foreach($filenames as &$filename) {
$filename = "/styles/".$filename;
}
}
Some tips:
Use braces to structure your nested code. This will give you (and php) a better understanding of what is you meant
Use Indenting. This will give you a better overview.
Name the Iterating-Variable using the singular of the Array-Variable (ie. foreach($apples as $apple)). This will give you an extra hint with which variable you're dealing with.
Good Luck!
foreach($media as $medium => $filenames) {
foreach($filenames as $key => $filename) {
$media[$medium][$key] = "/styles/" . $filename;
}
}
Related
Some time ago I had to parse nested data attributes to a JSON, so I found a JS solution here on SO. Eg.:
data-title="Title" data-ajax--url="/ajax/url" data-ajax--timeout="10" data-ajax--params--param-1="Param 1"
to
['title' => 'Title', 'ajax' => ['url' => '/ajax/url', 'timeout' => 10, 'params' => ['param-1' => 'Param 1']]]
So now I need a reverse action in PHP. I need to make attributes string from nested array to use it later in HTML. There can be infinite levels.
I've tried recursive functions. Tried recursive iterators. Still no luck. I always lose top level keys and get something like data-ajax--url=[...] --timeout=[...] --param-1=[...] (missing -ajax part) and so on. The part I can't get is the keys - getting values is easy. Any advice would be welcome.
This can be achieved with some simple concepts like loop,
recursive function and static variable.
Use of static variables is very important here since they remember the last modified value within the function's last call.
Within the loop, we are checking if the currently traversed value is an array.
If it's an array, we are modifying the prefix with the current key and calling the recursive function and .
If not, we are simply concatenating the prefix with the present key.
Try this:
$data = ['title' => 'Title', 'ajax' => ['url' => '/ajax/url', 'timeout' => 10, 'params' => ['param-1' => 'Param 1']]];
function formatter($data = array()) {
static $prefix = 'data-';
static $attr_string = '';
foreach($data as $key => $value) {
if (is_array($value)) {
$prefix .= $key.'--';
formatter($value);
} else {
$attr_string .= $prefix.$key.'="'.$value.'" ';
}
}
return $attr_string;
}
echo formatter($data);
Output:
data-title="Title" data-ajax--url="/ajax/url" data-ajax--timeout="10" data-ajax--params--param-1="Param 1"
So after almost 6 hours of trying to figure this out and lots of searches, also with some hints from Object Manipulator, I found this answer on SO and just had to adapt it to my needs:
function makeDataAttributes(array $attributes)
{
$rs = '';
$iterator = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($attributes));
foreach ($iterator as $key => $value) {
for ($i = $iterator->getDepth() - 1; $i >= 0; $i--) {
$key = $iterator->getSubIterator($i)->key() . '--' . $key;
}
$rs .= ' data-' . $key . '="' . $iterator->current() . '"';
}
return trim($rs);
}
Thanks everyone for your comments. It helped me to define my search more clearly. Also got some new knowledge about iterators.
I have a config.ini file that has a number of these types of variables:
site.social.twitter = "URL"
site.social.facebook = "URL"
site.social.google-plus = "URL"
I use PHP's built-in function to parse the INI.
$config = parse_ini_file(__DIR__ ."/../config.ini");
The next step is to get this (site.social.twitter)
to become an array.
$config['site']['social']['twitter']
You could just 'explode()', but it doesn't quite get there. Is there any simpler solutions to something like this?
OK, so you can look at using sections and the array syntax for INI files such as:
[site]
social[twitter] = URL
social[facebook] = URL
social[google-plus] = URL
Then pass true as the second argument:
$config = parse_ini_file(__DIR__ ."/../config.ini", true);
Or to build using your existing INI structure (adapted from How to access and manipulate multi-dimensional array by key names / path?):
$array = array();
foreach($config as $path => $value) {
$temp = &$array;
foreach(explode('.', $path) as $key) {
$temp =& $temp[$key];
}
$temp = $value;
}
$config = $array;
print_r($config);
Does simply referencing $config['site']['social'] get you the array you want? According to the documentation this should yield:
Array
(
[twitter] => "URL"
[facebook] => "URL"
[google-plus] => "URL"
)
I am having a bit of trouble trying to explain this correctly, so please bear with me...
I need to be able to recursively select keys based on a given array. I can do this via a fairly simple foreach statement (as shown below). However, I prefer to do things via PHP's built in functions whenever possible.
$selectors = array('plants', 'fruits', 'apple');
$list = array(
'plants' => array(
'fruits' => array(
'apple' => 'sweet',
'orange' => 'sweet',
'pear' => 'tart'
)
)
);
$select = $list;
foreach ($selectors as $selector) {
if (isset($select[$selector])) {
$select = $select[$selector];
} else {
exit("Error: '$selector' not found");
}
}
echo $select;
See this code in action
My Question: Is there a PHP function to recursively select array keys? If there is not, is there a better way than in the example above?
If i understand , you are searching about :
http://php.net/recursivearrayiterator
and
http://php.net/recursiveiteratoriterator
And code something like that:
$my_itera = new RecursiveIteratorIterator(new RecursiveArrayIterator($my_array));
$my_keys = array();
foreach ($my_itera as $my_key => $value) {
for ($i = $my_itera->getDepth() - 1; $i >= 0; $i--) {
$my_key = $my_itera->getSubIterator($i)->key() . '_' . $my_key;
}
$my_keys[] = $my_key;
}
var_export($my_keys);
I Hope it works.
I know I can add elemnts to an array like this:
$arr = array("foo" => "bar", 12 => true);
Now, how can I do that in a foreach using dynamic values? I have this code:
foreach ($values as $value) {
$imagePath = $value->getImagePath();
$dependsOn = $value->getDependsOn();
$dependsOn = explode(':', $dependsOn);
$dependsOnOptionValueTitle = trim($dependsOn[1]);
array_push($paths, $dependsOnOptionValueTitle => $imagePath); // not working
}
How can I add key/value pairs to my $paths array?
Instead of
array_push($paths, $dependsOnOptionValueTitle => $imagePath); // not working
you should be able to use
$paths[$dependsOnOptionValueTitle] = $imagePath;
From what I can see, this is what you're trying to do:
$paths[$dependsOnOptionValueTitle] = $imagePath;
Comment if I'm wrong and I'll try to fix it.
I have CSV file which contains a list of files and directories:
Depth;Directory;
0;bin
1;basename
1;bash
1;cat
1;cgclassify
1;cgcreate
0;etc
1;aliases
1;audit
2;auditd.conf
2;audit.rules
0;home
....
Each line depends on the above one (for the depth param)
I would like to create an array like this one in order to store it into my MongoDB collection with Materialized Paths
$directories = array(
array('_id' => null,
'name' => "auditd.conf",
'path' => "etc,audit,auditd.conf"),
array(....)
);
I don't know how to process...
Any ideas?
Edit 1:
I'm not really working with directories - it's an example, so I cannot use FileSystems functions or FileIterators.
Edit 2:
From this CSV file, I'm able to create a JSON nested array:
function nestedarray($row){
list($id, $depth, $cmd) = $row;
$arr = &$tree_map;
while($depth--) {
end($arr );
$arr = &$arr [key($arr )];
}
$arr [$cmd] = null;
}
But i'm not sure it's the best way to proceed...
This should do the trick, I think (it worked in my test, at least, with your data). Note that this code doesn't do much error checking and expects the input data to be in proper order (i.e. starting with level 0 and no holes).
<?php
$input = explode("\n",file_get_contents($argv[1]));
array_shift($input);
$data = array();
foreach($input as $dir)
{
if(count($parts = str_getcsv($dir, ';')) < 2)
{
continue;
}
if($parts[0] == 0)
{
$last = array('_id' => null,
'name' => $parts[1],
'path' => $parts[1]);
$levels = array($last);
$data[] = $last;
}
else
{
$last = array('id' => null,
'name' => $parts[1],
'path' => $levels[$parts[0] - 1]['path'] . ',' . $parts[1]);
$levels[$parts[0]] = $last;
$data[] = $last;
}
}
print_r($data);
?>
The "best" way to go would be to not store your data in CSV format, as it's the Wrong Tool For The Job.
That said, here you go:
<?php
$lines = file('/path/to/your/csv_file.csv');
$directories = array();
$path = array();
$lastDepth = NULL;
foreach ($lines as $line) {
list($depth, $dir) = str_getcsv($line, ';');
// Skip headers and such
if (!ctype_digit($depth)) {
continue;
}
if ($depth == $lastDepth) {
// If this depth is the same as the last, pop the last directory
// we added off the stack
array_pop($path);
} else if ($depth == 0) {
// At depth 0, reset the path
$path = array();
}
// Push the current directory onto the path stack
$path[] = $dir;
$directories[] = array(
'_id' => NULL,
'name' => $dir,
'path' => implode(',', $path)
);
$lastDepth = $depth;
}
var_dump($directories);
Edit:
For what it's worth, once you have the desired nested structure in PHP, it would probably be a good idea to use json_encode(), serialize(), or some other format to store it to disk again, and get rid of the CSV file. Then you can just use json_decode() or unserialize() to get it back in PHP array format whenever you need it again.