157

With a list in Python I can return a part of it using the following code:

foo = [1,2,3,4,5,6]
bar = [10,20,30,40,50,60]
half = len(foo) / 2
foobar = foo[:half] + bar[half:]

Since Ruby does everything in arrays I wonder if there is something similar to that.

Josh Lee
  • 171,072
  • 38
  • 269
  • 275
Christian Stade-Schuldt
  • 4,671
  • 7
  • 35
  • 30

6 Answers6

243

Yes, Ruby has very similar array-slicing syntax to Python. Here is the ri documentation for the array index method:

--------------------------------------------------------------- Array#[]
     array[index]                -> obj      or nil
     array[start, length]        -> an_array or nil
     array[range]                -> an_array or nil
     array.slice(index)          -> obj      or nil
     array.slice(start, length)  -> an_array or nil
     array.slice(range)          -> an_array or nil
------------------------------------------------------------------------
     Element Reference---Returns the element at index, or returns a 
     subarray starting at start and continuing for length elements, or 
     returns a subarray specified by range. Negative indices count 
     backward from the end of the array (-1 is the last element). 
     Returns nil if the index (or starting index) are out of range.

        a = [ "a", "b", "c", "d", "e" ]
        a[2] +  a[0] + a[1]    #=> "cab"
        a[6]                   #=> nil
        a[1, 2]                #=> [ "b", "c" ]
        a[1..3]                #=> [ "b", "c", "d" ]
        a[4..7]                #=> [ "e" ]
        a[6..10]               #=> nil
        a[-3, 3]               #=> [ "c", "d", "e" ]
        # special cases
        a[5]                   #=> nil
        a[6, 1]                #=> nil
        a[5, 1]                #=> []
        a[5..10]               #=> []
dlouzan
  • 675
  • 7
  • 16
Paige Ruten
  • 172,675
  • 36
  • 177
  • 197
  • 6
    why is a[5, 1] different from a[6, 1] ? – dertoni Mar 01 '11 at 10:37
  • 4
    @dertoni: http://stackoverflow.com/questions/3219229/why-does-array-slice-behave-differently-for-length-n – michelpm Dec 07 '11 at 23:22
  • 34
    `a[2..-1]` to get from the 3rd element to the last one. `a[2...-1]` to get from the 3rd element up to the second last element. – Clever Programmer Nov 24 '15 at 09:03
  • 1
    @Rafeh cheers, been wondering how this junk works, -1 did the trick – pronebird Dec 02 '15 at 08:36
  • 2
    @CleverProgrammer syntax is much closer to Python than the accepted answer. I love Ruby, but I must say that Python's syntax is shorter and clearer. As a bonus, it is possible to specify the step : `range(10)[:5:-1]` – Eric Duminil Dec 02 '16 at 12:08
30

If you want to split/cut the array on an index i,

arr = arr.drop(i)

> arr = [1,2,3,4,5]
 => [1, 2, 3, 4, 5] 
> arr.drop(2)
 => [3, 4, 5] 
Raj
  • 22,346
  • 14
  • 99
  • 142
20

Ruby 2.6 Beginless/Endless Ranges

(..1)
# or
(...1)

(1..)
# or
(1...)

[1,2,3,4,5,6][..3]
=> [1, 2, 3, 4]

[1,2,3,4,5,6][...3]
 => [1, 2, 3]

ROLES = %w[superadmin manager admin contact user]
ROLES[ROLES.index('admin')..]
=> ["admin", "contact", "user"]
PGill
  • 3,373
  • 18
  • 21
  • 1
    A little bit of explanation would be nice, especially since the `[..3]` returns a non-intuitive result. – not2qubit Jan 26 '22 at 22:34
18

You can use slice() for this:

>> foo = [1,2,3,4,5,6]
=> [1, 2, 3, 4, 5, 6]
>> bar = [10,20,30,40,50,60]
=> [10, 20, 30, 40, 50, 60]
>> half = foo.length / 2
=> 3
>> foobar = foo.slice(0, half) + bar.slice(half, foo.length)
=> [1, 2, 3, 40, 50, 60]

By the way, to the best of my knowledge, Python "lists" are just efficiently implemented dynamically growing arrays. Insertion at the beginning is in O(n), insertion at the end is amortized O(1), random access is O(1).

Manuel
  • 6,461
  • 7
  • 40
  • 54
  • Do you mean to use the bar array in the second slice? – Samuel Mar 29 '09 at 20:24
  • FYI: `slice!()` doesn't modify the array in place, rather, it "Deletes the element(s) given by an index (optionally up to length elements) or by a range." per http://ruby-doc.org/core-2.2.3/Array.html#method-i-slice-21 – Joshua Pinter Aug 26 '15 at 18:02
  • @joshuapinter Thank you self! I just got bit by this (again, apparently). – Joshua Pinter Mar 08 '21 at 22:26
9

another way is to use the range method

foo = [1,2,3,4,5,6]
bar = [10,20,30,40,50,60]
a = foo[0...3]
b = bar[3...6]

print a + b 
=> [1, 2, 3, 40, 50 , 60]
user3449311
  • 205
  • 2
  • 5
3

I like ranges for this:

def first_half(list)
  list[0...(list.length / 2)]
end

def last_half(list)
  list[(list.length / 2)..list.length]
end

However, be very careful about whether the endpoint is included in your range. This becomes critical on an odd-length list where you need to choose where you're going to break the middle. Otherwise you'll end up double-counting the middle element.

The above example will consistently put the middle element in the last half.

labyrinth
  • 13,397
  • 7
  • 35
  • 44
  • You can use `(list.length / 2.0).ceil` to consistently put the middle element in the first half if you need that. – Sorashi May 03 '19 at 13:32