Create infinitely deep multidimensional array from string in PHP [duplicate] - php

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
String with array structure to Array
I have a string "db/yum/user", and I'm trying to explode it so each element of / becomes a deeper dimension.
So the direct method of creating the data variable would be
$config['db']['yum']['user'] = "val";
My attempt so far:
$config = array();
function set_config($key,$value){
global $config;
//Multi deminsional config
$multi_configs = explode('/',$key);
if($multi_configs!==false){
$build_up = array();
$c =& $build_up;
foreach($multi_configs as $multi_config){
$c[$multi_config] = array();
$c =& $c[$multi_config];
}
//$c = $value;
array_merge($config,$c);
return;
}
$config[$key] = $value;
}
set_config('db/yum/user','val');
set_config('db/yum/server','val2');
//etc,etc,etc, this was modified to make more sense in this context.

This is probably what you are looking for:
#!/usr/bin/php
<?php
$config = array();
function set_config($key, $value) {
global $config;
if (FALSE=== ($levels=explode('/',$key)))
return;
$pointer = &$config;
for ($i=0; $i<sizeof($levels); $i++) {
if (!isset($pointer[$levels[$i]]))
$pointer[$levels[$i]]=array();
$pointer=&$pointer[$levels[$i]];
} // for
$pointer=$value;
} // set_config
set_config('db/yum/user','val');
set_config('db/yum/server','val2');
print_r($config);
?>
The output is:
Array
(
[db] => Array
(
[yum] => Array
(
[user] => val
[server] => val2
)
)
)

You can also achieve the same solution using a tree structure in the array . Here is the code to construct the array :
$arr = array (5,6);
$new_arr=array ();
$prev=0;
foreach ($arr as $val) {
$new_arr[$prev] = $val;
$prev=$val;
}
$new_arr[$prev]="value";
Here is the code to retrieve the value:
function retrieve ($arr) {
$prev=0;
while (1) {
if (! isset($arr[$prev] ) )
break;
else $prev = $arr[$prev];
}
return $prev;
}

Related

