Enumerating ruby dictionary keys using dictionary entries

0 votes
asked Sep 11, 2017 by sharktamer

Apologies for the messy sounding title, didn't know how to phrase this in a concise way...

I'm looking to create a general purpose Vagrantfile for various test purposes using a dictionary like the one below.

BOXES = {
  'bo': ['zesty-lxc', 4],
  'sw': ['zesty-lxc', 4],
  'db': ['centos-lxc', 4]
}

Which would give me a list like below:

[
  ["bo1", "zesty-lxc"],
  ["bo2", "zesty-lxc"],
  ["bo3", "zesty-lxc"],
  ["bo4", "zesty-lxc"],
  ["sw1", "zesty-lxc"],
  ["sw2", "zesty-lxc"],
  ["sw3", "zesty-lxc"],
  ["sw4", "zesty-lxc"],
  ["db1", "centos-lxc"],
  ["db2", "centos-lxc"],
  ["db3", "centos-lxc"],
  ["db4", "centos-lxc"]
]

that I can then iterate through later to create machines.

Vagrant.configure('2') do |config|
  box_list.each do |box, name|
    config.vm.define name do |subconfig|
      subconfig.vm.box = box
      subconfig.vm.hostname = name
    end
  end
end

I'm having a bit of trouble managing this. I've managed to print exactly what I want using this very messy construct:

BOXES.map{|key, v| ([key]*BOXES[key][1]).each.with_index(1) {|machine, count| puts "#{machine}#{count} #{BOXES[key][0]}" }}

but it only prints, I can't figure out how to get it to return the list that I'm intending to use later.

Any help would be appreciated, I'm very new to ruby, only started using it when I started messing around with vagrant.

2 Answers

0 votes
answered Sep 11, 2017 by unkmas
result = BOXES.each_with_object([]) do |(k, v), memo| 
  (1 .. v.last).each do |i| 
    memo << [k.to_s + i.to_s, v.first]
  end
end
0 votes
answered Sep 11, 2017 by engineersmnky

You could do this as follows:

# We will assume this is what you meant for BOXES
BOXES = {
  bo: ['zesty-lxc', 4],
  sw: ['zesty-lxc', 4],
  db: ['centos-lxc', 4]
} 
BOXES.map do |key,value|
  os, number = value # assign value[0] to os and value[1] to number
  number.times.map {|n| [key.to_s + (n + 1).to_s, os] }
end.flatten(1) # flatten the Array 1 level 
#=> [
#     ["bo1", "zesty-lxc"],
#     ["bo2", "zesty-lxc"],
#     ["bo3", "zesty-lxc"],
#     ["bo4", "zesty-lxc"],
#     ["sw1", "zesty-lxc"],
#     ["sw2", "zesty-lxc"],
#     ["sw3", "zesty-lxc"],
#     ["sw4", "zesty-lxc"],
#     ["db1", "centos-lxc"],
#     ["db2", "centos-lxc"],
#     ["db3", "centos-lxc"],
#     ["db4", "centos-lxc"]
#   ]

Steps:

  • BOXES#map will create a new Array and yield each key and value to the block
  • os, number = value will assign value[0] to os and value[1] to number
  • number.times.map will create a new Array and will loop through number number of times passing in each integer (n), starting with 0 as it loops
  • [key.to_s + (n + 1).to_s, os] will create a literal Array with [0] = key and number + 1 combined as a String and [1] = value
  • flatten(1) will remove the Array nesting created by number.times.map
Welcome to Q&A, where you can ask questions and receive answers from other members of the community.
Website Online Counter

...