Как сделать динамический многомерный массив в ruby? - PullRequest
5 голосов
/ 05 августа 2009

У меня вопрос начинающего рубина о многомерных массивах.

Я хочу отсортировать записи по году и месяцу. Поэтому я хочу создать многомерный массив, который будет содержать годы -> месяцы -> записи месяца

Таким образом, массив будет выглядеть так:

2009 ->
       08
          -> Entry 1
          -> Entry 2
       09
          -> Entry 3
2007 ->
       10
          -> Entry 5

Теперь у меня есть:

@years = []
@entries.each do |entry|
    timeobj = Time.parse(entry.created_at.to_s)
    year = timeobj.strftime("%Y").to_i
    month = timeobj.strftime("%m").to_i
    tmparr = []
    tmparr << {month=>entry}
    @years.push(year)
    @years << tmparr
end

но когда я пытаюсь выполнить итерацию по массиву лет, я получаю: "undefined method` each 'for 2009: Fixnum "

Пробовал также:

@years = []
@entries.each do |entry|
    timeobj = Time.parse(entry.created_at.to_s)
    year = timeobj.strftime("%Y").to_i
    month = timeobj.strftime("%m").to_i
    @years[year][month] << entry
end

Ответы [ 4 ]

11 голосов
/ 05 августа 2009

Вы получаете ошибку, потому что FixNum (то есть число) помещается в массив, в строке, которая читает @years.push(year).

Ваш подход к использованию массивов для начала немного ошибочен; массив идеально подходит для хранения упорядоченного списка элементов. В вашем случае у вас есть сопоставление ключей и значений, которое идеально подходит для хэша.

На первом уровне ключи - это годы, значения - хэши. Хэши второго уровня содержат ключи месяцев и значения массивов записей.

В этом случае типичный вывод вашего кода будет выглядеть примерно так (на основе вашего примера):

{ 2009 => { 8 => [Entry1, Entry2], 9 => [Entry3] }, 2007 => { 10 => [Entry5] }}

Обратите внимание, что порядок лет и месяцев не гарантируется в каком-либо конкретном порядке. Обычно решение состоит в том, чтобы заказывать ключи в любое время, когда вы хотите получить к ним доступ. Теперь код, который будет генерировать такой вывод (в зависимости от вашей компоновки кода, хотя может быть сделан намного rubier ):

@years = {}
@entries.each do |entry|
  timeobj = Time.parse(entry.created_at.to_s)
  year = timeobj.strftime("%Y").to_i
  month = timeobj.strftime("%m").to_i
  @years[year] ||= {} # Create a sub-hash unless it already exists
  @years[year][month] ||= []
  @years[year][month] << entry
end
8 голосов
/ 05 августа 2009

Вы можете получить структуру вложенного массива в одну строку, используя комбинацию group_by s и map:

@entries.group_by {|entry| entry.created_at.year }.map { |year, entries| [year, entries.group_by {|entry| entry.created_at.month }] }
4 голосов
/ 05 августа 2009
# create a hash of hashes of array
@years = Hash.new do |h,k|
  h[k] = Hash.new do |sh, sk|
    sh[sk] = []
  end
end

@entries.each do |entry|
  timeobj = Time.parse(entry.created_at.to_s)
  year = timeobj.year
  month = timeobj.month
  @years[year][month] << entry
end
4 голосов
/ 05 августа 2009

Я использую хеш-таблицы вместо массивов, потому что я думаю, что здесь, наверное, больше смысла. Тем не менее, довольно просто вернуться к использованию массивов, если вы этого хотите.

entries = [
    [2009, 8, 1],
    [2009, 8, 2],
    [2009, 9, 3],
    [2007, 10, 5]
]

years = Hash.new
entries.each { |e|
    year = e[0]
    month = e[1]
    entry = e[2]

    # Add to years array
    years[year] ||= Hash.new
    years[year][month] ||= Array.new
    years[year][month] << entry
}

puts years.inspect

Вывод: {2007=>{10=>[5]}, 2009=>{8=>[1, 2], 9=>[3]}}

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