Четные основные / дополнительные столбцы в Rails - PullRequest
3 голосов
/ 05 июля 2010

У меня есть набор регионов и городов (вложенных), и я хочу иметь возможность выводить их в несколько столбцов четной длины, упорядоченных в алфавитном порядке . Например:

[Alberta]   [Ontario]   [Quebec]
Calgary     Hamilton    Hull
Edmonton    Kitchener   Laval
[Manitoba]  Ottawa      Montreal
Winnipeg    Toronto
            Waterloo

Я взглянул на ' in_groups ' (и ' in_groups_of '), однако мне нужно сгруппировать данные по размеру отношений (то есть по количеству городов в регионе есть). Не уверен, существует ли хороший Rails способ сделать это. Пока мой код выглядит примерно так:

<% regions.in_groups(3, false) do |group| %>
  <div class="column">
    <% group.each do |region| %>
      <h1><%= region.name %></h1>
      <% region.cities.each do |city| %>
        <p><%= city.name %></p>
      <% end %>
    <% end %>
  </div>
<% end %>

Однако некоторые регионы крайне несбалансированы (то есть имеют много городов) и отображаются некорректно.

Ответы [ 2 ]

2 голосов
/ 06 июля 2010

Я согласен, что это должен быть вспомогательный код, а не встроенный в представление.

Предположим, у вас есть карта провинции в городе в хэше:

 map = {
   "Alberta" => ["Calgary", "Edmonton"],
   "Manitoba" => ["Winnipeg"],
   "Ontario" => ["Hamilton", "Kitchener", "Ottawa", "Toronto", "Waterloo"],
   "Quebec" => ["Hull", "Laval", "Montreal"]
 }

Начать прощедумая о 2 столбцах.Для 2 столбцов мы хотим решить, где остановиться 1-й столбец и начать 2-й.Есть 3 варианта для этих данных: между Альбертой и Манитобой, Манитобой и Онтарио и между Онтарио и Квебеком.

Итак, давайте начнем с создания функции, позволяющей разделить список в нескольких местах одновременно:

def split(items, indexes)
  if indexes.size == 0
    return [items]
  else
    index = indexes.shift
    first = items.take(index)
    indexes = indexes.map { |i| i - index }
    rest = split(items.drop(index), indexes)
    return rest.unshift(first)
  end
end

Затем мы можем взглянуть на все разные способы, которыми мы можем сделать 2 столбца:

require 'pp' # Pretty print function: pp

provinces = map.keys.sort

1.upto(provinces.size - 1) do |i|
  puts pp(split(provinces, [i]))
end

=>

[["Alberta"], ["Manitoba", "Ontario", "Quebec"]]
[["Alberta", "Manitoba"], ["Ontario", "Quebec"]]
[["Alberta", "Manitoba", "Ontario"], ["Quebec"]]

Или мы можем посмотреть на разные способы, которыми мы можем сделать 3 столбца:

1.upto(provinces.size - 2) do |i|
  (i+1).upto(provinces.size - 1) do |j|
    puts pp(split(provinces, [i, j]))
  end
end

=>

[["Alberta"], ["Manitoba"], ["Ontario", "Quebec"]]
[["Alberta"], ["Manitoba", "Ontario"], ["Quebec"]]
[["Alberta", "Manitoba"], ["Ontario"], ["Quebec"]]

Как только вы сможете это сделать, вы можете найти расположение, в котором столбцы имеют наиболее равномерную высоту.Нам понадобится способ найти высоту столбца:

def column_height(map, provinces)
  provinces.clone.reduce(0) do |sum,province|
   sum + map[province].size
  end
end

Затем вы можете использовать цикл до того, чтобы найти 3 столбца с минимальной разницей между самым высоким и самым коротким столбцами:

def find_best_columns(map)
  provinces = map.keys.sort
  best_columns = []
  min_difference = -1
  1.upto(provinces.size - 2) do |i|
    (i+1).upto(provinces.size - 1) do |j|
      columns = split(provinces, [i, j])
      heights = columns.map {|col| column_height(map, col) }
      difference = heights.max - heights.min
      if min_difference == -1 or difference < min_difference
        min_difference = difference
        best_columns = columns
      end
    end
  end
  return best_columns
end

Это даст вам список для каждого столбца:

puts pp(find_best_columns(map))

=>

[["Alberta", "Manitoba"], ["Ontario"], ["Quebec"]]

Это здорово, потому что вы выясняете, какие провинции принадлежат каждому столбцу независимо от структуры модели, и это не так.не генерировать HTML напрямую.Таким образом, модели и представления могут меняться, но вы все равно можете использовать этот код.Поскольку эти функции автономны, для них также легко написать модульные тесты.Если вам нужно сбалансировать 4 столбца, вам просто нужно настроить функцию find_best_columns или вы можете переписать ее рекурсивно для поддержки n столбцов, где n - другой параметр.

1 голос
/ 05 июля 2010

Если вы хотите оставить их слева направо в алфавитном порядке, я не могу найти хороший способ.Используя то, что у вас есть.Вот то, что я имел в виду.Это следует немного разделить на вспомогательный / контроллер / модель, но это должно дать вам представление о том, соответствует ли это тому, о чем вы думали

def region_columns(column_count)
  regions = Region.all(:include => :cities)
  regions.sort!{|a,b| a.cities.size <=> b.cities.size}.invert
  columns = Array.new(column_count, [])

  regions.each do |region|
    columns.sort!{|a,b| a.size <=> b.size}
    columns[0] << "<h1>#{region.name}</h1>"
    columns[0] << region.cities.map{|city| "<p>#{city.name}</p>"}
    columns[0].flatten
  end

  columns
end

, что даст вам столбцы HTML, которые вы быпросто нужно пройтись по вашему мнению.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...