Welcome Guest, Not a member yet? Register   Sign In
Programming Challenge
#1

[eluser]seanloving[/eluser]
How do I write an algorithm that takes an array like this...
Code:
$regions = array ( 'box',
                   'box_left',
                   'box_left_top',
                   'box_left_bottom',
                   'box_right');

and generates HTML like this...
Code:
<div id="box">
     <div id="left">
          <div id="top"></div>
          <div id="bottom"></div>
     </div>
     <div id="right"></div>
</div>
?

Need the algorithm to understand the (hopefully obvious) hierarchical naming convention. Here's what I have so far
Code:
foreach( $regions as $region )
    {
        // first get the unique region name
        // the unique region name is the last word in the string
        $id = strrchr($region, '_');
        if( !$id )
        {
            // handle the first element
            $id=$region;
        }
        else
        {
            // get rid of the leading _
            $id = substr($id,1);
        }
        echo "<div id=\"$id\">$id";
        echo "</div>";
    }
Can anybody help me figure out the hierarchy part? Thanks in advance.

--Sean Loving


Thanks
#2

[eluser]alboyd[/eluser]
OMG.. Begs the question.. WHY?
#3

[eluser]BrianDHall[/eluser]
For hierarchy I'd use preg function to search the string for the chosen id, , such as 'top', then search in reverse and count all the instances of '<div'. 1 means top level, 3 means it is nested inside 2 other divs, etc.

To simplify the preg function since regex isn't everyone's favorite thing in the world, you could use strpos() to get the position of the string and then use substr() to isolate only your chosen id and any part of the string that comes before it.

Note however that it's a hack and is not 100% robust, as it would be fooled by "<div></div><div id='id'>" - it would think id is nested instead of being top-level. To address that you'd need to parse as you go, incrementing for each <div> but subtracting for each </div> until you get to your ID.

The other solution is to parse it like XML of sorts and then use pre-built functions, but you'd need to get pretty chummy with XML functions to enjoy that task.
#4

[eluser]sophistry[/eluser]
switch to YAML to represent your block element hierarchy and use this nice YAML parser on the wiki :-)
#5

[eluser]helmutbjorg[/eluser]
[quote author="alboyd" date="1260394733"]OMG.. Begs the question.. WHY?[/quote]

hhahaa.. nice.
#6

[eluser]helmutbjorg[/eluser]
Actually i felt bad... You will end up pulling your hair out. You should have a better layout for your array if you want it to work like that. And then use a recursive function that will output the html no matter how big or nasty your array becomes.

Code:
function create($segments, $html='') {
    foreach($segments as $id => $segment) {
        $html .= '<div id="'.$id.'">';
        if(is_array($segment)) $html = create($segment, $html);
        $html .= '</div>';        
    }
    return $html;
}                
    
$regions = array('box'=>array('left'=>array('top'=>'','bottom'=>''),'right'=>''));    
        
echo create($regions);

// Outputs
<div id="box">
    <div id="left">
        <div id="top"></div>
        <div id="bottom"></div>
    </div>
    <div id="right"></div>
</div>
#7

[eluser]helmutbjorg[/eluser]
Actually sitting here I figured a way you can actually turn your array into my array so I created a (messy as hell) function to do it. It only goes to three levels (like your example) but could go to more. If you had time you could make that a recursive function too which will allow you to go to infinite levels.

See below
Code:
// This is a shit function and I recommend you simply change your array layout
function reformat($regions) {
    $format = array();
    foreach($regions as $region) {
        $region = explode('_', $region);
        $count = count($region);
        if($count==1) $format[$region[0]]='';
        else if($count==2) $format[$region[0]][$region[1]]='';
        else if($count==3) $format[$region[0]][$region[1]][$region[2]]='';
    }
    return $format;
}

function create($segments, $html='') {
    foreach($segments as $id => $segment) {
        $html .= '<div id="'.$id.'">';
        if(is_array($segment)) $html = create($segment, $html);
        $html .= '</div>';        
    }
    return $html;
}  

$regions = array ( 'box',
                   'box_left',
                   'box_left_top',
                   'box_left_bottom',
                   'box_right');


$html = create(reformat($regions));

// Outputs the html you need...
#8

[eluser]seanloving[/eluser]
@sophistry - YAML might be the right way to prepare the $regions array

@helmutbjorg - I think you are right about the need for a recursive function

@BrianDHall - I think I'm doing something like you are suggesting - with strstr()

---------------

I ALMOST got this working... can somebody PLEASE help?

Once again, here's my desired output...
Code:
<div id="box">
     <div id="left">
          <div id="top"></div>
          <div id="bottom"></div>
     </div>
     <div id="right"></div>
</div>

But here is the actual output of my code
Code:
<div id="box">
     <div id="left">
          <div id="top"></div>
          <div id="bottom"></div>
          <div id="right"></div>
     </div>
</div>


And here is my code:
Code:
// pass this function an array of view partials (a.k.a. regions,  a.k.a. strings)
    // use this function to hierarchically structure (with div tags) the $regions
    function structure( &$regions=array() )
    {
        foreach( $regions as $parent=>$content )
        {
            // opening wrapper for this region
            echo "<div id=\"$parent\"><b>$parent</b> OPEN";
            
            // remove this region now that it has been rendered
            unset( $regions[$parent] );

            // see if this region is a parent of any remaining regions
            foreach($regions as $child=>$data)
            {
                if( ($parent & strstr($child,$parent)) )
                {
                    // at least one child was found so recurse
                    $regions = $this->structure( $regions );  
                    echo "<b>$parent</b> CLOSED</div>";
                    return $regions;
                }
            }
            echo "<br><b>".$parent."</b> CLOSED</div>";
        }
        
    }

I call that function like this:
Code:
// This is a test array. Remove it.
            $regions = array( 'box'             => 'the main box',
                              'box_left'        => 'the box on the left inside the main box',
                              'box_left_top'    => 'the box on the top inside the left box',
                              'box_left_bottom' => 'the box on the bottom inside the left box',
                              'box_right'       => 'the box on the left inside the main box'
                            );

            $regions = $this->structure( $regions );

Anyone? Please!! Thanks!

--Sean Loving
#9

[eluser]sophistry[/eluser]
@seanloving

i think you misunderstood what i meant. the suggestion wasn't to prepare the flat regions array with YAML, it was to prepare a nested array so that all your code had to do was iterate the array and output your divs.

the problem is that the regions array with strings (as it stands now) is using underscore to delimit nesting level; this is a non-standard way to represent hierarchy.

all i was suggesting was to use a standard way of representing hierarchy right from the start: YAML (using indentations and dashes) see http://www.yaml.org/
#10

[eluser]seanloving[/eluser]
@sophistry- thanks for taking the time to clarify.

The key names in my $regions array are actually extracted from the (arbitrarily predefined) field names of a database table. The name of the table is the name of the template. The field names are the region names. Each table row is a unique page. The naming convention of the field names dictates the hierarchical structure. I can't name MySQL fields with YAML, although I could build a translation layer if it made sense.

I suppose the database I describe could be generated from the same YAML file that would presumably be used to generate the <div> hierarchy. Sounds like a good approach. The pro is that YAML is easy to read. The con is that you need to add one or two extra processing steps.

Anyway, yikes trying to write this recursively is tricky.

-SL




Theme © iAndrew 2016 - Forum software by © MyBB