Jump to content

Distribute Data Evenly into columns


brooksh

Recommended Posts

I'm trying to create a sitemap page. With Main categories and then sub categories. Some have no subcategories and some have a lot. I want them evenly distributed into each column. I can't seem to find a script. Can someone help? I want it to look similar to this http://www.walmart.com/cp/All-Departments/121828

 

Here is a small portion of the categories:

<ul class="dirlist">
   
         <li><a class="s1" href="Accessories.htm"><b>A</b>ccessories</a></li>

            
               <ul class="subcats">
               
                     <li><a href="Belts.htm">Belts</a></li>
               
                     <li><a href="Gloves-and-Mittens.htm">Gloves 
					& Mittens</a></li>
               
               </ul>
            
         <li><a class="s1" href="Activewear.htm"><b>A</b>ctivewear</a></li>

            
         <li><a class="s1" href="Handbags-and-Luggage.htm"><b>H</b>andbags 
		& Luggage</a></li>

            
               <ul class="subcats">
               
                     <li><a href="Backpacks.htm">Backpacks</a></li>

               
                     <li><a href="Briefcases.htm">Briefcases</a></li>
               
                     <li><a href="Computer-Bags.htm">Computer 
					Bags</a></li>
               
               </ul>
            
          </ul>

Link to comment
Share on other sites

It's a messy problem in general.  If I was doing it I would start by deciding the number of columns, then developing a way to measure the vertical size of each category.  That would be something like "title + 1 (the gap) + number of subcats".  Then it's a matter of adding categories to each column (which could be represented by adding them into an array) until they reach approximately 1/5 of the total size of all categories.  Then start adding to the next column, and so on.

Link to comment
Share on other sites

Here's an exhaustive article on accomplishing the task using mark-up alone.

http://www.alistapart.com/articles/multicolumnlists/

 

Personally, I would probably go the way btherl suggested.

 

This works for categories. It doesn't work for categories with subcategories. this is why I'm having such a hard time. I've even looked at hotscripts.com to find something without any luck.

Link to comment
Share on other sites

Here's what I ended up with. To get absolutely ideal results, you'll have to mess with $value['cat'] and $value['subcat']. Messing with how the average is calculated is optional as well, as it's used to determine if we should move on to the next column or try to squeak one last category in.

 

<style type="text/css">
#sitemap td {
	border: 1px solid black;
	vertical-align: top;
	font: 10px sans-serif;
}
#sitemap h3,ul {
	margin: 0;
}
#sitemap h3{
	margin-top: 10px;
}
</style>
<?php

$data = array();

// Generate some random data
for( $i=0,$imax=rand(15,30); $i < $imax; $i++ ) {
$data[$i] = array('Category '.($i+1),array());
for( $j=0,$jmax=rand(0,10); $j < $jmax; $j++ )
	$data[$i][1][$j] = 'Subcategory '.($j+1);
}

// Some parameters for our script.

$columns = 4; // Number of columns
$value['cat'] = 3; // A 'size' value given to categories
$value['subcat'] = 2; // A 'size' value given to subcategories
$value['total'] = 0; // Will contain the total size value of all the categories and subcategories
$value['average'] = 0; // Will contain the average size value of each catgories and it's subs
$value['percolumn'] = 0; // The size value each columb should be close to

// Get a sum of all categories and sub-categories.
foreach( $data as $category )
$value['total'] += $value['cat'] + count($category[1]) * $value['subcat'];
$value['average'] = floor( $value['total'] / count($data) );
$value['percolumn'] = floor( $value['total'] / $columns );

echo '<table id="sitemap"><tr><td>';

$column_value = 0; // Will hold the total value of the current column
$column_current = 1; // Will hold the current column number
foreach( $data as $category ) {
// Grab the value of the category about to be displayed
$cat_value = $value['cat'] + count($category[1]) * $value['subcat'];
// Check if category is too big for column, unless it's the last column
if( ($cat_value+$column_value) > ($value['percolumn']+$value['average']) && $column_current < $columns ) {
	// For debugging
	echo 'Column value total: '.$column_value;
	// Start a new column
	echo '</td><td>';
	// Reset the total column value
	$column_value = 0;
	// Increment the current column
	$column_current++;
}
// Output category
echo '<h3>'.$category[0].'</h3>';
// Verify category isn't empty
if( !empty($category[1]) ) {
	// Output subcategories
	echo '<ul>';
	foreach( $category[1] as $subcategory )
		echo '<li>'.$subcategory.'</li>';
	echo '</ul>';
}
// Add category's value to the column's value
$column_value += $cat_value;

}
// For debugging
echo 'Column value total: '.$column_value;
// End the table
echo '</td></tr></table>';

// More debugging
print_r( $value );

?>

 

It'd say without any changes, it works quite well. If someone wants to steam-line this a little, we could have a really cool snippet. There are some unnecessary variables used, I know. I just wanted to isolate as much logic as possible for ease of understanding.

 

EDIT - If you're finding the last column doesn't have enough content, change the average calculation to something like this.

$value['average'] = floor( $value['total'] / count($data) * 2/3 );

 

The greater the variation in category size, the greater the chance you'll end up with unideal results.

Link to comment
Share on other sites

More thinking:

 

If this was something you could cache, you could run a loop that totals each column, and tests a bunch of possibilities for the most 'ideal' distribution. Hmmm. Back to the drawing board :D

Link to comment
Share on other sites

Sorry to spam this thread. I've developed a 'smart' version of this script. It's quite a bit slower, though.

 

What it does is it builds all possible category/column combinations for the average column length to the average column length + average category length, in intervals of one.

 

The results are pretty sexy, and it could easily be optimized to skip %50+ of the iterations. This is more of a proof-of-concept.

 

<style type="text/css">
#sitemap td {
	border: 1px solid black;
	vertical-align: top;
	font: 10px sans-serif;
}
#sitemap h3,ul {
	margin: 0;
}
#sitemap h3{
	margin-top: 10px;
}
</style>
<?php

$data = array();

// Generate some random data
for( $i=0,$imax=rand(15,30); $i < $imax; $i++ ) {
$data[$i] = array('Category '.($i+1),array());
for( $j=0,$jmax=rand(0,10); $j < $jmax; $j++ )
	$data[$i][1][$j] = 'Subcategory '.($j+1);
}

// Some parameters for our script.

$columns = 5; // Number of columns
$cat_value = 3; // Size value of each category
$sub_value = 2; // Size value of each subcategory
//
// Get our category data
$category_size = array();
foreach( $data as $key => $category )
$category_size[$key] = $cat_value + ($sub_value * count($category[1]));

$average_cat = ceil( array_sum($category_size) / count($category_size) );
$average_col = ceil( array_sum($category_size) / $columns );

$difference = array();
$columndata = array();
$debug_sizes = array();

for( $i = $average_cat; $i > 0; $i-- ) {
$curcol = 1;
$colsize = 0;
$sizes = array();
foreach( $data as $key => $category ) {
	if( $category_size[$key]+$colsize > $average_col+$i && $curcol < $columns ) {
		$sizes[$curcol] = $colsize;
		$debug_sizes[$i][$curcol] = $colsize;
		$curcol++;
		$colsize = 0;
	}
	$columndata[$i][$curcol][] = $key;
	$colsize += $category_size[$key];
}
$debug_sizes[$i][$curcol] = $colsize;
$sizes[$curcol] = $colsize;
if( count($sizes) == $columns ) {
	sort( $sizes );
	$difference[$i] = $sizes[($columns-1)] - $sizes[0];
}
}

asort( $difference );

echo '<table id="sitemap"><tr>';
foreach( $columndata[key($difference)] as $catarray ) {
echo '<td>';
foreach( $catarray as $category ) {
	echo '<h3>'.$data[$category][0].'</h3><ul>';
	foreach( $data[$category][1] as $sub ) {
		echo '<li>'.$sub.'</li>';
	}
	echo '</ul>';
}
echo '</td>';
}
echo '</tr></table>';

echo '<h3>Debug Information</h3>';
echo '<strong>List of differences</strong><pre>';
print_r( array_unique($difference) );
echo '</pre><strong>Average Category Size: </strong>' . $average_cat . '<br>';
echo '<strong>Average Column Size: </strong>' . $average_col . '<br>';
echo '<strong>Actual Column Sizes:</strong><pre>';
print_r( $debug_sizes[key($difference)] );
echo '</pre>';


