Вывод списка в несколько колонок
Чтобы было понятно, над чем я тупил целых 3 часа, формализуем задачу. Дан массив из N
элементов. Его надо разделить на C массивов наиболее равномерным образом. Например, то самое меню, с которым я бился: нужно распределить массив ссылок на 3 колонки. Причём, принципиально выводить элементы по столбцам, а не по строкам!
Проиллюстрирую, что значит равномерно.
Это равномерно |
А это неравномерно (последнюю колонку явно обделили) |
С наскоку мне удалось получить результат, изображенный на второй картинке, хотя на первый взгляд, задача тривиальна. Видимо, сказалась привычка делить на только на 2 колонки, что значительно проще.
Объясню откуда алгоритм получения неверного результата:
- Делим количество элементов на количество колонок и округляем вверх. Получаем количество элементов в одной колонке. Обозначим его
n
. - Выводим по
n
элементов в каждой колонке, кроме последней. В последнюю выводим то, что останется.
Этот простой алгоритм замечательно работает, если колонок всего 2, поэтому в подавляющем большинстве случаем им можно пользоваться.
Но если нужно более универсальное решение, то придётся пораскинуть мозгами. На словах универсальный алгоритм выглядит так.
- Вычисляем минимальное количество элементов в колонке, поделив общее количество элементов на число колонок и округлив вниз. В примере —
$min
. - Вычисляем количество «лишних» элементов — это остаток от деления общего количества элементов на число колонок. В примере —
$extra
. - Теперь ключевой момент. Создаём массив, каждый элемент которого — это количество элементов в соответствующей колонке. Все элементы будут равны числу, высчитанному на первом шаге, а к нескольким первым мы добавим по лишней единичке. Число «счастливчиков» равно числу лишних элементов, полученных на шаге 2. Чтобы дальше было проще, можно запоминать не количество элементов в колонках, а индексы «пограничных» элементов — элементов на которых будет заканчиваться один массив и начинаться другой. В примере —
$colsIndex
. - Дальше всё просто. Пробегаемся циклом по исходному массиву. Если индекс текущего элемента содержится в массиве, полученном на предыдущем этапе, то начинаем выводить новую колонку.
Надеюсь идея ясна. А теперь код примера:
// исходные данные
$elems = 10; // число элементов
$cols = 3; // число количество колонок
// заполняем массив тестовыми элементами
$array = array();
for($q = 0; $q < $elems; $q++)
$array[] = $q;
// а теперь сам код
$total = sizeof($array); // сколько у нас элементов
$min = floor($total / $cols); // минимальное количество элементов в столбце
$extra = $total - $min * $cols; // "лишние" элементы
$colsIndex = array(); // массив с "пограничными" индексами для колонок
$prevCol = 0; // количество элементов, которое уже распределено по колонкам
for($q = 0; $q < $cols; $q++){
// если еще есть лишние элементы, то добавляем один из них к текущей колонке
$colNum = $extra-- > 0 ? $min + 1 : $min;
$prevCol += $colNum;
$colsIndex[] = $prevCol;
}
$curCol = 0; // какую колонку выводим
foreach($array as $c => $val){
if($c == $colsIndex[$curCol]){
++$curCol;
echo "\n";
}
echo $val."\n";
}
Кстати мой товарищ, занимающийся программированием для телефонов в очень приличной конторе, решил эту задачку за 10 минут :)
Буду издеваться над кандидатами на должность программиста :)
# Выводим (или записываем в соответствующий массив) последовательно в каждую колонку по $min * $cols элементнов;
# Записав последний элемент, смотрим, если номер текущей колонки меньше либо равен $extra (строго меньше, если нумерация с нуля), то добавляем еще один, иначе сразу переходим к следующей колонке.
Надо будет включить в статью.
foreach($a as $i=>$v)$ar[$i%3][$i]=$v;
$ar[$i%3][$i]=$v; - тут 3 это колличество колонок
$a = array(1,2,3,4,5,6,7,8,9,10);
echo '<ul>';
foreach($a as $i=>$v) echo '<li '.(!($i%3)?'style="clear:both"':'').'>'.$v.'</li>';
echo '</ul>';
echo '<style>ul > li {float:left;width:100px;list-style: none outside none;}</style>';
$a = array(1,2,3,4,5,6,7,8,9,10);
$c=count($a);$t=$c/3;
foreach($a as $i=>$v)$y[$v%$t][$i]=$v;
Мой скрипит выводит элементы массива по одному на строке и еще одну пустую строку между колонками. Вы, вероятно, этого не увидили, потому что смотрели результат работы скрипа в браузере. Откройте исходный код вашей страницы — там всё будет видно правильно.
А на JavaScript'е такое писать ИМХО извращение. Потому что будет медленно и глюковато.
Осталось дождаться поддержки большинством браузеров и отмирания динозавров
Ему бы только побороть проблему висячих строк: http://prntscr.com/6js2n
Выровнять колонки по ширине и добавить расстановку переносов: http://code.google.com/p/hyphenator/
И останется только одна проблема - получить заказ на разработку сайта от www.thetimes.co.uk :)
А array_chunk, не проще ли использовать, указал на сколько делить, и вперед.