Using pipes in Ruby Loops

So, perhaps I am complicating too much what is not so difficult, but here it goes.

In Ruby, there is a looping method called .each. I think this is very cool, but what I consider less cool is the amount of material written about the pipe that appears after it (or any other do-type loop in Ruby, it would seem). Sometimes there is one thing in the pipe:

basket.each do |fruit| puts "This is a #{fruit}.\n" end 

But sometimes there are two things in this pipe:

 contacts.each do |name, profession| puts "#{name} is a #{profession}.\n" end 

So, what is interesting to me now is it possible to have more than two elements in this channel? As if I had a huge, large and ugly multi-ton array?

What if I add things to my pipe and they’re not there? Will it give value in the nil pipe? Or will it cause an error?

Again, sorry if this is obvious to longtime rubies, but I came from a country of strongly typed variables, and now I am leaving the land of PHP. :)

EDIT

So if I have something like this:

 categories = [["Bathroom", "Bathroom Fixtures", "Plumbing"], ["Ceiling Fixtures", "Chandeliers", "Flush Mounts", "Mini Chandeliers", "Semi-Flush Mounts", "Pendants", "Track Lighting", "Mini Pendants", "Directional", "Island/Pool Table", "Recessed Lighting"], ["Outdoor", "Exterior", "Landscape Lighting"], ["Fans", "Fans", "Fan Accessories"], ["Lamps", "Lamps", "Shades"], ["Wall Fixtures", "Sconces", "Foyer/Hall Lanterns"], ["Specialty", "Undercabinet", "Light Bulbs", "Lighting Controls", "Glass", "Specialty Items", "Utility"], ["Home Decor", "Decor/Home Accents", "Furniture"]] 

Can i skip it like that?

 categories.each do |category, subcats| puts "The main category is #{category} and the sub categories are: " subcats.each do |subcat| puts "#{subcat}, " end end 
+4
source share
4 answers

When you have a simple array, the following things happen:

 arr = [1,2,3,4] arr.each do |x| px end 1 2 3 4 => [1,2,3,4] arr.each do |x,y| px py end 1 nil 2 nil 3 nil 4 nil => [1,2,3,4] 

therefore, if ruby ​​does not know what to put in the block argument, it simply sets it to nil . Now consider a nested array:

 arr = [[1,2],[3,4],[5,6]] arr.each do |x| px end [1, 2] [3, 4] [5, 6] => [[1,2],[3,4],[5,6]] arr.each do |x,y| px py end 1 2 3 4 5 6 => [[1,2],[3,4],[5,6]] 

In this case, ruby ​​assumes that you want to assign two elements of internal arrays to the block variables x and y . The same goes for hashes, where Ruby assigns the key and value to x and y :

 hash = {1 => 2, 3 => 4, 5 => 6} hash.each do |x,y| px py end 1 2 3 4 5 6 => {1=>2,3=>4,5=>6} 

If you do not have enough elements in nested arrays, the block variables are assigned nil . When there are too many of them, they are simply discarded:

 arr = [[1,2,3],[4,5],[6]] arr.each do |x,y| px py end 1 2 4 5 6 nil => [[1,2,3],[4,5],[6]] 

quite simple!

EDIT

As for your edited question: no, you cannot apply this 1: 1 code to Ruby, you will have to manually apply the splat ( * ) operator to subcats . Thus, ruby ​​assigns all remaining elements to the "splatted" block variable:

 categories.each do |category,*subcats| puts "The main category is #{category} and the sub categories are: " subcats.each do |subcat| puts "#{subcat}, " end end 

although I would generate a list of subcategories separated by commas:

 categories.each do |category,*subcats| puts "The main category is #{category} and the sub categories are: " puts subcats.join(', ') end 

EDIT 2 :

Oh, and you won’t process a huge ugly multidimensional array by defining many block parameters for your elements. You could probably iterate over it using nested loops, like in any other language, if only because you never know how many elements it contains.

+4
source

Let's start by breaking each method.

 a = [1,2,3,4,5] a.each do |num| puts num end # 1 # 2 # 3 # 4 # 5 

The do ... end is called block

This block takes one parameter (element in an array)

Method for passing parameters to block with | 's

If several arguments are added to the block:

 a.each do |num, x| puts num puts x end # 1 # # 2 # # 3 # # 4 # # 5 # 

x for each iteration nil .

Let's write our own method that uses blocks so you can see how they work.

 def my_each(a=[]) a.each do |x| yield x if block_given? end end my_each(a) do |num| puts num end 

Here yield x says execute the supplied block and pass x .

If you pass another parameter to your block , it will be nil . Why?

Our implementation of my_each knows nothing about the second parameter, so it does not yield anything, therefore it remains nil .

+4
source

The pipes you are talking about are a list of block "variable" parameters. In fact, this is some kind of pointer to a function, and pipes mark a list of parameters.

Check out the description of array.each .

This is not magic, the number of parameters is defined in the block, you cannot add more than this, if you do, they will not get the value. The reason is that there may be several “once”, it is probably hash.each, which has two parameters, a key and a value.

You can create your own functions using block parameters, read this .

For your iterative problem, you can use a hash, or you can write your own iterator.

+1
source

A few arguments for the block

Array # each iterates over an array object and passes either one object to a block or returns an enumerator. You can override this behavior, but #each is the wrong method if you need multiple values ​​at a time; see Enumerator # each_slice for an alternative.

Data structures

Your problem will be easier to solve with the correct data structure. Instead of an array, you should use a hash. For instance:

 categories = {"Bathroom"=>["Bathroom Fixtures", "Plumbing"], "Ceiling Fixtures"=>["Chandeliers", "Flush Mounts", "Mini Chandeliers"]} categories.each do |key, value| puts "#{key}:" value.each { |v| puts "\t%s" % v } end 

This returns:

 Bathroom: Bathroom Fixtures Plumbing Ceiling Fixtures: Chandeliers Flush Mounts Mini Chandeliers 
+1
source

All Articles