I have a multi-dimensional array with key value pairs. Some of the values of the keys are arrays, and some of the values in that array are arrays as well. It is only 3 branches deep (for now), but I am trying to recursively loop through the array, save the key for each level of the branch, and create a new array when it reaches a string that also mimics the structure of the original array. When it reaches a string, there should be a new object instantiation for each value.
The code below sort of does this, but only creates a flat array.
foreach ($data as $key => $value) {
if (!is_array($value)) {
$a_objects[$key] = new Component([$key], $value);
} else {
foreach ($value as $valueKey => $valueValue) {
if (!is_array($valueValue)) {
$a_objects[$key . "_" . $valueKey] = new Component([$key, $valueKey], $valueValue);
} else {
foreach ($valueValue as $k => $v) {
$a_objects[$key . "_" . $valueKey . "_" . $k] = new Component([$key, $valueKey, $k], $v);
}
}
}
}
Here is my own best attempt at this but it does not seem to save into the b_objects after some testing it does not seem to be saving the object instances.
$b_objects = [];
function create_r($data, $id)
{
foreach ($data as $key => $value) {
if (is_array($value) == true) {
array_push($id, $key);
create_r($value, $id);
} else {
array_push($id, $key);
$b_objects[$key] = new Component($id, $value);
$id = [];
}
}
}
$id = [];
create_r($data, $id);
echo "this is b_objects";
echo "<pre>";
print_r($b_objects);
echo "</pre>";
I verified that $id array contains the right "keys". I feel like this solution is pretty close but I have no idea how to use my $id array to mimic the structure of the $data.
I want to be able to say $b_objects[$level1key][$level2key][$level3key]...[$leveln-1key] = new Component...
Thanks!
I suggest you pass $b_objects by reference to your create_r() function. Otherwise each function call will instantiate a new $b_objects variable which is not used anywhere in the code afterwards.
Passing by reference allows the function to actually change the input array. Notice the & sign in the function declaration.
function create_r($data, $id, &$res)
{
foreach ($data as $key => $value) {
if (is_array($value) == true) {
array_push($id, $key);
create_r($value, $id, $res);
} else {
array_push($id, $key);
$res[$key] = new Component($id, $value);
$id = [];
}
}
}
$id = [];
$b_objects = [];
create_r($data, $id, $b_objects);
Related
I building a translation form that is build using a array filled with the translation keys, to keys are however often duplicated. As the form is builded in group i want to move the duplicates to the $translatables['global']
Note that what you're seeing in the image below is already placed in $translatables['modules']['Module_{{ModuleName}}']
As you can see in the image below, some fields are duplicates. those needs to be moved. The duplicates can be placed accross multiple elements.
I edit for the duplicate message: It ain't, its a question to move those values not to filter them (and keep 1 remaining), they need to be placed under a global section.
I solved it myself
class Tools_Array
{
public static function find_duplicates_recursive(&$array, $remove = false)
{
$duplicates = array();
$valueCounts = array_count_values(self::array_values_recursive($array));
foreach ($valueCounts as $key => $value) {
if ($value > 1)
{
$duplicates[] = $key;
}
}
if ($remove) {
foreach ($duplicates as $duplicate)
{
$array = self::remove_values_recursive($array, $duplicate);
}
}
return $duplicates;
}
public static function array_values_recursive($array)
{
$arr2 = array();
foreach ($array as $key => $value)
{
if(is_array($value))
{
$arr2 = array_merge(array_values_recursive($value), $arr2);
}else{
$arr2[] = $value;
}
}
return $arr2;
}
public static function remove_values_recursive($array, $needle)
{
foreach ($array as $key => $value)
{
if(is_array($value))
{
$array[$key] = self::remove_values_recursive($value, $needle);
}else{
if ($value == $needle)
{
unset($array[$key]);
}
}
}
return $array;
}
}
Then i can filter it with:
$duplicates = Tools_Array::find_duplicates_recursive($translatables, true);
$translatables['globaal'] = array_merge($translatables['globaal'], $duplicates);
How would I go about accessing these elements from a Shopify JSON in PHP
JSON:
{
"orders":[
{ "note_attributes":[
{
"name":"field1",
"value":"xxxxxxxxxxxx"
},
{
"name":"field2",
"value":"xxxxxx"
},
{
"name":"field3",
"value":"xxxxxx"
}
],
This is just snippet of the object. How would I access each value and assign it to the name? Eg this $req['account_id'] would equal the value for that name tag. This is how am trying to do it but it's not working:
foreach ($order['note_attributes'] as $attributes) {
$req = array();
$req[$attributes->name] = $attributes[$attributes->value];
}
I would then like to echo $req['account_id'] and that would = xxxxxxxxx#gmail.com but it's not working any suggestions or fixes?
When iterating over Objects properties, one approach is to use (as you have) foreach
// accessing property
foreach ($objects as $object) {
echo $object->property;
}
// accessing key-value pair
foreach ($object as $key => $value) {
echo "$key => $value\n";
}
An example:
$attributes = array();
.
.
foreach($note_attributes as $note_attribute) {
$key = $note_attribute['name'];
$value = $note_attribute['value'];
$attributes[$key] = $value;
}
The structure should be in key-value pairs. ie.
attributes [
"account1234" => "abc#xxx.com",
"account_password" => "asdasdasd"
]
Hope this helps.
Update: as Roopendra mentioned, json_decode() with a TRUE flag with return an associative array, else stdClass.
$array['a:b']['c:d'] = 'test';
$array['a:b']['e:f']= 'abc';
I need output like below. array can have multiple level . Its comes with api so we do not know where colon come.
$array['ab']['cd'] = 'test';
$array['ab']['ef']= 'abc';
(untested code) but the idea should be correct if want to remove ':' from keys:
function clean_keys(&$array)
{
// it's bad to modify the array being iterated on, so we do this in 2 steps:
// find the affected keys first
// then move then in a second loop
$to_move = array();
forach($array as $key => $value) {
if (strpos($key, ':') >= 0) {
$target_key = str_replace(':','', $key);
if (array_key_exists($target_key, $array)) {
throw new Exception('Key conflict detected: ' . $key . ' -> ' . $target_key);
}
array_push($to_move, array(
"old_key" => $key,
"new_key" => $target_key
));
}
// recursive descent
if (is_array($value)) {
clean_keys($array[$key]);
}
}
foreach($to_move as $map) {
$array[$map["new_key"]] = $array[$map["old_key"]];
unset($array[$map["old_key"]]);
}
}
try this:
$array=array();
$array[str_replace(':','','a:b')][str_replace(':','','c:d')]="test";
print_r($array);
This seems like the simplest and most performant approach:
foreach ($array as $key => $val) {
$newArray[str_replace($search, $replace, $key)] = $val;
}
I've got this function:
$request = array("setNewDoc", "setNewFile");
foreach($request as $key => $val) {
$result = $controller->setNewDoc($key);
$smarty->assign('result', $result);
$index = TPL_DIR . 'docs.tpl';
}
Is it possible to make this loop dynamic with putting a variable in my pointer? The $keys in my array are also the name of the function.
Like $controller->$variable($key);
Like this(pseudo code)
$result = array("setNewDoc", "setNewFile");
foreach($request as $key => $val) {
$result = $controller->set$key($request);
$smarty->assign('result', $result);
// $key = setNewDoc or setNewFile
$index = TPL_DIR . $key . '.tpl';
}
Yes you can.
You can use it to cast a new object or to acces a property.
For exmaple
$classname = foo;
$prop=name;
$myObject = new $classname();
$myObject->{$prop} or a function: $myObject->{$prop}()
You can even foreach over an object's properties
foreach ($myObject as $key => $value) {
print $key // would be name
print $value // would be the value of the property
}
Yes that's possible and it is in fact pretty easy:
<?php
class MyClass {
function myMethod()
{
echo "Hello World";
}
}
$obj = new MyClass();
$dynamicVar = 'Method';
$obj->{'my' . $dynamicVar}();
enclose your method name with curly brackets and you can do some string concatenation inside. Once everything inside the the brackets is processed it will eventually used as your method name.
For your specific use case you would write something like
<?php
$result = array("setNewDoc", "setNewFile");
foreach($request as $key => $val) {
$result = $controller->{'set' . $key}($request);
$smarty->assign('result', $result);
// $key = setNewDoc or setNewFile
$index = TPL_DIR . $key . '.tpl';
}
Ok so I have an array that holds the following elements:
$array['a']['b'][0]['c'];
$array['a']['b'][1]['c'];
$array['a']['d'][0]['c']['c'];
$array['b']['c'];
Then in a separate array, I have defined the path to these values:
$structure[0] = array('a','b','#','c');
$structure[1] = array('a','d','#','c','c');
$structure[2] = array('b','c');
Finally, I have an array holding the values:
$values[0] = array('value0-0','value0-1');
$values[1] = array('value1-0');
$values[2] = array('value2-0');
I'm trying to find a simple function/loop that will be able to apply the values in $values to the array path of $array that is defined in $structure.
The end result would be:
$array['a']['b'][0]['c']='value0-0';
$array['a']['b'][1]['c']='value0-1';
$array['a']['d'][0]['c']['c']='value1-0';
$array['b']['c']='value2-0';
In the case of $values[0] or $values[1], it would be able to loop through each value and substitute the $structure element matching '#' with the iteration number for that particular $value.
Is this simply a case of knuckling down and writing a drawn out recursive function, or is there a smart construct or php function that could provide a more elegant solution?
SOLUTION:
Thanks to Mario, my eventual solution is:
foreach ($struct as $i=>$keys)
foreach ($values[$i] as $val) {
$r = & $array;
foreach ($keys as $key) {
if ($key == "#") { $key = $i; }
$r = & $r[$key]; // move pointer to subarray
}
$r = $val;
}
}
You will have to work with references to traverse the target array:
function inject($array, $struct, $values) {
foreach ($struct as $i=>$keys)
foreach ($values[$i] as $val) {
$r = & $array;
foreach ($keys as $key) {
if ($key == "#") { $key = count($r); }
settype($r[$key], "array");
$r = & $r[$key]; // move pointer to subarray
}
$r = $val;
}