Golang for Rubyists. Part 7. Ruby and Golang, methods comparison

Hello, my dear friends. We all love Ruby (you too, right?) for its expressiveness and a set of useful methods out of the box. It would be a pleasure if when start using a new language, you had the similar methods or at least familiar ways to achieve same goals. Let’s try to pick a few useful methods from Ruby and find out, are there any equivalents in Golang for them.


pablo-hermoso-422530-unsplash

Photo by Pablo Hermoso on Unsplash

Array

Let’s start with something simple:

2.2.1 :001 > array = [1,3,5,7]
 => [1, 3, 5, 7] 
2.2.1 :002 > array.shift
 => 1 
2.2.1 :003 > array
 => [3, 5, 7] 
2.2.1 :004 > array.push(1)
 => [3, 5, 7, 1] 
2.2.1 :005 > array.unshift(array.pop)
 => [1, 3, 5, 7] 

So, we used four methods here: shift, unshift, push, pop. Now let me try to do something similar in Golang:

package main

import ("fmt")

func main() {
	// Initialize the slice
	array := []int{1, 3, 5, 7}
	fmt.Println(array)
	
	// Shift
	x := array[0]
	fmt.Println(x)
	array = array[1:]
	fmt.Println(array)
	
	// Push
	array = append(array, x)
	fmt.Println(array)
	
	// Pop and Unshift
	x, array = array[len(array)-1], array[:len(array)-1]
	array = append([]int{x}, array...)
	fmt.Println(array)
}

