Slicing and Splicing: Ruby and Javascript Gotchas

Ruby and Javascript both have a method called slice. They looks similar but they work differently. Let’s look at some examples.

Guess what the following Ruby code returns.

[1,2,3,4,5].slice(0,2)

And it returns…

[1,2]

It’s a new array containing the first 2 values of the calling array starting from index 0.

What about this Javascript?

[1,2,3,4,5].slice(0,2);

[1,2]

Javascript returns an array with the values starting from index 0 up to, but not including index 2.

I know. It looks the same. But don’t fall for it. Here’s another example that actually shows this difference.

Ruby

[1,2,3,4,5].slice(2,3)

[3, 4, 5]

Javascript

[1,2,3,4,5].slice(2,3);

[3]

In Ruby the method is slice(start, length) where start is the starting index, length is the number of values to take up to.

In Javascript the method is slice(begin, end) where begin is, again, the starting index, but end is the index to take up to, non-inclusive.

slice also has a single argument format

Ruby

[1,2,3,4,5].slice(2)

3

Javascript

[1,2,3,4,5].slice(2);

[3, 4, 5]

Here, Ruby’s slice(index) simply returns the value at index.

And Javascript’s slice(begin) returns a new array starting from the value at the index begin all the way through to end of the array.

Ruby and Javascript both also have String.slice that work the same as their respective array-based counterparts, but on characters in the string.

Ruby

"hello".slice(2)
"hello".slice(2,3)
"l"
"llo"

Javascript

"hello".slice(2);
"hello".slice(2,3);
"llo"
"l"

Javascript also has a splice method. It’s just like slice but it removes the selected values from the original array. It’s like Ruby’s slice! method.

Ruby

arr = [1,2,3,4,5]
arr.slice!(1,1)     # returns  [2]
arr                 # contains [1,3,4,5]

Javascript

arr = [1,2,3,4,5];
arr.splice(1,1);     // returns  [2]
arr;                 // contains [1,3,4,5]

Okay last example.

Ruby

str = "hello"
str.slice!(1,1)     # returns  "e"
str                 # contains "hllo"

Javascript

str = "hello";
str.splice(1,1);

TypeError: str.splice is not a function

Whoops! There’s no built-in String.splice method in Javascript. Arrays only!

If you have to switch between Ruby and Javascript a lot, be careful out there and always keep your methods straight.

And there are more differences than what I mentioned here. Ruby’s slice methods can take a range value as a parameter. Javascripts splice can be used to insert values. And more! Check out the docs for details.

References

Class Methods and Variables in Ruby

Class methods are methods that apply to a particular class in Ruby. Rather than acting on a particular instance of a class, they act on the class blueprint itself, i.e. the instance of the Class object.

Class methods can’t access instance variables. But instance methods can access class variables.

The most common way to create a class method in Ruby is to use the self keyword. This refers back to the class object itself.

class IceCream
  @@total_scoops = 0

  def self.increment_scoops
    @@total_scoops +=1
  end

  def self.scoops_served
    @@total_scoops
  end
end

In this example, we use the class variable @@scoops to keep track of total ice cream scoops served. If you aren’t careful, class variables can cause you problems in Ruby. Consider the following example.

class FrozenYogurt < IceCream
  @@total_scoops = 0
end

What would you expect the output to be when you ran the following code?

puts "Serving ice cream."
IceCream.increment_scoops
puts "#{IceCream.scoops_served} scoops of ice cream served."

puts "Serving froyo."
FrozenYogurt.increment_scoops
puts "#{FrozenYogurt.scoops_served} scoops of froyo served."

The answer is…

Serving ice cream.
1 scoops of ice cream served.
Serving froyo.
2 scoops of froyo served.

In Ruby, classes share class variables with their ancestors. Both of these classes are operating on the same @@scoops variable. This is often not the desired behavior. When you don’t want this, you should use what are sometimes called “class instance variables”. These are really just instance variables, but they’re applied to the instance of the Class class, the blueprint object. You create one by defining an instance variable right in the class body. You can only refer to these variable within class methods. Let’s try it.

class IceCream
  @total_scoops = 0

  def self.increment_scoops
    @total_scoops +=1
  end

  def self.scoops_served
    @total_scoops
  end
end
class FrozenYogurt < IceCream
  @total_scoops = 0
end

puts "Serving ice cream."
IceCream.increment_scoops
puts "#{IceCream.scoops_served} scoops of ice cream served."

puts "Serving froyo."
FrozenYogurt.increment_scoops
puts "#{FrozenYogurt.scoops_served} scoops of froyo served."

This code outputs:

Serving ice cream.
1 scoops of ice cream served.
Serving froyo.
1 scoops of froyo served.

Now both of our class instances, IceCream and Froyo, each have their own @scoops variable. Remember, classes in Ruby are objects too, specifically instances of the Class class. It’s a little confusing but all we are doing here is adding instance variables to our class instance. This avoids the issue with class variables being inherited by child classes.

A Spooky Ruby Class

