mousesports
mousesports

Reputation: 499

Building multidimensional array in Ruby

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

Answers (4)

Cary Swoveland
Cary Swoveland

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

sawa
sawa

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

Aleksei Matiushkin
Aleksei Matiushkin

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

yanick
yanick

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

Related Questions