Ruby Facets: Arrays

Luca Pette

If you’re not familiar with this series, please consider reading the introduction.

Arrays in Ruby are pretty important.

I know…reading that sentence you might be thinking “Arrays are important in all languages” but that’s not really true. There are a lot of languages out there that encourage you to use specialized collections. Rubyists tends to stay with basic types, it’s one of the truly beautiful aspects of this language that is difficult to get.

Anyway, working with arrays often involves a bit of manipulation of the array itself. For this reason, the require 'facets/array' will give you a lot of interesting methods that can help solve your specific problems with better expressiveness. Before we start to dig into the various methods the Facets library offer, just add the following to your irbrc/pryrc:

[gist id=”2029565″]

I mean, you can do it if you haven’t something similar yet. It’s just a little method that can be useful when you want to play with arrays:

[gist id=”2029599″]

If you get a picture of what the Ruby facets library can offer try downloading the code in your workspace directory. Assuming you’re using Git, you can do the following:

[gist id=”2029629″]

And if you, like me, use GitHub all the time then the odds are you’ll really like hub. It’s a little tool that works perfectly when you alias git to it. It allows you to do crazy stuff like git clone rails/rails, guess what git fork will do!

Now that you have the library locally, you can cd to the lib/core/facets/array directory. There are a lot of files and it can be a bit overwhelming to read them all. But we’re here to explore it and we just need a criteria to do so. We can group the extensions to the Array class in the following way:

  • Methods that add semantic values to the Array class.

  • Methods that perform some kind of calculation and return an array.

  • Methods that perform some kind of calculation of the array and return a string or an hash.

I deliberately omitted some methods because they have been introduced in Ruby recent version. I mean, you know that Ruby 1.8.7 introduced such a nice method?

[gist id=”2078152″]

It’s just an example and I recommend you to grep for defined? in the Array directory, because the Ruby standard library contains so many nice examples. I found interesting methods like permutation just reading the source of the Array extension in the Ruby Facets library.

The first category (the one about semantic stuff) is my favorite kind of extension from a general point of view. I think the key to the success of Ruby is in its expressiveness. The real power you have is coding in the language of the domain. This kind of extension, used with care, can add real value to a project and makes a programmer happy. You can consider the following extensions like an source of inspiration for your specific problems and projects.

So, let’s start with semantic stuff. The following:

[gist id=”2078599″]

is just an alias of the include? method and it can be considered silly at a first sight. But I’m pretty sure that even a simple extension can make the difference in a project. Another interesting example is the following:

[gist id=”2078713″]

Yes it’s just the opposite of empty? and in some cases can be useful to use this kind of extension because it promotes readability.

The last extension I want to talk about in this category is really a good example of how little methods can add real value to a piece of code. Consider the following trivial example:

[gist id=”2079403″]

And now consider the following:

[gist id=”2079462″]

It’s fair to say that the second version is really easier to read. And easier to read means easier to maintain.

Now, it’s time to take a look at another category of methods. The ones that work with the array itself and return something else. So, let’s start with two methods I like a lot, before and after. These two methods are opposites of the other and they simple return the element before (or the one after) the one you pass to it. Take a look at the following example:

[gist id=”2079671″]

It’s nice, isn’t it?

Now, suppose you have a array of people and you want to get all the people with the same age. The facets library makes it pretty simple:

[gist id=”2079850″]

It’s pretty useful and I recommend you read the code because the implementation is interesting.

Another very common problem we run into while working with arrays is joining them with a separator and return a string. It’s so common that Ruby offers an Array#join method that works for simple cases. The Ruby Facets library offers a version of this method called Array#conjoin that has a very powerful API. The method accept four different options:

  • space
  • spacer
  • first
  • last

You can see how them works in the following example:

[gist id=”2107187″]

It’s a very flexible API and the method even accepts a block. This feature allows very crazy transformations. The block accepts three parameters:

[gist id=”2107461″]

They are really crazy, aren’t they?

Now, it’s time to move towards a number and how we can extract nice information from arrays. Suppose, for example, you want to know how much the information in an array changes. Well, the Ruby Facets library allows the following:

[gist id=”2108116″]

So, a higher number means more change. Pretty neat, huh?. Now, instead, suppose you want to know which element in your array is the most frequently present. In statistics this is called the “mode” and the Ruby Facets library offers an Array#mode method:

[gist id=”2108584″]

Nice. And if you want to know how many times each element appears in an array, you can use Array#probability:

[gist id=”2108995″]

Don’t forget to take a look at the implementation for this method and for the other ones too. They are very interesting to read.

It’s time to talk about the last category of methods. These methods perform some calculation (or provides a way to do it) on the array and return another array as the result. The first method we’ll see is just a shortcut for a longer calculation you can do with the standard Enumerable methods:

[gist id=”2109237″]

As you can imagine, it just chains Array#flatten and Array#compact. It’s a simple extension but it can still help you improve the readability of your code. The library offers another simple extension that can help. It’s Array#delete_unless and it’s just the inverse of Array#delete_if. In some situations, it can be more natural to express intent via Array#delete_unless. Another interesting extension that starts with delete is Array#delete_values and it allows you to delete multiple elements from an array:

[gist id=”2109596″]

Simple and effective. Well, if you are dealing with duplicates or non-unique elements you can try to require facets/array/nonuniq. This one will give you the following methods:

  • nonuniq / nonuniq!
  • duplicates
  • occurrent

Let’s take a look at them in turn. The Array#nonuniq method has a very self-explanatory name. Indeed, you can use it to extract an array of non-unique elements from a given array:

[gist id=”2109880″]

Of course the Array#nonuniq! method just does the same thing but modifies its receiver. The library offers another method with a self-explanatory name, Array#duplicates. It returns the array of duplicate elements and accept a minimum number for inclusion in the returned array:

[gist id=”2113150″]

Now consider the situation where you have to ensure that an array has always the same size, 5 for example, and when your input has a size less then 5 you have to add a particular element to your input. Well, the Array#pad method can help you to solve this problem concisely:

[gist id=”2113444″]

And, as you’ve seen in the example, you can pass in a negative number if you want to pad the array from the head.

The last method I want to talk about in this category is very powerful and really can help in some situation. Furthermore, the implementation is worth reading it. We’re talking about Array#recurse that allows you to apply a block to an array recursively:

[gist id=”2113839″]

Very powerful and expressive.

OK, that’s all for now. I hope you enjoyed it! Stay tuned if you liked this one because other articles are coming!