(https://play.golang.org/p/wNgO9LeX514)

Not so short and expressive, huh? But still pretty straightforward. The only confusion I can anticipate is the last example, with pop and unshift. Just to explain, at first we assign to x the last element of an array and we modify the array variable to be everything starting from the element at index 0 to the pre-last one. And at the next line we create a new slice with just x and append an array to it.
Also, you could notice here, that usage of [] to access an array/slice element is exactly the same as in Ruby (or most of the languages).

Let’s move on to the more solid examples:

2.2.1 :001 > array = [1, 3, 5, 7]
 => [1, 3, 5, 7] 
2.2.1 :002 > array.map { |a| a + 1 }
 => [2, 4, 6, 8] 
2.2.1 :003 > array
 => [1, 3, 5, 7] 
2.2.1 :005 > array.reject { |a| a % 3 == 0}
 => [1, 5, 7] 
2.2.1 :006 > array
 => [1, 3, 5, 7] 
2.2.1 :007 > array.sample
 => 7 
2.2.1 :008 > array
 => [1, 3, 5, 7] 
2.2.1 :009 > 
2.2.1 :010 > array.min
 => 1 
2.2.1 :011 > 
2.2.1 :012 > array.max
 => 7 
2.2.1 :014 > array.shuffle
 => [5, 7, 3, 1] 

Here we iterate through a collection, without original collection modification, then we reject some value also without a modification, then we get a random element from it, then we have a basic min/max methods and a shuffle method, which shuffles an array elements. Are you optimistic about Golang abilities here? Let it a try! Nope, let me just tell you, that Go is not a functional language. Not at all. Not for an iota. The only way you can achieve map/reduce/filter functionality is by writing for loops. Also, there are no built-in min/max functions, unfortunately. Surprisingly I was able to find a shuffle implementation:

package main

import (
	"fmt"
	"math/rand"
)

func main() {
	// Initialize the slice
	array := []int{1, 3, 5, 7}
	fmt.Println(array)
	rand.Shuffle(len(array), func(i, j int) {
		array[i], array[j] = array[j], array[i]
	})
	fmt.Println(array)
}

Btw it still uses two for loops under the hood: https://golang.org/src/math/rand/rand.go#L232. Interesting to notice here is that it’s possible to pass a function to a method in Golang.

Just to give an impression, here is the possible implementation of our own Filter function in Golang:

package main

import ("fmt")

func main() {
	array := []int{1, 3, 5, 7}
	filteredArray := Filter(array, func(i int) bool {
		return i%3 != 0
	})
	fmt.Println(filteredArray)
}

func Filter(in []int, fn func(int) bool) []int {
	out := make([]int, 0, len(in))

	for i := 0; i < len(in); i++ {
		current := in[i]

		if fn(current) {
			out = append(out, current)
		}
	}

	return out
}

(https://play.golang.org/p/LFvueXzO-BU. Here is a version, using interfaces, more generic, but still far from ideal https://play.golang.org/p/N9JvUXo7ugq)

As you can see, this will work only with slices of integers. In the link above you can find a more generic implementation. It uses reflection mechanism of Golang, which we didn’t explore yet.
So, this function may look cumbersome, but let’s take a look onto Ruby’s map method under the hood:

static VALUE
rb_ary_collect(VALUE ary)
{
    long i;
    VALUE collect;

    RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
    collect = rb_ary_new2(RARRAY_LEN(ary));
    for (i = 0; i < RARRAY_LEN(ary); i++) {
        rb_ary_push(collect, rb_yield(RARRAY_AREF(ary, i)));
    }
    return collect;
}

Looks pretty similar, isn’t it? Sure, in Ruby we don’t work with types – that’s the only difference. So, long story short, use for loops, that’s the way to go.
But also one thing I want to mention is that there is a more convenient way to iterate through a collection, here how it looks like (works for both maps and arrays):

package main

import ("fmt")

func main() {
	array := []int{1, 3, 5, 7}
	for i := range array {
	  fmt.Println(i * 2)
	}
}

(https://play.golang.org/p/6g28EUCvQ6f)

Hashes

One of the most heavily used data structures in Ruby is Hash.
Just kiddin, I have no idea, which is the most heavily used one. Let’s take a look at these examples, where we apply a couple of useful functions to it:

2.2.1 :001 > hash = { foo: 'bar', fizz: 'buzz', berlin: 'rain', munich: 'beer' }
 => {:foo=>"bar", :fizz=>"buzz", :berlin=>"rain", :munich=>"beer"} 
2.2.1 :003 >   
2.2.1 :004 >   hash.values
 => ["bar", "buzz", "rain", "beer"] 
2.2.1 :005 > hash.keys
 => [:foo, :fizz, :berlin, :munich] 
2.2.1 :006 > 
2.2.1 :008 > hash.values_at(:munich, :berlin)
 , > ["beer", "rain"] 

Nothing magical, just retrieving all values, all keys, and values by multiple keys, should be easy-peasy?

package main

import ("fmt")

func main() {
	hash := map[string]string{"foo": "bar", "fizz": "buzz", "berlin": "rain", "munich": "beer"}
	fmt.Println(hash)

	values := make([]string, 0, len(hash))
	for _, value := range hash {
		values = append(values, value)
	}
	fmt.Println(values)

	keys := make([]string, 0, len(hash))
	for key := range hash {
		keys = append(keys, key)
	}
	fmt.Println(keys)
	
	valuesAt := []string{hash["munich"], hash["berlin"]}
	fmt.Println(valuesAt)
}

(https://play.golang.org/p/YN0xnhly-r9)
aww
I am honestly not sure about the last one, probably there is a way to make it more readable. But anyway, beauty of Go in it’s straightforwardness 🙂

String

In Ruby, Strings can operate as arrays in most cases, but here we will pick a few string-specific methods:

2.2.1 :001 > str = " Star wars "
 => " Star wars " 
2.2.1 :002 > str.upcase
 => " STAR WARS " 
2.2.1 :003 > str.downcase
 => " star wars " 
2.2.1 :004 > str.strip
 => "Star wars"

Nothing wow-like for Ruby, boring stuff, but let’s check, what Golang has to offer for such basic things:

package main

import (
	"fmt"
	"strings"
)

func main() {
	str := " Star wars "
	fmt.Println(str)
	fmt.Println(strings.ToLower(str))
	fmt.Println(strings.ToUpper(str))
	fmt.Println(strings.TrimSpace(str))
}

(https://play.golang.org/p/9GvLs9yFIea)

Bang-bang! Such a bliss! Actually, Golang has a pretty extensive set of methods for strings manipulation, you can find way more in their official documentation: https://golang.org/pkg/strings/

Conclusion

There are much more to discover, so start using Golang for your pet projects or for your actual projects at work, f.e. if you plan to have some simple Lambda function, it’s a good place to introduce it, check out my past blog post on how to make it painlessly.
But for today that’s it, thank you for reading.
If you don’t want to miss the next posts, subscribe to my Twitter, tho I chat there for random topics, also I share some interesting posts I’ve discovered, as well as my own.
And psst, there is a big orange button beneath, using it, you can buy me an espresso macchiato, which I really love and will appreciate 😉

P.S. Thanks Claude for coffee and pleasant feedback, it helps staying motivated!


4 thoughts on “Golang for Rubyists. Part 7. Ruby and Golang, methods comparison

  1. Hello, I’m also a Rubyist and now trying to learn Golang (still a rookie). I appreciate the job you’ve done. May I repost your articles in my blog? For sure I will post your original link at the top of the article. Look forward to your reply. Thanks!

  2. Plase stop calling the language “Golang”: this word has two meanings: the keyword processed by a popular Internet search engine and the domain name of the main site hosting the Go project.

    The language is called “Go”.

    If you think otherwise, please be consistent and also use terms like “Rubylang”, “Javalang” and so on. Thanks.

    • Hi Konstantin,
      You’re totally right, the official language name is “Go”. However, “Golang” is not only a keyword and the domain name. It is also a Github repo name and Reddit’s subreddit name. Ruby, Java or others are never referenced as *lang, but it’s not the case for Go. I can assume that it happens due to the succinct naming, which can be misleading, as a word in general and as a name in particular (I mean the confusion with Go! language).
      However, thank you for emphasizing that. I think at least it makes a perfect sense to use the proper language name in the title.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.