Ignore empty strings in array_merge [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 2 years ago.
Improve this question
I have 3 if statements (see code). Each for creating an array of email addresses. How can I merge those 3 strings into a $to (knowing that they could be empty or doesn't even exist)?
Apparently this doesn't work...
if ( in_array('client', $measurement_mail_report_recipients) ) {
$measurement_client_id = intval($_POST['acf']['field_5e147914518a6']);
$list1 = array();
if (have_rows('company_email_addresses', $measurement_client_id)) {
while (have_rows('company_email_addresses', $measurement_client_id)) {
the_row();
$list1[] = get_sub_field('company_email_address');
}
}
}
if ( in_array('contact', $measurement_mail_report_recipients) ) {
$measurement_contact_id = intval($_POST['acf']['field_5e149714d044e']);
$list2 = array();
if (have_rows('contact_email_addresses', $measurement_contact_id)) {
while (have_rows('contact_email_addresses', $measurement_contact_id)) {
the_row();
$list2[] = get_sub_field('contact_email_address');
}
}
}
if ( in_array('extra', $measurement_mail_report_recipients) ) {
$measurement_mail_extra_recipients = $_POST['acf']['field_5f71d4eaaf381'];
if ( $measurement_mail_extra_recipients ) {
$list3 = array();
foreach( $measurement_mail_extra_recipients as $measurement_mail_extra_recipient ) {
$list3[] = $measurement_mail_extra_recipient['field_5f71d55aaf382'];
}
}
}
$to = array_merge($list1, $list2, $list3);
Hopefully this helps you out:
Merge the arrays
Loop through the merged array
Check if current element is "" or null
If so delete it with array_splice()
I know what I forgot. I had to declare the array empty in case the if statement wasn't triggered. Check my working code below:
if ( in_array('client', $measurement_mail_report_recipients) ) {
$measurement_client_id = intval($_POST['acf']['field_5e147914518a6']);
$list1 = array();
if (have_rows('company_email_addresses', $measurement_client_id)) {
while (have_rows('company_email_addresses', $measurement_client_id)) {
the_row();
$list1[] = get_sub_field('company_email_address');
}
}
} else { $list1 = array(); }
if ( in_array('contact', $measurement_mail_report_recipients) ) {
$measurement_contact_id = intval($_POST['acf']['field_5e149714d044e']);
$list2 = array();
if (have_rows('contact_email_addresses', $measurement_contact_id)) {
while (have_rows('contact_email_addresses', $measurement_contact_id)) {
the_row();
$list2[] = get_sub_field('contact_email_address');
}
}
} else { $list2 = array(); }
if ( in_array('extra', $measurement_mail_report_recipients) ) {
$measurement_mail_extra_recipients = $_POST['acf']['field_5f71d4eaaf381'];
if ( $measurement_mail_extra_recipients ) {
$list3 = array();
foreach( $measurement_mail_extra_recipients as $measurement_mail_extra_recipient ) {
$list3[] = $measurement_mail_extra_recipient['field_5f71d55aaf382'];
}
}
} else { $list3 = array(); }
$recipients = array_merge($list1, $list2, $list3);
$to = array_filter($recipients);
$myArray = [];
$myArray2 = [];
// If undefined the value becomes false instead of error (always do this)
$myArray2["value1"] = $_POST["post-value"] ?? false;
// Always use isset to check if a variable is defined
if(isset($myArray["some-value"])){
// do some work
}
// proceed to remove an associated array or any variable
unset($myArray2["value1"]);// You can also unset any variable
PHP isset function returns true if a variable has been defined otherwise false, so use it to check if the array exists. To check if the array has value, use comparison operator like: sth equals another. to delete an associative array or any variable unset it as described above.
//Use this method
$array1 = [0,1,3,6];
$array2 = [];
$array3 = ["a","b","c"];
/*
Store all the collective array in one array
the array_1,2 style will help to resolve things inside for loop
*/
$biggerArray = ["array_1"=>$array1, "array_2"=>$array2, "array_3"=>$array3];
$bigger2 = [];
for($i = 0; $i < 3; $i++){
foreach($biggerArray["$i"] as $k => $v){
if(!$biggerArray["$i"][$k]){
// the value is empty
}else{
// it's not empty
if(!isset($bigger2["array_$i"]){
$bigger2["array_$i"] = [];
}else{
array_push($bigger2["array_$i"],$biggerArray["$i"][$k]);
}
}
}
}
// with function return $bigger2 array

Convert PHP array from XML that contains duplicate elements

Up until now, I've been using the snippet below to convert an XML tree to an array:
$a = json_decode(json_encode((array) simplexml_load_string($xml)),1);
..however, I'm now working with an XML that has duplicate key values, so the array is breaking when it loops through the XML. For example:
<users>
<user>x</user>
<user>y</user>
<user>z</user>
</users>
Is there a better method to do this that allows for duplicate Keys, or perhaps a way to add an incremented value to each key when it spits out the array, like this:
$array = array(
users => array(
user_1 => x,
user_2 => y,
user_3 => z
)
)
I'm stumped, so any help would be very appreciated.
Here is a complete universal recursive solution.
This class will parse any XML under any structure, with or without tags, from the simplest to the most complex ones.
It retains all proper values and convert them (bool, txt or int), generates adequate array keys for all elements groups including tags, keep duplicates elements etc etc...
Please forgive the statics, it s part of a large XML tools set I used, before rewriting them all for HHVM or pthreads, I havent got time to properly construct this one, but it will work like a charm for straightforward PHP.
For tags, the declared value is '#attr' in this case but can be whatever your needs are.
$xml = "<body>
<users id='group 1'>
<user>x</user>
<user>y</user>
<user>z</user>
</users>
<users id='group 2'>
<user>x</user>
<user>y</user>
<user>z</user>
</users>
</body>";
$result = xml_utils::xml_to_array($xml);
result:
Array ( [users] => Array ( [0] => Array ( [user] => Array ( [0] => x [1] => y [2] => z ) [#attr] => Array ( [id] => group 1 ) ) [1] => Array ( [user] => Array ( [0] => x [1] => y [2] => z ) [#attr] => Array ( [id] => group 2 ) ) ) )
Class:
class xml_utils {
/*object to array mapper */
public static function objectToArray($object) {
if (!is_object($object) && !is_array($object)) {
return $object;
}
if (is_object($object)) {
$object = get_object_vars($object);
}
return array_map('objectToArray', $object);
}
/* xml DOM loader*/
public static function xml_to_array($xmlstr) {
$doc = new DOMDocument();
$doc->loadXML($xmlstr);
return xml_utils::dom_to_array($doc->documentElement);
}
/* recursive XMl to array parser */
public static function dom_to_array($node) {
$output = array();
switch ($node->nodeType) {
case XML_CDATA_SECTION_NODE:
case XML_TEXT_NODE:
$output = trim($node->textContent);
break;
case XML_ELEMENT_NODE:
for ($i = 0, $m = $node->childNodes->length; $i < $m; $i++) {
$child = $node->childNodes->item($i);
$v = xml_utils::dom_to_array($child);
if (isset($child->tagName)) {
$t = xml_utils::ConvertTypes($child->tagName);
if (!isset($output[$t])) {
$output[$t] = array();
}
$output[$t][] = $v;
} elseif ($v) {
$output = (string) $v;
}
}
if (is_array($output)) {
if ($node->attributes->length) {
$a = array();
foreach ($node->attributes as $attrName => $attrNode) {
$a[$attrName] = xml_utils::ConvertTypes($attrNode->value);
}
$output['#attr'] = $a;
}
foreach ($output as $t => $v) {
if (is_array($v) && count($v) == 1 && $t != '#attr') {
$output[$t] = $v[0];
}
}
}
break;
}
return $output;
}
/* elements converter */
public static function ConvertTypes($org) {
if (is_numeric($org)) {
$val = floatval($org);
} else {
if ($org === 'true') {
$val = true;
} else if ($org === 'false') {
$val = false;
} else {
if ($org === '') {
$val = null;
} else {
$val = $org;
}
}
}
return $val;
}
}
You can loop through each key in your result and if the value is an array (as it is for user that has 3 elements in your example) then you can add each individual value in that array to the parent array and unset the value:
foreach($a as $user_key => $user_values) {
if(!is_array($user_values))
continue; //not an array nothing to do
unset($a[$user_key]); //it's an array so remove it from parent array
$i = 1; //counter for new key
//add each value to the parent array with numbered keys
foreach($user_values as $user_value) {
$new_key = $user_key . '_' . $i++; //create new key i.e 'user_1'
$a[$new_key] = $user_value; //add it to the parent array
}
}
var_dump($a);
First of all this line of code contains a superfluous cast to array:
$a = json_decode(json_encode((array) simplexml_load_string($xml)),1);
^^^^^^^
When you JSON-encode a SimpleXMLElement (which is returned by simplexml_load_string when the parameter could be parsed as XML) this already behaves as-if there would have been an array cast. So it's better to remove it:
$sxml = simplexml_load_string($xml);
$array = json_decode(json_encode($sxml), 1);
Even the result is still the same, this now allows you to create a subtype of SimpleXMLElement implementing the JsonSerialize interface changing the array creation to your needs.
The overall method (as well as the default behaviour) is outlined in a blog-series of mine, on Stackoverflow I have left some more examples already as well:
PHP convert XML to JSON group when there is one child (Jun 2013)
Resolve namespaces with SimpleXML regardless of structure or namespace (Oct 2014)
XML to JSON conversion in PHP SimpleXML (Dec 2014)
Your case I think is similar to what has been asked in the first of those three links.

php multi array create dynamically

I have q question: what is the easiest way to create multi-dimensional array in php dynamically?
Here a static version:
$tab['k1']['k2']['k3'] = 'value';
I would like to avoid eval()
I'm not successful with variable variable ($$)
so I'm trying to develop a function fun with such interface:
$tab = fun( $tab, array( 'k1', 'k2', 'k3' ), 'value' );
Do you have a solution? What is the simplest way?
regards,
Annie
There are a number of ways to achieve this, but here is one which uses PHP's ability to have N arguments passed to a function. This gives you the flexibility of creating an array with a depth of 3, or 2, or 7 or whatever.
// pass $value as first param -- params 2 - N define the multi array
function MakeMultiArray()
{
$args = func_get_args();
$output = array();
if (count($args) == 1)
$output[] = $args[0]; // just the value
else if (count($args) > 1)
{
$output = $args[0];
// loop the args from the end to the front to make the array
for ($i = count($args)-1; $i >= 1; $i--)
{
$output = array($args[$i] => $output);
}
}
return $output;
}
Here's how it would work:
$array = MakeMultiArray('value', 'k1', 'k2', 'k3');
And will produce this:
Array
(
[k1] => Array
(
[k2] => Array
(
[k3] => value
)
)
)
Following function will work for any number of keys.
function fun($keys, $value) {
// If not keys array found then return false
if (empty($keys)) return false;
// If only one key then
if (count($keys) == 1) {
$result[$keys[0]] = $value;
return $result;
}
// prepare initial array with first key
$result[array_shift($keys)] = '';
// now $keys = ['key2', 'key3']
// get last key of array
$last_key = end($keys);
foreach($keys as $key) {
$val = $key == $last_key ? $value : '';
array_walk_recursive($result, function(&$item, $k) use ($key, $val) {
$item[$key] = $val;
});
}
return $result;
}
This should work if $tab always has 3 indices:
function func(&$name, $indices, $value)
{
$name[$indices[0]][$indices[1]][$indices[2]] = $value;
};
func($tab, array( 'k1', 'k2', 'k3' ), 'value' );

Increasing array elements while in foreach loop in php? [duplicate]

This question already has answers here:
How does PHP 'foreach' actually work?
(7 answers)
Closed 8 years ago.
Consider the code below:
<?php
$arr = array();
$arr['b'] = 'book';
foreach($arr as $key=>$val) {
print "key=>$key\n";
if(!isset($arr['a']))
$arr['a'] = 'apple';
}
?>
It is not displaying 'a'. How foreach works with hash-table(array), to traverse each element. If lists are implement why can't I add more at run time ?
Please don't tell me that I could do this task with numeric based index with help of counting.
Foreach copies structure of array before looping(read more), so you cannot change structure of array and wait for new elements inside loop. You could use while instead of foreach.
$arr = array();
$arr['b'] = 'book';
reset($arr);
while ($val = current($arr))
{
print "key=".key($arr).PHP_EOL;
if (!isset($arr['a']))
$arr['a'] = 'apple';
next($arr);
}
Or use ArrayIterator with foreach, because ArrayIterator is not an array.
$arr = array();
$arr['b'] = 'book';
$array_iterator = new ArrayIterator($arr);
foreach($array_iterator as $key=>$val) {
print "key=>$key\n";
if(!isset($array_iterator['a']))
$array_iterator['a'] = 'apple';
}
I think you need to store array element continue sly
Try
<?php
$arr = array();
$arr['b'] = 'book';
foreach($arr as $key=>$val) {
print "key=>$key\n";
if(!isset($arr['a']))
$arr['a'][] = 'apple';
}
print_r($arr);
?>
In order to be able to directly modify array elements within the loop precede $value with &. In that case the value will be assigned by reference.
http://cz2.php.net/manual/en/control-structures.foreach.php
Try this:
You will get values.
<?php
$arr = array();
$arr['b'] = 'book';
foreach($arr as $key=>$val) {
print "key=>$key\n";
if(!isset($arr['a']))
$arr['a'] = 'apple';
}
echo '<pre>';
print_r($arr);
?>
Output:
key=>b
<pre>Array
(
[b] => book
[a] => apple
)
If you want to check key exist or not in array use array_key_exists function
Eg:
<?php
$arr = array();
$arr['b'] = 'book';
print_r($arr); // prints Array ( [b] => book )
if(!array_key_exists("a",$arr))
$arr['a'] = 'apple';
print_r($arr); // prints Array ( [b] => book [a] => apple )
?>
If you want to use isset condition try like this:
$arr = array();
$arr['b'] = 'book';
$flag = 0;
foreach($arr as $key=>$val) {
print "key=>$key\n";
if(!isset($arr["a"]))
{
$flag = 1;
}
}
if(flag)
{
$arr['a'] = 'apple';
}
print_r($arr);
How about using for and realtime array_keys()?
<?php
$arr = array();
$arr['b'] = 'book';
for ($x=0;$x<count($arr); $x++) {
$keys = array_keys($arr);
$key = $keys[$x];
print "key=>$key\n";
if(!isset($arr['a']))
$arr['a'] = 'apple';
}

Using func_get_args to edit an array

I wish to use a function with an arbitrary number of arguments to edit an array. The code I have so far is:
function setProperty()
{
$numargs = func_num_args();
$arglist = func_get_args();
$toedit = array();
for ($i = 0; $i < $numargs-1; $i++)
{
$toedit[] = $arglist[$i];
}
$array[] = $arglist[$numargs-1];
}
The idea of the code being I can do the following:
setProperty('array', '2nd-depth', '3rd', 'value1');
setProperty('array', 'something', 'x', 'value2');
setProperty('Another value','value3');
Resulting in the following array:
Array
(
[array] => Array
(
[2nd-depth] => Array
(
[3rd] => value1
)
[something] => Array
(
[x] => value2
)
)
[Another Value] => value3
)
The issue I believe is with the line:
$toedit[] = $arglist[$i];
What does this line need to be to achieve the required functionality?
Cheers,
You need to walk the path to the destination before storing the new value. You can do this with a reference:
function setProperty() {
$numargs = func_num_args();
if ($numargs < 2) return false; // not enough arguments
$arglist = func_get_args();
// reference for array walk
$ar = &$array;
// walk the array to the destination
for ($i=0; $i<$numargs-1; $i++) {
$key = $arglist[$i];
// create array if not already existing
if (!isset($ar[$key])) $ar[$key] = array();
// update array reference
$ar = &$ar[$key];
}
// add value
$ar = $arglist[$numargs-1];
}
But the question where this $array should be stored still remains.
class foo {
private $storage;
function setProperty()
{
$arglist = func_get_args();
if(count($argslist) < 2) return false;
$target = &$this->storage;
while($current = array_shift($arglist)){
if(count($arglist)==1){
$target[$current] = array_shift($arglist);
break;
}
if(!isset($target[$current])) $target[$current] = array();
$target = &$target[$current];
}
}
}
Try using foreach to loop through your array first. Then to handle children, you pass it to a child function that will grab everything.
I also recommend using the function sizeof() to determine how big your arrays are first, so you'll know your upper bounds.

Categories