1

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.

6
  • 1
    The example you’ve posted is nether a Hash, nor an Array. It is invalid structure. Commented Mar 18, 2016 at 17:27
  • Not clear what the elements of Array #1 are. Are they strings? What are titles and links? Commented Mar 18, 2016 at 17:35
  • You are using the same values for all elements in Array #2. That is misleading to show your logic. Commented Mar 18, 2016 at 17:40
  • There are no multi-dimensional arrays in Ruby, just arrays-of-arrays and the like. Commented Mar 18, 2016 at 17:48
  • @muistooshort Strictly speaking, there is no such thing as “array-of-arrays” as well. There is an array-of-anything. Commented Mar 18, 2016 at 17:52

4 Answers 4

2

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Cary, you're a boss - thanks so much. Small fix in the loop though, you wrote h[:main] instead of g[:main].
2

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.

Comments

2
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/",
    {
    }
  ]
}

Comments

1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.