I have a nested array in PHP:
array (
'0' => "+5x",
'1' => array (
'0' => "+",
'1' => "(",
'2' => "+3",
'3' => array (
'0' => "+",
'1' => "(",
'2' => array ( // I want to find this one.
'0' => "+",
'1' => "(",
'2' => "+5",
'3' => "-3",
'4' => ")"
),
'3' => "-3",
'4' => ")"
),
'4' => ")"
)
);
I need to process the innermost arrays here, the one with the comment: "I want to find this one." Is there a function for that?
I have thought about doing (written as an idea, not as correct PHP):
foreach ($array as $id => $value) {
if ($value is array) {
$name = $id;
foreach ($array[$id] as $id_2 => $value_2) {
if ($value_2 is array) {
$name .= "." . $id_2;
foreach ($array[$id][$id_2] as $id_3 => $value_3) {
if ($value_3 is array) {
$name .= "." . $id_3;
foreach ($array[$id][$id_2][$id_3] as $id_4 => $value_4) {
if ($value_4 is array) {
$name .= "." . $id_4;
foreach [and so it goes on];
} else {
$listOfInnerArrays[] = $name;
break;
}
}
} else {
$listOfInnerArrays[] = $name;
break;
}
}
} else {
$listOfInnerArrays[] = $name;
break;
}
}
}
}
So what it does is it makes $name
the current key in the array. If the value is an array, it goes into it with foreach and adds "." and the id of the array. So we would in the example array end up with:
array (
'0' => "1.3.2",
)
Then I can process those values to access the innner arrays.
The problem is that the array that I'm trying to find the inner arrays of is dynamic and made of a user input. (It splits an input string where it finds + or -, and puts it in a separate nested array if it contains brackets. So if the user types a lot of brackets, there will be a lot of nested arrays.) Therefore I need to make this pattern go for 20 times down, and still it will only catch 20 nested arrays no matter what.
Is there a function for that, again? Or is there a way to make it do this without my long code? Maybe make a loop make the necessary number of the foreach pattern and run it through eval()
?
RecursiveIteratorIterator
knows the current depth of any children. As you're interested only in children that have children, filter those with no children out and look for max-depth.Then filter again based by depth for max-depth:
Demo / Source
Please, try the following code and let me know the results.
You just need to pass your array to the
find_deepest
function.The most important parts of the code are:
each
command inside thewhile
looparray_push
function call, where we store the current array in the "array stack" in order to return back to it laterarray_pop
function call, where we return one level back by restoring the current array from the "array stack"Recursive foreach function, from comments at: http://php.net/manual/en/control-structures.foreach.php
Note that the above implementation produces a flattened array. To preserve nesting:
To modify an array in-place:
Definitions
For an illustration of "inner" and "outer" nodes, consider:
3 and 7 are always outer nodes. 6 is outer relative to 5, but inner relative to 1.Answer
The difficulty here lies more in the uneven expression format than the nesting. If you use expression trees, the example
5x+3=(x+(3+(5-3)))
equation would parse to:Note that nodes for binary operations are binary, and unary operations would have unary nodes. If the nodes for binary commutative operations could be combined into n-ary nodes,
5x+3=x+3+5-3
could be parsed to:Then, you'd write a post-order recursive function that would simplify nodes. "Post-order" means node processing happens after processing its children; there's also pre-order (process a node before its children) and in-order (process some children before a node, and the rest after). What follows is a rough outline. In it, "thing : Type" means "thing" has type "Type", and "&" indicates pass-by-reference.
In a way, the real challenge is writing
simplify_node
. It could perform a number of operations on expression nodes:If a node and a child are the same commutative operation, the nodes could be rearranged. For example, there's rotation:
This corresponds to changing "(a+b)+c" to "a+(b+c)". You'll want to rotate when "a" doesn't match the constness of "b" and "c". It allows the next transformation to be applied to the tree. For example, this step would convert "(x+3)+1" to "x+(3+1)", so the next step could then convert it to "x+4".
The overall goal is to make a tree with const children as siblings. If a commutative node has two const descendants, they can be rotated next to each other. If a node has only one const descendent, make it a child so that a node further up in the hierarchy can potentially combine the const node with another of the ancestor's const children (i.e. const nodes float up until they're siblings, at which point they combine like bubbles in soda).
Handling nodes with more than one compound child and n-ary nodes left as exercises for the reader.
Object-Oriented Alternative
An OO approach (using objects rather than arrays to build expression trees) would have a number of advantages. Operations would be more closely associated with nodes, for one; they'd be a property of a node object, rather than as the node key. It would also be easier to associate ancillary data with expression nodes, which would be useful for optimizations. You probably wouldn't need to get too deep into the OOP paradigm to implement this. The following simple type hierarchy could be made to work:
Existing free functions that manipulate trees would become methods. The interfaces could look something like the following pseudocode. In it:
Child < Parent
means "Child" is a subclass of "Parent".isConstant
) can be methods or fields; in PHP, you can implement this using overloading.(...){...}
indicate functions, with the parameters between parentheses and the body between brackets (much likefunction (...){...}
in Javascript). This syntax is used for properties that are methods. Plain methods simply use brackets for the method body.Now for the sample:
The PHP implementation for
SimpleExpr
andConstantExpr
, for example, could be:An alternate implementation of
ConstantExpr
: