Created
January 9, 2014 06:38
-
-
Save kitgrose/8330310 to your computer and use it in GitHub Desktop.
Makes n balanced lists of grouped items in PHP, without breaking up any groups in the initial set and while preserving the order of sets. Useful for taking a grouped set of navigation items and presenting them in columns in a navigation menu.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
Makes n balanced lists of sub-lists, without breaking inside any existing lists. | |
Expects input in the form: | |
array( | |
'Section 1' => array( | |
'Item', | |
'Item', | |
... | |
), | |
'Section 2' => array( | |
... | |
), | |
... | |
) | |
Returns output in the form: | |
array( | |
0 => array( | |
'Section 1' => array( | |
'Item', | |
... | |
), | |
... | |
), | |
1 => array( | |
'Section 2' => array( | |
... | |
), | |
... | |
), | |
... | |
) | |
*/ | |
function generateRandomData($maxSections = 20, $maxItems = 20) { | |
$output = array(); | |
for($i = 0; $i < rand(5, $maxSections); $i++) { | |
for($j = 0; $j < rand(1, $maxItems); $j++) { | |
$output['Section '.($i + 1)][] = 'Item '.($j + 1); | |
} | |
} | |
return $output; | |
} | |
function balanceList($fullList, $numColumns = 3) { | |
$totalListHeight = array_sum(array_map('count', $fullList)) + count($fullList); // Number of items plus number of sections to include headings | |
$idealColumnHeight = $totalListHeight / $numColumns; | |
$output = array(); | |
// Prime the column arrays so array_map can work | |
for($i = 0; $i < $numColumns; $i++) { | |
$output[$i] = array(); | |
} | |
$currentColumn = 0; | |
$listHeightRemaining = $totalListHeight; | |
foreach($fullList as $title => $section) { | |
$columnHeightWithSection = array_sum(array_map('count', $output[$currentColumn])) + count($section); | |
if($columnHeightWithSection > $idealColumnHeight && $currentColumn !== $numColumns - 1) { | |
$overflow = $columnHeightWithSection - $idealColumnHeight; | |
// If the overflow of inserting this item is less than the overflow that would be caused by letting it carry over, insert it | |
$expectedOverflowOnRemainingColumns = $listHeightRemaining / ($numColumns - $currentColumn - 1) - $idealColumnHeight; | |
if($overflow > $expectedOverflowOnRemainingColumns) { | |
$currentColumn++; | |
} | |
} | |
$output[$currentColumn][$title] = $section; | |
$listHeightRemaining -= count($section) + 1; | |
} | |
return $output; | |
} | |
$numColumns = !empty($_REQUEST['cols']) ? intval($_REQUEST['cols']) : rand(3, 5); | |
?> | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<style> | |
#columns { | |
display: table; | |
width: 100%; | |
} | |
#columns div { | |
display: table-cell; | |
vertical-align: top; | |
} | |
h1 { | |
font-size: 1em; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="columns"> | |
<?php | |
foreach(balanceList(generateRandomData(), $numColumns) as $column) { | |
?> | |
<div class="column"> | |
<?php | |
foreach($column as $title => $section) { | |
?> | |
<h1><?php echo $title; ?></h1> | |
<ul> | |
<?php | |
foreach($section as $item) { | |
?> | |
<li><?php echo $item; ?></li> | |
<?php | |
} | |
?> | |
</ul> | |
<?php | |
} | |
?> | |
</div> | |
<?php | |
} | |
?> | |
</div> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment