Reputation: 499
I am trying to build a multidimensional array from two arrays. The use case is to build a dynamic menu.
Array #1
contains the titles for the menu where any title prefixed with --
will be a 'main' title and any title prefixed with -
will be a sub for the previous 'main' title.
For instance, if we have a hypothetical scenario like:
--Home
--About
-Our Philosophy
-The Company
--Careers
-Job List
-Apply
--Contact
Array #2
contains the links for each menu item.
The first step was to combine both arrays into a hash by doing combined = Hash[titles.zip(links)
.
How would I build it so that I can then loop over and build the menu dynamically?
Looking for a final similar structure to this (unless there's another suggested way):
['Home' => 'http://stackoverflow.com/'],
['About' => 'http://stackoverflow.com/',
[
'Our Philosophy' => 'http://stackoverflow.com/',
'The Company' => 'http://stackoverflow.com/'
]
]
etc...
If someone could explain how to then iterate over it, that would be awesome.
Upvotes: 1
Views: 106
Reputation: 110745
I think the array I construct below would serve your needs.
arr = [
"--Home",
"--About",
"-Our Philosophy",
"-The Company",
"--Careers",
"-Job List",
"-Apply",
"--Contact"
]
links = ["l1", "l2", "l3", "l4", "l5", "l6", "l7", "l8"]
links_cpy = links.dup
menu = arr.each_with_object([]) do |s,a|
if s[1] == '-'
a << { main: [s[2..-1], links_cpy.shift] }
else
(a[-1][:subs] ||= []) << [s[1..-1], links_cpy.shift]
end
end
#=> [{:main=>["Home", "l1"]},
# {:main=>["About", "l2"], :subs=>[["Our Philosophy", "l3"], ["The Company", "l4"]]},
# {:main=>["Careers", "l5"], :subs=>[["Job List", "l6"], ["Apply", "l7"]]},
# {:main=>["Contact", "l8"]}]
You would then construct the menu in the obvious way:
menu.each do |g|
<construct main item labelled g[:main][0] with link g[:main][1]>
g[:subs].each do |label,link|
<construct sub item label->link >
end
end
I chose to make menu
and the value of subs
arrays (rather than hashes) to ensure that the order of menu items is preserved for pre-1.9 versions of Ruby.
Upvotes: 2
Reputation: 168239
array_1 = [
"--Home",
"--About",
"-Our Philosophy",
"-The Company",
"--Careers",
]
array_2 = [
"http://stackoverflow.com/",
"http://stackoverflow.com/",
"http://stackoverflow.com/",
"http://stackoverflow.com/",
"http://stackoverflow.com/",
]
array_1.zip(array_2)
.slice_before{|k, _| k =~ /\A--/}
.map{|(k, v), *a| [k[/[^-]+/], [v, a.map{|(k, v)| [k[/[^-]+/], v]}.to_h]]}.to_h
Output:
{
"Home"=>[
"http://stackoverflow.com/",
{
}
],
"About"=>[
"http://stackoverflow.com/",
{
"Our Philosophy" => "http://stackoverflow.com/",
"The Company" => "http://stackoverflow.com/"
}
],
"Careers"=>[
"http://stackoverflow.com/",
{
}
]
}
Upvotes: 2
Reputation: 121010
The correct structure to describe this data relation would be:
menu = {
'Home' => ['http://stackoverflow.com/'],
'About' => ['http://stackoverflow.com/',
'Our Philosophy' => 'http://stackoverflow.com/',
'The Company' => 'http://stackoverflow.com/']
}
Iteration:
menu.each do |k, v|
puts "<a href='#{v.first}'>#{k}</a>"
puts " Children: #{v[1..-1]}" unless v.size == 1
end
#⇒ <a href='http://stackoverflow.com/'>Home</a>
#⇒ <a href='http://stackoverflow.com/'>About</a>
#⇒ Children: [{"Our Philosophy"=>"http://stackoverflow.com/", "The Co...}]
How to deal with children is up to you then.
Upvotes: 2
Reputation: 17
Strictly speaking it is not possible to create multi dimensional arrays in Ruby. But it is possible to put an array in another array, which is almost the same as a multi dimensional array.
This is how you could create a 2D array in Ruby:
a = [[1,2,3], [4,5,6], [7,8,9]]
As stated in the comments, you could also use NArray which is a Ruby numerical array library:
require 'narray' b = NArray[ [1,2,3], [4,5,6], [7,8,9] ]
Use a[i][j] to access the elements of the array. Basically a[i] returns the 'sub array' stored on position i of a and thus a[i][j] returns element number j from the array that is stored on position i.
Upvotes: 1