162

Let's say I have a Ruby array

a = [1, 2, 3, 4]

If I want all but the first item, I can write a.drop(1), which is great. If I want all but the last item, though, I can only think of this way

a[0..-2]   # or
a[0...-1]

but neither of these seem as clean as using drop. Any other built-in ways I'm missing?

5
  • According to globalnerdy.com/2008/07/10/… , drop is ruby 1.9, rather than ruby 1.8.6 Commented Oct 23, 2009 at 0:13
  • What about the performance.. If I were to use these answers in iterations for 1000s of times.. which one would win? Commented Aug 24, 2011 at 9:16
  • In other words, which solution does not traverse array under the hood? Commented Aug 24, 2011 at 9:25
  • an other way, a - ([a.size]) Commented Mar 30, 2019 at 11:15
  • WHY for the love of god, is pop destructive and drop not? Commented Aug 23, 2021 at 14:23

15 Answers 15

155

Perhaps...

a = t               # => [1, 2, 3, 4]
a.first a.size - 1  # => [1, 2, 3]

or

a.take 3

or

a.first 3

or

a.pop

which will return the last and leave the array with everything before it

or make the computer work for its dinner:

a.reverse.drop(1).reverse

or

class Array
  def clip n=1
    take size - n
  end
end
a          # => [1, 2, 3, 4]
a.clip     # => [1, 2, 3]
a = a + a  # => [1, 2, 3, 4, 1, 2, 3, 4]
a.clip 2   # => [1, 2, 3, 4, 1, 2]
Sign up to request clarification or add additional context in comments.

4 Comments

Adding a method to Array seems like the best approach to me. Most projects end up with a core_ext.rb file with little extensions like this. Some libraries are practically all extensions like this: ActiveSupport for example.
That's interesting, I've wondered how common it is. What do people think about poetry mode?
How does someone submit this clip method to the Ruby community? I really think this should be in there, and monkey-patching is the Wrong Thing.
I like the a.reverse.drop(1).reverse strategy. ;-)
105

Out of curiosity, why don't you like a[0...-1]? You want to get a slice of the array, so the slice operator seems like the idiomatic choice.

But if you need to call this all over the place, you always have the option of adding a method with a more friendly name to the Array class, like DigitalRoss suggested. Perhaps like this:

class Array
    def drop_last
        self[0...-1]
    end
end

6 Comments

it just seems like lots of clutter, when drop is so clean. it's not a big deal, but hard to confirm at a brief glance that it is exactly right. I prefer your name, drop_last, by the way...
Curious, what is the meaning of that third .?
... means not including the last item, The -1 is the last item. If you did two dots, then it would include the last item, three dots then it won't include the last item.
Be careful using the slice operator for getting the tail of array: it may return nil: [][1..-1] -> nil, [][0...-1]->[]
When getting tail of array ActiveSupport's Array#from may be of use api.rubyonrails.org/classes/Array.html#method-i-from.
|
69

Another cool trick

>> *a, b = [1,2,3]
=> [1, 2, 3]
>> a
=> [1, 2]
>> b
=> 3

4 Comments

Nice, wasn't aware Ruby had this; so a, *b = [1,2,3] would let us look at the array in a classic car/cdr fashion.
Honestly, I don't even remember writing that comment :D deleted it now...
Soooo simple and nice. THX
Official docs for this syntax at ruby-doc.org/3.1.2/syntax/…
56

I do it like this:

my_array[0..-2]

5 Comments

This works fantastically. I'm confused why there are a number of far more complex answers. Is this a new Ruby addition?
@JoshuaPinter it's there since the beginning. I agree that it's by far the best approach.
@JoshuaPinter because it's already in the question itself
@ilya_i Ha! You're absolutely right. I doubt that I noticed that 7 years ago. Thanks for pointing it out, and the laugh.
BTW, I like my_array[...-1]
50

If you want to perform a pop() operation on an array (which is going to result in the last item deleted), but you're interested in obtaining the array instead of a popped element, you can use tap(&:pop):

> arr = [1, 2, 3, 4, 5]
> arr.pop
=> 5
> arr
=> [1, 2, 3, 4]
> arr.tap(&:pop)
=> [1, 2, 3]

3 Comments

This answer is the one that semantically makes most sense to this user. The mirror operations are at hand and it uses words that are easy to interpret && concise.
That is some slick Ruby.
This gets the job done, but I think it falls in the category of code that's not particularly clear about what it's doing. Over the years I've found that I read more code than I write, so I place a premium on choices that make code easier to read.
27

How about augmenting the drop method itself, for example like this?

class Array
  def drop(n)
    n < 0 ? self[0...n] : super
  end
end

Then you can use a negative size to remove elements from the end, like so:

[1, 2, 3, 4].drop(-1) #=> [1, 2, 3]
[1, 2, 3, 4].drop(-2) #=> [1, 2]

Comments

19