In Ruby, pretty much everything is an object. The number 5 is an object. The string “boo!” is an object. When we want to be able to create a lot of similar objects, we use classes. Classes are objects too.

A class is like a blueprint for something we call instances. Each object you create using a class is called an instance. Ruby has a lot of built-in classes. Let’s take a quick look at the String class. To create a new instance of any class, we call the new method. So to create instances of the String class we could type:

greeting = String.new("Hi!")
#=>"Hi!"
formal_greeting = String.new("Hello!")
#=>"Hello!"

These String instances are each their own objects but they also share a lot of methods that they get from the String class.

greeting.size
#=>3
formal_greeting.size
#=>6

The built-in classes are great and all, but let’s try making our own. Let’s make a class for spooky monsters.

class Monster
  def initialize(spookiness, spooky_grabber)
    @spookiness = spookiness
    @spooky_grabber = spooky_grabber
  end
end

A lot of stuff is actually going on here so let’s break it down a bit.

class Monster

The class keyword declares the beginning of a class. Our class can be referenced later by its class name, Monster.

``ruby def initialize(spookiness, spooky_grabber)


Here we declare the `initialize` method. `initialize` is a special method. Whenever a new instance is of a class created, that class's `initialize` method is automatically called on that instance. It also receives all the arguments that were passed to the new method. This is the place to set up your object.

```ruby
    @spookiness = spookiness
    @spooky_grabber = spooky_grabber

Just assigning the parameters to some variables. But what are those @ signs? Those @ signs indicate that these are instance variables. Each instance keeps track of it’s own instance variables. Each monster will have it’s own individual spookiness and spooky grabber.

Now we can create some instances!

ghost = Monster.new(1, "a chilling presence")
skeleton = Monster.new(2, "a bony claw")
fishman = Monster.new(2, "a damp webbed hand")
cthulu = Monster.new(3, "several eldritch tentacles")

The good news is we have our instances. The bad news is, they don’t really do much yet. Let’s add some methods to our class so we can at least inspect the values we set up.

class Monster
  def initialize(spookiness, spooky_grabber)
    @spookiness = spookiness
    @spooky_grabber = spooky_grabber
  end

  def spookiness
    @spookiness
  end

  def spooky_grabber
    @spooky_grabber
  end
end

We can use these methods to see the values of our instance variables.

ghost.spookiness
#=>1

That seems like a lot of code to write just to read each variable. I know what you’re thinking. This is Ruby. There’s gotta be a shorter way. That’s where the attr_reader keyword come into play.

class Monster
  attr_reader "spookiness"
end

is the same as

class Monster
  def spookiness
    @spookiness
  end
end

Great let’s add it to our code.

class Monster
  attr_reader "spookiness", "spooky_grabber"

And let’s add some methods to print spooky text to the console.

class Monster
  attr_reader "spookiness", "spooky_grabber"

  def initialize(spookiness, spooky_grabber)
    @spookiness = spookiness
    @spooky_grabber = spooky_grabber
  end

  def spook
    print "You suddenly feel #{@spooky_grabber} brush your shoulder"
    puts  "." * spookiness
  end

  def scare
    print "You suddenly feel #{@spooky_grabber} wrap around your neck"
  puts  "!" * spookiness
  end
end

The spook and scare methods are called ‘instance methods’. That’s because each instance can use them. And when you call an instance method it has access to its caller’s instance variables.

ghost.spook
#  You suddenly feel a chilling presence brush your shoulder.
#=>nil
skeleton.scare
#  You suddenly feel a bony claw wrap around your neck!!
#=>nil

Sometimes you have a method that’s related to a class, but doesn’t really apply to individual instances. You can create ‘class methods’ by using the self keyword. Here’s an example. Pay attention to how self is used.

class Monster
  def self.merge (monster_a, monster_b)
    spookiness = monster_a.spookiness + monster_b.spookiness
    grabber = monster_a.spooky_grabber + " and " +  monster_b.spooky_grabber
    Monster.new(spookiness, grabber)
  end
end

You call class methods like this:

spook_team = Monster.merge(fishman, cthulu)

This method combines the properties of two objects then creates and returns a new instance. The created instance also has all the same methods as the other instances.

spook_team.scare
#  You suddenly feel a damp webbed hand and several eldritch tentacles wrap around your neck!!!!!
#=>nil

Great, nice and spooky!

And here’s the full code for our class.

class Monster
  attr_reader "spookiness", "spooky_grabber"

  def initialize(spookiness, spooky_grabber)
    @spookiness = spookiness
    @spooky_grabber = spooky_grabber
  end

  def spook
    print "You suddenly feel #{@spooky_grabber} brush your shoulder"
    puts  "." * spookiness
  end

  def scare
    print "You suddenly feel #{@spooky_grabber} wrap around your neck"
    puts  "!" * spookiness
  end

  def self.merge (monster_a, monster_b)
    spookiness = monster_a.spookiness + monster_b.spookiness
    grabber = monster_a.spooky_grabber + " and " +  monster_b.spooky_grabber
    Monster.new(spookiness, grabber)
  end
end