How to make dynamic multi-dimensional array in ruby?
Solution 1
You are getting the error because a FixNum
(that is, a number) is pushed on the array, in the line that reads @years.push(year)
.
Your approach of using Arrays to start with is a bit flawed; an array is perfect to hold an ordered list of items. In your case, you have a mapping from keys to values, which is perfect for a Hash.
In the first level, the keys are years, the values are hashes. The second level's hashes contain keys of months, and values of arrays of entries.
In this case, a typical output of your code would look something like (based on your example):
{ 2009 => { 8 => [Entry1, Entry2], 9 => [Entry3] }, 2007 => { 10 => [Entry5] }}
Notice that, however, the order of years and months is not guaranteed to be in any particular order. The solution is normally to order the keys whenever you want to access them. Now, a code that would generate such an output (based on your layout of code, although can be made much 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
Solution 2
You can get the nested array structure in one line by using a combination of group_by
s and map
:
@entries.group_by {|entry| entry.created_at.year }.map { |year, entries| [year, entries.group_by {|entry| entry.created_at.month }] }
Solution 3
I'm using hash tables instead of arrays, because I think it probably makes more sense here. However, it's fairly trivial to change back to using arrays if that's what you prefer.
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
The output is: {2007=>{10=>[5]}, 2009=>{8=>[1, 2], 9=>[3]}}
Solution 4
# 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
Admin
Updated on June 26, 2022Comments
-
Admin almost 2 years
I have a beginner ruby question about multi dimensional arrays.
I want to sort entries by year and month. So I want to create a multi-dimensional array that would contain years -> months -> entries of month
So the array would be like:
2009 -> 08 -> Entry 1 -> Entry 2 09 -> Entry 3 2007 -> 10 -> Entry 5
Now I have:
@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
but when I try to iterate through the years array, I get: "undefined method `each' for 2009:Fixnum"
Tried also:
@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
-
Admin over 14 yearsThanks for the enlightening me.
-
arun prakash over 14 yearsYou're welcome. Look at Michael_Sepcot's answer for how to do in a more rubyish way.
-
oligan over 14 yearsIsn't group_by a rails addition, not plain old ruby?
-
Diego Somar almost 7 yearsThanks! Finally I understand this