JasonInCode

Ruby, the Missing LINQ Blues

March 28, 2013

Having spent a lot of time in the realm of .NET I grew accustomed to LINQ or Language Integrated Queries. LINQ is a way to query collections of objects in a SQL-like DSL. While there is not a direct competitor to the DSL provided by LINQ in Ruby there are several methods that are equivalent to the extension methods provided by LINQ that exist in Ruby.

Where()

One of the most used (just guessing) LINQ methods would be Where(), in .NET allows you to provide a Lambda that is used to filter a collection of objects.

var ints = new[] { 13, 42, 96, 2, 83 };

var evens = ints
    .Where(x => x % 2 == 0);

foreach (int i in evens)
{
    Console.WriteLine(i);
}

And in Ruby this functionality would be achieved with select.

ints = [ 13, 42, 96, 2, 83 ]

evens = ints
    .select { |x| x % 2 == 0 }

puts evens

Both examples output 42, 96, and 2. In the Ruby example find_all would be interchangeable with select.

Select()

In .NET to map a set of items to a different set of items one would use the Select() method. This method is also useful for creating projections of a collection.

var words = new[] { "Hello", "JasonInCode", "Readers" };

var shouting = words
    .Select(x => x.ToUpper();

foreach (String s in shouting)
{
    Console.WriteLine(s);
}

In Ruby you have two options again map and collect. Both are used in the same manner, supply a block that receives a single parameter that is an individual item in the collection and returns the object that it is to be mapped to.

words = %w{ Hello JasonInCode Readers }

shouting = words
    .map { |x| x.upcase }

puts shouting

Both the .NET and Ruby examples will output “HELLO JASONINCODE READERS”.

Count()

In .NET you can call the Count() method with no arguments to count the entire collection or provide a Lambda to filter which items get counted. It’s usage in .NET looks as follows:

var ints = new[] { 13, 42, 96, 2, 83 };

var all = ints
    .Count();

var some = ints
    .Count(x => x >= 42);

var one = ints
    .Count(x => x == 42);

Console.WriteLine(all);
Console.WriteLine(some);
Console.WriteLine(one);

That would output 5, 3, and 1. To do the similar task in Ruby one would use the count method as so:

ints = [ 13, 42, 96, 2, 83 ]

all = ints
    .count

some = ints
        .count { |x| x >= 42 }

one = ints
    .count 42

puts all, some, one

Any()

Any() is useful when you need to determine if a collection contains one or more items that pass a provided test.

var words = new[] { "C#", "Ruby", "VisualBasic", "Java" };

var yes = words
    .Any(x => x.Contains("y"));

var no = words
    .Any(x => x.Contains("x"));

Console.WriteLine(yes);
Console.WriteLine(no);

Ruby collections have the any? method that serves the very same purpose.

words = %w{ C# Ruby VisualBasic Java }

yes = words
    .any? { |x| x.include? "y" }

no = words
    .any? { |x| x.include? "x" }

puts yes, no

Both example will output true followed by false.

When switching from .NET to Ruby it is nice to find some of my old creature comforts came along for the ride. And once I started learning more about Ruby not only did I find the niceties listed above but a whole list of methods that make Ruby an enjoyable programming experience.

Bonus

If you have spent time in .NET then you likely come across extension methods. A lot of LINQ’s methods are actually implemented as extension methods. Extension methods are .NET’s way of adding methods to a class that is already implemented.

public statis class StringExtension
{
    public static bool Palindrome(this String me)
    {
    var arr = me.ToLower.ToCharArray();
    Array.Reverse(arr);
    return me.ToLower == new String(arr);
    }
}

Ruby, having an open class implementation, has a much better approach then extension methods. Since a class is never final a developer is free to add and redefine methods.

class String
    def palindrome?
    self.downcase == self.downcase.reverse
    end
end

Jason Worley

Written by Jason Worley who lives and works in Indianapolis building useful things.