Nested array from indented text
Moderator: General Moderators
Nested array from indented text
Hello,
I have an issue that I think has a simple solution but is driving me insane. Any help would be greatly appreciated.
To start, I have a single-dimension array similar to the following (it's output from "identify -verbose somefile.png"):
[text]
Array
(
[0] => Image: somefile.png
[1] => Format: PNG (Portable Network Graphics)
[2] => Class: DirectClass
[...]
[8] => Channel depth:
[9] => Red: 8-bit
[10] => Green: 8-bit
[11] => Blue: 8-bit
[12] => Alpha: 1-bit
[13] => Channel statistics:
[14] => Red:
[15] => Min: 0 (0)
[16] => Max: 255 (1)
[17] => Mean: 214.231 (0.84012)
[18] => Standard deviation: 67.8397 (0.266038)
)[/text]
My goal is to get this into a nested array, so I have something like:
[text]
Array
(
[Format] => PNG (Portable Network Graphics)
[Class] => DirectClass
[...]
[Channel depth] => Array
(
[Red] => 8-bit
[Green] => 8-bit
[Blue] => 8-bit
[Alpha] => 1-bit
)
[Channel statistics] => Array
(
[Red] => Array
(
[Min] => 0 (0)
[Max] => 255 (1)
[Mean] => 214.231 (0.84012)
[Standard deviation] => 67.8397 (0.266038)
)
)
)[/text]
I started by cycling through the single-dimension array, and exploding each value on ':' to get two elements, a key and a value. I then count the spaces before the key to determine the indent level. Basically, for each given line, I know the indent level, the key, and the value.
My problem is that I don't know how to recursively set an array from there.
Thank you.
I have an issue that I think has a simple solution but is driving me insane. Any help would be greatly appreciated.
To start, I have a single-dimension array similar to the following (it's output from "identify -verbose somefile.png"):
[text]
Array
(
[0] => Image: somefile.png
[1] => Format: PNG (Portable Network Graphics)
[2] => Class: DirectClass
[...]
[8] => Channel depth:
[9] => Red: 8-bit
[10] => Green: 8-bit
[11] => Blue: 8-bit
[12] => Alpha: 1-bit
[13] => Channel statistics:
[14] => Red:
[15] => Min: 0 (0)
[16] => Max: 255 (1)
[17] => Mean: 214.231 (0.84012)
[18] => Standard deviation: 67.8397 (0.266038)
)[/text]
My goal is to get this into a nested array, so I have something like:
[text]
Array
(
[Format] => PNG (Portable Network Graphics)
[Class] => DirectClass
[...]
[Channel depth] => Array
(
[Red] => 8-bit
[Green] => 8-bit
[Blue] => 8-bit
[Alpha] => 1-bit
)
[Channel statistics] => Array
(
[Red] => Array
(
[Min] => 0 (0)
[Max] => 255 (1)
[Mean] => 214.231 (0.84012)
[Standard deviation] => 67.8397 (0.266038)
)
)
)[/text]
I started by cycling through the single-dimension array, and exploding each value on ':' to get two elements, a key and a value. I then count the spaces before the key to determine the indent level. Basically, for each given line, I know the indent level, the key, and the value.
My problem is that I don't know how to recursively set an array from there.
Thank you.
Re: Nested array from indented text
Hmm.
trim() can probably get rid of the white space, which will help you somewhat. Then, split the string. Write into an array, where $result is the result of your explode: $array[$result[0]] = $result[1].
Does that help? You can also use things like strtolower() to fix formatting for your key.
trim() can probably get rid of the white space, which will help you somewhat. Then, split the string. Write into an array, where $result is the result of your explode: $array[$result[0]] = $result[1].
Does that help? You can also use things like strtolower() to fix formatting for your key.
Re: Nested array from indented text
Thanks for the reply, but I was already doing that.
The problem with $array[$result[0]] = $result[1] is that I'll still have a single-dimension array.
I want something like that, but where $array is the last node in the current chain, so that:
Key 1:
--Key 2:
----Key 3: Value
Becomes $array['Key 1']['Key 2']['Key 3'] = 'Value'
Any ideas?
The problem with $array[$result[0]] = $result[1] is that I'll still have a single-dimension array.
I want something like that, but where $array is the last node in the current chain, so that:
Key 1:
--Key 2:
----Key 3: Value
Becomes $array['Key 1']['Key 2']['Key 3'] = 'Value'
Any ideas?
Re: Nested array from indented text
$finalArray[$position][$key]=$value;
Something like that, where $position increments each time you loop through?
Something like that, where $position increments each time you loop through?
Re: Nested array from indented text
Sorry, that's still not it.
I wrote a script that hopefully better illustrates what I'm trying to do.
First, here's the full array that I'm starting with (sorry this is long, but I thought a full example might be useful):
[text]
This is the result of: exec('identify -verbose some_image.png' , $img_data);
Array
(
[0] => Image: some_image.png
[1] => Format: PNG (Portable Network Graphics)
[2] => Class: DirectClass
[3] => Geometry: 800x995+0+0
[4] => Type: TrueColorMatte
[5] => Endianess: Undefined
[6] => Colorspace: RGB
[7] => Depth: 8-bit
[8] => Channel depth:
[9] => Red: 8-bit
[10] => Green: 8-bit
[11] => Blue: 8-bit
[12] => Alpha: 1-bit
[13] => Channel statistics:
[14] => Red:
[15] => Min: 0 (0)
[16] => Max: 255 (1)
[17] => Mean: 214.231 (0.84012)
[18] => Standard deviation: 67.8397 (0.266038)
[19] => Green:
[20] => Min: 0 (0)
[21] => Max: 255 (1)
[22] => Mean: 203.409 (0.797681)
[23] => Standard deviation: 69.5713 (0.272829)
[24] => Blue:
[25] => Min: 0 (0)
[26] => Max: 255 (1)
[27] => Mean: 200.052 (0.784518)
[28] => Standard deviation: 77.8171 (0.305165)
[29] => Opacity:
[30] => Min: 0 (0)
[31] => Max: 0 (0)
[32] => Mean: 0 (0)
[33] => Standard deviation: 0 (0)
[34] => Rendering intent: Saturation
[35] => Gamma: 0.45455
[36] => Chromaticity:
[37] => red primary: (0.64,0.33)
[38] => green primary: (0.3,0.6)
[39] => blue primary: (0.15,0.06)
[40] => white point: (0.3127,0.329)
[41] => Resolution: 28.35x28.35
[42] => Units: PixelsPerCentimeter
[43] => Filesize: 220.906kb
[44] => Interlace: None
[45] => Background color: white
[46] => Border color: rgb(223,223,223)
[47] => Matte color: grey74
[48] => Transparent color: none
[49] => Page geometry: 800x995+0+0
[50] => Dispose: Undefined
[51] => Iterations: 0
[52] => Compression: Zip
[53] => Orientation: Undefined
[54] => Signature: 1518c127005b589fa58edddc32d21dff2d5711bade8258a93345f3c71163525b
[55] => Tainted: False
[56] => Version: ImageMagick 6.3.7 06/04/09 Q16 http://www.imagemagick.org
)
[/text]
And here's the code that kind of does what I want:
Which outputs:
[text]
Array
(
[0] => root.Image=some_image.png
[1] => root.Format=PNG (Portable Network Graphics)
[2] => root.Class=DirectClass
[3] => root.Geometry=800x995+0+0
[4] => root.Type=TrueColorMatte
[5] => root.Endianess=Undefined
[6] => root.Colorspace=RGB
[7] => root.Depth=8-bit
[8] => root.Channel depth.Red=8-bit
[9] => root.Channel depth.Green=8-bit
[10] => root.Channel depth.Blue=8-bit
[11] => root.Channel depth.Alpha=1-bit
[12] => root.Channel statistics.Red.Min=0 (0)
[13] => root.Channel statistics.Red.Max=255 (1)
[14] => root.Channel statistics.Red.Mean=214.231 (0.84012)
[15] => root.Channel statistics.Red.Standard deviation=67.8397 (0.266038)
[16] => root.Channel statistics.Green.Min=0 (0)
[17] => root.Channel statistics.Green.Max=255 (1)
[18] => root.Channel statistics.Green.Mean=203.409 (0.797681)
[19] => root.Channel statistics.Green.Standard deviation=69.5713 (0.272829)
[20] => root.Channel statistics.Blue.Min=0 (0)
[21] => root.Channel statistics.Blue.Max=255 (1)
[22] => root.Channel statistics.Blue.Mean=200.052 (0.784518)
[23] => root.Channel statistics.Blue.Standard deviation=77.8171 (0.305165)
[24] => root.Channel statistics.Opacity.Min=0 (0)
[25] => root.Channel statistics.Opacity.Max=0 (0)
[26] => root.Channel statistics.Opacity.Mean=0 (0)
[27] => root.Channel statistics.Opacity.Standard deviation=0 (0)
[28] => root.Rendering intent=Saturation
[29] => root.Gamma=0.45455
[30] => root.Chromaticity.red primary=(0.64,0.33)
[31] => root.Chromaticity.green primary=(0.3,0.6)
[32] => root.Chromaticity.blue primary=(0.15,0.06)
[33] => root.Chromaticity.white point=(0.3127,0.329)
[34] => root.Resolution=28.35x28.35
[35] => root.Units=PixelsPerCentimeter
[36] => root.Filesize=220.906kb
[37] => root.Interlace=None
[38] => root.Background color=white
[39] => root.Border color=rgb(223,223,223)
[40] => root.Matte color=grey74
[41] => root.Transparent color=none
[42] => root.Page geometry=800x995+0+0
[43] => root.Dispose=Undefined
[44] => root.Iterations=0
[45] => root.Compression=Zip
[46] => root.Orientation=Undefined
[47] => root.Signature=1518c127005b589fa58edddc32d21dff2d5711bade8258a93345f3c71163525b
[48] => root.Tainted=False
[49] => root.Version=ImageMagick 6.3.7 06/04/09 Q16 http
)
[/text]
The "dots" are merely to demonstrate what I'm trying to get with a PHP array.
So instead of element 12, for example, I want: $root[Channel statistics][Red][Min] = '0 (0)'
My primary problem is that while I can add and remove nodes onto a string, I don't know how to do so with an array (while keeping previous entries).
Thank you again.
I wrote a script that hopefully better illustrates what I'm trying to do.
First, here's the full array that I'm starting with (sorry this is long, but I thought a full example might be useful):
[text]
This is the result of: exec('identify -verbose some_image.png' , $img_data);
Array
(
[0] => Image: some_image.png
[1] => Format: PNG (Portable Network Graphics)
[2] => Class: DirectClass
[3] => Geometry: 800x995+0+0
[4] => Type: TrueColorMatte
[5] => Endianess: Undefined
[6] => Colorspace: RGB
[7] => Depth: 8-bit
[8] => Channel depth:
[9] => Red: 8-bit
[10] => Green: 8-bit
[11] => Blue: 8-bit
[12] => Alpha: 1-bit
[13] => Channel statistics:
[14] => Red:
[15] => Min: 0 (0)
[16] => Max: 255 (1)
[17] => Mean: 214.231 (0.84012)
[18] => Standard deviation: 67.8397 (0.266038)
[19] => Green:
[20] => Min: 0 (0)
[21] => Max: 255 (1)
[22] => Mean: 203.409 (0.797681)
[23] => Standard deviation: 69.5713 (0.272829)
[24] => Blue:
[25] => Min: 0 (0)
[26] => Max: 255 (1)
[27] => Mean: 200.052 (0.784518)
[28] => Standard deviation: 77.8171 (0.305165)
[29] => Opacity:
[30] => Min: 0 (0)
[31] => Max: 0 (0)
[32] => Mean: 0 (0)
[33] => Standard deviation: 0 (0)
[34] => Rendering intent: Saturation
[35] => Gamma: 0.45455
[36] => Chromaticity:
[37] => red primary: (0.64,0.33)
[38] => green primary: (0.3,0.6)
[39] => blue primary: (0.15,0.06)
[40] => white point: (0.3127,0.329)
[41] => Resolution: 28.35x28.35
[42] => Units: PixelsPerCentimeter
[43] => Filesize: 220.906kb
[44] => Interlace: None
[45] => Background color: white
[46] => Border color: rgb(223,223,223)
[47] => Matte color: grey74
[48] => Transparent color: none
[49] => Page geometry: 800x995+0+0
[50] => Dispose: Undefined
[51] => Iterations: 0
[52] => Compression: Zip
[53] => Orientation: Undefined
[54] => Signature: 1518c127005b589fa58edddc32d21dff2d5711bade8258a93345f3c71163525b
[55] => Tainted: False
[56] => Version: ImageMagick 6.3.7 06/04/09 Q16 http://www.imagemagick.org
)
[/text]
And here's the code that kind of does what I want:
Code: Select all
<?php
exec('identify -verbose some_image.png',$img_data);
$chain = 'root';
// $chain will be added on to so that an entry might look like: root.Channel statistics.Green.Standard deviation=69...
// But instead of dots I want nested arrays: $root[Channel statistics][Green][Standard deviation] = 69
foreach($img_data as $line_number=>$line) {
$line_parts = explode(':',$line);
// determine number of spaces that line is indented
$indent = 0;
while($line_parts[0][$indent] == ' ')
$indent ++;
// $indent is now the quantity of leading spaces
$indent_diff = ($indent - $last_indent) / 2; // $indent_diff is difference from last indent (2 spaces per indentation)
/*
Example:
LINE ONE //$indent_diff = 0
LINE TWO //$indent_diff = +1
LINE THREE //$indent_diff = +1
LINE FOUR //$indent_diff = -2
*/
// trim key and value
foreach($line_parts as $part=>$value) $line_parts[$part] = trim($value);
if($indent_diff < 0) { // remove nodes from chain
for($remove_i = $indent_diff; $remove_i < 0; $remove_i ++) {
$chain = substr($chain,0,strrpos($chain,'.'));
}
}
if($line_parts[1] == '') { // line does not have a value, so add a node
$chain .= '.' . $line_parts[0] ;
}
else { // line has a value, make an entry in $final, where $line_parts[0] is the last node
$final[] = $chain . '.' . $line_parts[0] . '=' . $line_parts[1];
}
$last_indent = $indent; // set for next iteration
}
print_r($final);
?>
[text]
Array
(
[0] => root.Image=some_image.png
[1] => root.Format=PNG (Portable Network Graphics)
[2] => root.Class=DirectClass
[3] => root.Geometry=800x995+0+0
[4] => root.Type=TrueColorMatte
[5] => root.Endianess=Undefined
[6] => root.Colorspace=RGB
[7] => root.Depth=8-bit
[8] => root.Channel depth.Red=8-bit
[9] => root.Channel depth.Green=8-bit
[10] => root.Channel depth.Blue=8-bit
[11] => root.Channel depth.Alpha=1-bit
[12] => root.Channel statistics.Red.Min=0 (0)
[13] => root.Channel statistics.Red.Max=255 (1)
[14] => root.Channel statistics.Red.Mean=214.231 (0.84012)
[15] => root.Channel statistics.Red.Standard deviation=67.8397 (0.266038)
[16] => root.Channel statistics.Green.Min=0 (0)
[17] => root.Channel statistics.Green.Max=255 (1)
[18] => root.Channel statistics.Green.Mean=203.409 (0.797681)
[19] => root.Channel statistics.Green.Standard deviation=69.5713 (0.272829)
[20] => root.Channel statistics.Blue.Min=0 (0)
[21] => root.Channel statistics.Blue.Max=255 (1)
[22] => root.Channel statistics.Blue.Mean=200.052 (0.784518)
[23] => root.Channel statistics.Blue.Standard deviation=77.8171 (0.305165)
[24] => root.Channel statistics.Opacity.Min=0 (0)
[25] => root.Channel statistics.Opacity.Max=0 (0)
[26] => root.Channel statistics.Opacity.Mean=0 (0)
[27] => root.Channel statistics.Opacity.Standard deviation=0 (0)
[28] => root.Rendering intent=Saturation
[29] => root.Gamma=0.45455
[30] => root.Chromaticity.red primary=(0.64,0.33)
[31] => root.Chromaticity.green primary=(0.3,0.6)
[32] => root.Chromaticity.blue primary=(0.15,0.06)
[33] => root.Chromaticity.white point=(0.3127,0.329)
[34] => root.Resolution=28.35x28.35
[35] => root.Units=PixelsPerCentimeter
[36] => root.Filesize=220.906kb
[37] => root.Interlace=None
[38] => root.Background color=white
[39] => root.Border color=rgb(223,223,223)
[40] => root.Matte color=grey74
[41] => root.Transparent color=none
[42] => root.Page geometry=800x995+0+0
[43] => root.Dispose=Undefined
[44] => root.Iterations=0
[45] => root.Compression=Zip
[46] => root.Orientation=Undefined
[47] => root.Signature=1518c127005b589fa58edddc32d21dff2d5711bade8258a93345f3c71163525b
[48] => root.Tainted=False
[49] => root.Version=ImageMagick 6.3.7 06/04/09 Q16 http
)
[/text]
The "dots" are merely to demonstrate what I'm trying to get with a PHP array.
So instead of element 12, for example, I want: $root[Channel statistics][Red][Min] = '0 (0)'
My primary problem is that while I can add and remove nodes onto a string, I don't know how to do so with an array (while keeping previous entries).
Thank you again.
Re: Nested array from indented text
Well, you really just basically wrote it yourself:
$root['Channel statistics']['Red']['Min'] = '0 (0)'; should work just fine. It's not efficient, but it should work.
$root['Channel statistics']['Red']['Min'] = '0 (0)'; should work just fine. It's not efficient, but it should work.
Re: Nested array from indented text
But each key ('Channel statistics', 'Red', 'Min') is dynamic, as well as how many keys there are (the original array is the result of running a system script, I have no control over how it's initial format).
You did give me an idea, though, that perhaps I could do:
$root[$node1][$node2][$node3] = $value;
but the number of level/nodes change.
I suppose I could then do:
Because with this data there will only be a maximum of 3 nodes I think this would be plausible, but it seems kind of messy.
I think there's a better way to do this, that would allow for an unlimited number of levels. I've seen similar with scripts that build a nested array of a file system, but I can't figure this one out.
You did give me an idea, though, that perhaps I could do:
$root[$node1][$node2][$node3] = $value;
but the number of level/nodes change.
I suppose I could then do:
Code: Select all
switch($levels)
case 1:
$root[$node1] = $value;
break;
case 2:
$root[$node1][$node2] = $value
break;
// etc...
}
I think there's a better way to do this, that would allow for an unlimited number of levels. I've seen similar with scripts that build a nested array of a file system, but I can't figure this one out.
Re: Nested array from indented text
Oh! I finally caught on. If you counted the spaces in a loop, which paused every time it hit 0, and used that number of spaces to count the times in a loop that you would need to write the array? In other words, would the number of spaces be able to tell you how many entries deep the array needs to be to hold the information?
Re: Nested array from indented text
No worries - I'm having a hard time even explaining what it is that I'm trying to do.
In answer to your question, yes, the number of spaces indicates the depth of the array (kind of, the output is actually 2 spaces per level, but I can just divide by 2). If it is indented from the previous entry, it should be part of the previous node (and will never be indented more than one level from the previous). If it is out-dented, it should "back out" by how many indents there are, so 4 spaces, or 2 indents, would be 2 levels less deep than the prior entry. I hope that makes some sense.
Thanks again.
In answer to your question, yes, the number of spaces indicates the depth of the array (kind of, the output is actually 2 spaces per level, but I can just divide by 2). If it is indented from the previous entry, it should be part of the previous node (and will never be indented more than one level from the previous). If it is out-dented, it should "back out" by how many indents there are, so 4 spaces, or 2 indents, would be 2 levels less deep than the prior entry. I hope that makes some sense.
Thanks again.
Re: Nested array from indented text
I don't suppose something like this would work, would it?
http://www.php.net/manual/en/function.e ... d-data.php
http://www.php.net/manual/en/function.e ... d-data.php
- AbraCadaver
- DevNet Master
- Posts: 2572
- Joined: Mon Feb 24, 2003 10:12 am
- Location: The Republic of Texas
- Contact:
Re: Nested array from indented text
I only tested on one array so its a little specific to the format you posted and haven't optimized it, but here's a start:
Code: Select all
$image = array_shift($img_data);
$result = $current = array();
$parent = &$result;
$prev_level = 0;
foreach($img_data) as $item) {
list($key, $val) = explode(': ', $item, 2);
$level = preg_match_all('/[ ]/', $key, $m) - preg_match_all('/[ ][^ ]/', $key, $m);
$key = trim(preg_replace('/:$/', '', $key));
$val = trim($val);
if($level > $prev_level) {
$current[$level] = &$parent;
} elseif($level <= $prev_level) {
$parent = &$current[$level];
}
if($val == '') {
$parent[$key] = array();
$parent = &$parent[$key];
$prev_level = $level;
} else {
$parent[$key] = $val;
}
}
print_r($result);mysql_function(): WARNING: This extension is deprecated as of PHP 5.5.0, and will be removed in the future. Instead, the MySQLi or PDO_MySQLextension should be used. See also MySQL: choosing an API guide and related FAQ for more information.
Re: Nested array from indented text
AbraCadaver, that will work perfectly. Thank you so much.