?>

Link to comment
Share on other sites

Your script looks like it can work, I just don't know how to implement it with my query.

 

To grab the main category:

SELECT DISTINCT category_name,category_id FROM db WHERE parent_id ='0' Order By category_name

 

Then grab the subcategory:

SELECT DISTINCT category_name,category_id FROM db WHERE parent_id = '$category_id' Order By category_name

Link to comment
Share on other sites

Sorry to spam this thread. I've developed a 'smart' version of this script. It's quite a bit slower, though.

 

What it does is it builds all possible category/column combinations for the average column length to the average column length + average category length, in intervals of one.

 

The results are pretty sexy, and it could easily be optimized to skip %50+ of the iterations. This is more of a proof-of-concept.

 

<style type="text/css">
#sitemap td {
	border: 1px solid black;
	vertical-align: top;
	font: 10px sans-serif;
}
#sitemap h3,ul {
	margin: 0;
}
#sitemap h3{
	margin-top: 10px;
}
</style>
<?php

$data = array();

// Generate some random data
for( $i=0,$imax=rand(15,30); $i < $imax; $i++ ) {
$data[$i] = array('Category '.($i+1),array());
for( $j=0,$jmax=rand(0,10); $j < $jmax; $j++ )
	$data[$i][1][$j] = 'Subcategory '.($j+1);
}

// Some parameters for our script.

$columns = 5; // Number of columns
$cat_value = 3; // Size value of each category
$sub_value = 2; // Size value of each subcategory
//
// Get our category data
$category_size = array();
foreach( $data as $key => $category )
$category_size[$key] = $cat_value + ($sub_value * count($category[1]));

$average_cat = ceil( array_sum($category_size) / count($category_size) );
$average_col = ceil( array_sum($category_size) / $columns );

$difference = array();
$columndata = array();
$debug_sizes = array();

for( $i = $average_cat; $i > 0; $i-- ) {
$curcol = 1;
$colsize = 0;
$sizes = array();
foreach( $data as $key => $category ) {
	if( $category_size[$key]+$colsize > $average_col+$i && $curcol < $columns ) {
		$sizes[$curcol] = $colsize;
		$debug_sizes[$i][$curcol] = $colsize;
		$curcol++;
		$colsize = 0;
	}
	$columndata[$i][$curcol][] = $key;
	$colsize += $category_size[$key];
}
$debug_sizes[$i][$curcol] = $colsize;
$sizes[$curcol] = $colsize;
if( count($sizes) == $columns ) {
	sort( $sizes );
	$difference[$i] = $sizes[($columns-1)] - $sizes[0];
}
}

asort( $difference );

echo '<table id="sitemap"><tr>';
foreach( $columndata[key($difference)] as $catarray ) {
echo '<td>';
foreach( $catarray as $category ) {
	echo '<h3>'.$data[$category][0].'</h3><ul>';
	foreach( $data[$category][1] as $sub ) {
		echo '<li>'.$sub.'</li>';
	}
	echo '</ul>';
}
echo '</td>';
}
echo '</tr></table>';

echo '<h3>Debug Information</h3>';
echo '<strong>List of differences</strong><pre>';
print_r( array_unique($difference) );
echo '</pre><strong>Average Category Size: </strong>' . $average_cat . '<br>';
echo '<strong>Average Column Size: </strong>' . $average_col . '<br>';
echo '<strong>Actual Column Sizes:</strong><pre>';
print_r( $debug_sizes[key($difference)] );
echo '</pre>';


?>

 

You should polish that up a little and throw it in the snippet repo, it looks pretty useful.

Link to comment
Share on other sites

Could you please give me an example array instead of the random array? I don't know too much.

I know this isn't correct, but I'm just trying to figure this script out.

 

$imax = array(Category:subcategory:subcategory,Category2:subcategory:subcategory)

 

// Generate some random data
for( $i=0,$imax=rand(15,30); $i < $imax; $i++ ) {
$data[$i] = array('Category '.($i+1),array());
for( $j=0,$jmax=rand(0,10); $j < $jmax; $j++ )
	$data[$i][1][$j] = 'Subcategory '.($j+1);
}

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.