a[0...-1] seems like the best way. The array slicing syntax was created for exactly this purpose...

Alternatively, if you don't mind modifying the array in place, you could just call a.pop:

>> a = [1, 2, 3, 4]
>> a.pop
>> a
=> [1, 2, 3]

1 Comment

you can also assign last element to a variable for later use: b = a.pop
15

To get rid of the last element in one line with the remainder returning

[1, 2, 4, 5, 6].reverse.drop(1).reverse

Seriously though,

[1,2,3,4][0..-2]
#=> [1,2,3]

Comments

11

This is the way:

[1,2,3,4,5][0..-1-1]

But let's explain how this works:

a = [1,2,3,4,5]

Next example will return all records, from 0 position to last

a[0..-1]
=> [1, 2, 3, 4, 5]

Next example will return records from 1 position to last

a[1..-1]
=> [2, 3, 4, 5]

And here you have what you need. Next example will return records from 0 position to last-1

a[0..-1-1]
=> [1, 2, 3, 4]

2 Comments

You can write -2 instead of -1-1.
You could do a[0...-1] (notice the 3rd dot) and get the same result as a[0..-1-1], because the third dot tells it to exclude the ending element. See this for more details.
7

Have you tried "take"

a.take(3) 

1 Comment

in general, this is a.take(a.size - 1); yes, I considered this option.
2

Answer: a.τwτ, but you have to install Pyper first...

Pyper intro: Do you know Lispy car and cdr returning "first" and "rest" of the array? Just for the needs like yours, I made an extension of this Lispy mechanism. It's called pyper, and it allows you to access also 2nd, rest from 2nd, 3rd, rest from 3d, and also last, everything except last etc. That wouldn't be much to write about, but it also allows letter composition, just like caar, cadr, cdadar etc. known from Lisp:

# First, gem install pyper
require 'pyper'
include Pyper
a = %w/lorem ipsum dolor sit amet/
# To avoid confusion with other methods, and also because it resembles a rain gutter,
# Greek letter τ is used to delimit Pyper methods:
a.τaτ #=> "lorem"
a.τdτ #=> ["ipsum", "dolor", "sit", "amet"]
a.τbτ #=> "ipsum"
a.τeτ #=> ["dolor", "sit", "amet"]
a.τcτ #=> "dolor" (3rd)
a.τzτ #=> "amet" (last)
a.τyτ #=> "sit" (2nd from the end)
a.τxτ #=> "dolor" (3rd from the end)

and finally, the answer to your question:

a.τwτ #=> ["lorem", "ipsum", "dolor", "sit"] (all except last)

There is more:

a.τuτ #=> ["lorem", "ipsum", "dolor"] (all except last 2)
a.τ1τ #=> ["lorem", "ipsum"] (first 2)
a.τ8τ #=> (last 2)
a.τ7τ #=> (last 3)

Compositions:

a.τwydτ #=> "olor" (all except 1st letter of the last word of all-except-last array)

There are also more command characters than just a..f, u..z and 0..9, most notably m, meaning map:

a.τwmbτ #=> ["o", "p", "o", "i"] (second letters of all-except-last array)

But other command characters are too hot and not very easy to use at the moment.

1 Comment

I'm reading this on April 1st so I assumed it was a joke... nope. Had no idea this stuff existed for Ruby. :)
1

This makes a new array with all but the last elements of the original:

ary2 = ary.dup
ary2.pop

Note that a few others have suggested using #pop. If you are ok modifying the array in place, that's fine. If you aren't ok with that, then dup the array first, as in this example.

3 Comments

A few others have suggested using #pop, if you are ok modifying the array in place. If you aren't ok with that, then dup the array first, as in this example.
This is an important comment. You might just add it to your answer as well. Since #pop doesn't have a bang (!), the fact that it modifies the original array might escape people.
This is way inefficient if the array is big.
1

I often find myself wanting all but the last n elements of an array. I've rigged up my own function to do this in a way that I find more readable than other solutions:

class Array
  def all_but_the_last(n)
    self.first(self.size - n)
  end
end

Now you can do the following:

arr = ["One", "Two", "Three", "Four", "Five"]
# => ["One", "Two", "Three", "Four", "Five"]

arr.all_but_the_last(1)
# => ["One", "Two", "Three", "Four"]

arr.all_but_the_last(3)
# => ["One", "Two"]

arr.all_but_the_last(5)
# => []

arr.all_but_the_last(6)
# ArgumentError: negative array size

I've deliberately allowed for the ArgumentError so that the caller is responsible for how they use this method. I'd love to hear comments/criticisms of this approach.

Comments

1

You can use:

[1,2,3,4].tap(&:pop)
=> [1, 2, 3]

2 Comments

This answer is correct, but it's the weirdest possible way to do it
If the requirement is to mutate the same object but have it chain-able then this answer is great.
-1
a = [1,2,3,4]

a[0..(a.length - 2)]
=> [1,2,3]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.