1

I'm trying to write a method that will cause a rspec test like this to pass:

it "starts the thing and move on" do
  class.method_1("Name One")
  class.method_1("Name Two")
  expect(class.method_2).to eq "Some string Name One"
  expect(class.method_3).to eq ["Name Two"]
end

method_1 just adds a name to an array, and method_3 returns the array (defined in initialize method):

def method_1(name)
  @array << name
end

def method_3
  @array
end

I figured it would be pretty simple to interpolate @array[0] into the string and use @array.delete_at(0) to modify the array. Like so:

def method_2
  p "Some string #{@array[0]}"
  @array.delete_at(0)
end

But that method returns "Name One" instead of the string. If I comment out the delete code, the string returns properly but my array hasn't been modified. I've been in Ruby docs for a long time but #shift has the same issue about returning the removed item.

I'm almost certain I've over complicated this -- what am I missing?

2 Answers 2

1

You can collapse all this down to more conventional Ruby like this:

class MyTestClass
  attr_reader :array

  def initialize
    @array = [ ]
  end

  def push(s)
    @array << s
  end

  def special_shift
    "Some string #{@array.shift}"
  end
end

Then in terms of usage:

it "starts the thing and move on" do
  my_thing.push("Name One")
  my_thing.push("Name Two")
  expect(my_thing.special_shift).to eq "Some string Name One"
  expect(my_thing.array).to eq ["Name Two"]
end

Using names like push and shift which are consistent with Ruby conventions make the purpose and action of a method a lot easier to understand.

When it comes to your implementation of method_3 you forget that you can inline whatever you want inside a #{...} block, even methods that modify things. The p method is used for display, it won't return anything. To return something you need to have it either as the last thing evaluated (implicit) or by using return (explicit).

Sign up to request clarification or add additional context in comments.

2 Comments

I can't believe I didn't think to integrate @array.shift into the print line of code. Thank you for the help and the clarification on returns. You're right that method_3 could very easily be an inline block, but an earlier test needed that to be defined.
Other programming languages have strict limits on what you can interpolate, so it's okay if you didn't think about doing it. Can be something you never think to challenge. In Ruby you can insert any valid block of code there.
0

Change method_2 to the following to get the array back

def method_2
  p "Some string #{@array[0]}"
  @array.delete_at(0)
  @array
end

From array#delete_if on ruby-doc.org

Deletes the element at the specified index, returning that element, or nil if the index is out of range.

Alternatively use object#tapwhich returns self

@array = [1,2,3,4]
#=> [1, 2, 3, 4]
@array.tap {|arr| arr.delete_at(0)}
#=> [2, 3, 4]

3 Comments

tap is pretty heavy-handed here.
I am searching for a way to delete from an array at an index and return the modified array in place, instead the deleted item. I can't find anything so far, except this .tap example. @tadman, could you explain to me, what about tap is heavy handed here?
There's no advantage to doing tap just to get a one-line solution. The two line version here is perfectly fine and much more easily understood.

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.