rubyguides

Enumerable#drop

Drops (skips) the first n elements from an enumerator and returns the rest as an array.

In practice, drop is the version of slicing you reach for when the front of the collection is predictable but not useful. It keeps the tail intact, which makes it handy for paging, trimming warm-up records, or skipping a fixed prefix before further processing.

Signature

drop(n) -> Array

The signature is straightforward, but it is worth reading it as a promise about shape as much as about behavior. drop always returns an array, so the caller can expect the same kind of result whether the input came from a range, a list, or another enumerable.

Basic Usage

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

The return value is a new array containing the items after the skipped prefix. That makes the method easy to chain with other enumerable operations, especially when the original data should stay untouched.

drop counts from the start and returns everything after the first n elements. If n is zero, it returns a copy of the original array. If n exceeds the collection size, it returns an empty array — no error is raised.

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

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

Those edge values help show that the method is intentionally narrow. It does not try to guess what you meant, it just removes a fixed number of leading items and returns whatever is left.

How it differs from take

take and drop are complementary:

  • take(n) keeps the first n elements
  • drop(n) removes the first n elements and returns the rest
queue = [10, 20, 30, 40, 50]

queue.take(3)
# => [10, 20, 30]

queue.drop(3)
# => [40, 50]

take and drop are easiest to understand as complementary operations. One keeps a prefix, the other removes it, so together they make it simple to split a list into two clear parts without writing manual index logic.

You can combine them to split a collection into head and tail:

head, *tail = [1, 2, 3, 4, 5].partition.with_index { |_, i| i < 2 }
head
# => [[1, 2]]
tail
# => [[3, 4, 5]]

# Or with drop and take:
rest = [1, 2, 3, 4, 5].drop(2)
rest.first(2)  # same as take(2)
# => [3, 4]

That example shows why drop is often paired with another read of the result. You can peel off a fixed prefix, then inspect or slice the remaining tail with whatever method fits the next step.

How it differs from drop_while

  • drop skips elements by position (fixed count)
  • drop_while skips elements by condition (until the block returns false)
# drop skips exactly 2 elements regardless of their values
[1, 99, 2, 3].drop(2)
# => [2, 3]

# drop_while skips until a value fails the condition
[1, 99, 2, 3].drop_while { |x| x < 10 }
# => [99, 2, 3]

The difference is about intent. drop is based on position, while drop_while is based on content, so the first is better when you know the offset and the second is better when you need a rule to decide where the tail begins.

Practical Examples

Skip a header row

rows = [
  ["Name", "Score"],
  ["Alice", 95],
  ["Bob", 87],
  ["Carol", 92]
]

data_rows = rows.drop(1)
# => [["Alice", 95], ["Bob", 87], ["Carol", 92]]

This is the classic header-row example, and it is one of the easiest places to see why the method exists. The shape of the data stays the same, but the first record is removed so later code can work only with actual rows.

Skip calibration readings

readings = [0.0, 0.0, 0.1, 0.2, 0.5, 1.2, 1.8]
calibrated = readings.drop(3)
# => [0.2, 0.5, 1.2, 1.8]

For sensor data, the first few samples are sometimes noisy or expected to settle. Dropping a fixed count is a simple way to ignore that warm-up period without changing the rest of the sequence.

That same pattern works for other staged inputs too, like buffered records or preamble lines in a file. If the first few elements are known to be setup noise, drop gives you a clean handoff to the real data.

Head and tail pattern with take and drop

def split_head_tail(arr, n)
  head = arr.take(n)
  tail = arr.drop(n)
  [head, tail]
end

split_head_tail([1, 2, 3, 4, 5], 2)
# => [[1, 2], [3, 4, 5]]

This helper wraps the two-step split into a tiny reusable pattern. It is easier to read than manual indexing, and it keeps the call site focused on the size of the slice rather than on loop mechanics.

Return Value

drop always returns an Array, even if the original enumerator is something else like a Range or a Hash (which gets enumerated to its pairs):

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

{a: 1, b: 2, c: 3}.drop(1)
# => [[:b, 2], [:c, 3]]

The result is still an array, even when the original collection is a range or a hash. That consistency makes drop convenient in generic code because you do not need a separate branch for each enumerable type.

Edge Cases

InputResult
drop(0)Full copy of the collection
drop(size)Empty array []
drop(n > size)Empty array []
drop on emptyEmpty array []

See Also