3

Lets say I have an array like so: ['x','cat', 'dog', 'x', 'dolphin', 'cougar', 'whale']

I don't know the length of the array or when an 'x' will occur. When I reach 'x' I want to push the following elements into a new array until I reach the next element that includes?('x').

The desired output would be: [['cat', 'dog']['dolphin','cougar', 'whale']]

How can I achieve this?

2
  • 2
    Ahh the beloved SO 'What have you tried so far?'. Tried a lot but it hasn't got me far... Don't know how to solve this one. IM STUCK :( Commented May 9, 2013 at 22:39
  • What if there are two 'x' in a row or it ends on 'x'? What's the behavior? Ignore or have an empty []? Commented May 10, 2013 at 14:08

5 Answers 5

7

Enumerable#slice_before makes this simple:

a = ['x','cat', 'dog', 'x', 'dolphin', 'cougar', 'whale']
a.slice_before(/\Ax\z/).map { |chunk| chunk.drop(1) }
=> [["cat", "dog"], ["dolphin", "cougar", "whale"]]
Sign up to request clarification or add additional context in comments.

1 Comment

+1, I was hoping someone would mention slice_before. It's made for exactly this situation.
3
ar =  ['x', 'cat', 'dog', 'x', 'dolphin', 'cougar', 'whale']
p ar.chunk{|el| el == 'x'}.each_slice(2).map{|el| el.last.last}
#=> [["cat", "dog"], ["dolphin", "cougar", "whale"]]

Most of the work is chopping off the unneeded side results of the chunk method.

Comments

3

Enumerable#chunk is the way to go. You can use nil to drop those chunks you don't want:

arr = ['x','cat', 'dog', 'x', 'dolphin', 'cougar', 'whale']

arr.chunk{ |e| e != 'x' || nil }.map(&:last)
#=> [["cat", "dog"], ["dolphin", "cougar", "whale"]]

2 Comments

Cool. And this is equivalent: arr.chunk{ |e| true if e != 'x' }.map(&:last) And you could replace true with any truthy.
This version works and it is very explicit: arr.chunk{ |e| e == 'x' ? :_separator : :payload }.map(&:last)
2

Good old Enumerable#reduce is handy for so many things:

def split_array_by_item(array, item)
  array.reduce([]) do |memo, x|
    memo.push([]) if (x == item) || memo.empty?
    memo[-1].push(x) unless x == item
    memo
  end
end

a = ['x', 'cat', 'dog', 'x', 'dolphin', 'cougar', 'whale'] 
split_array_by_item(a, 'x') # => [["cat", "dog"], ["dolphin", "cougar", "whale"]] 

[Edit] Also:

def split_array_by_item(array, item)
  array.chunk{|x|x==item}.reject(&:first).map(&:last)
end

1 Comment

@Snarf I answered, can I have one too?
0

Since Ruby 2.0, a nice solution is slice_before method or since 2.2 slice_when method :

We however need to drop the first element 'x' for each array generated :

ary =  ['x', 'cat', 'dog', 'x', 'dolphin', 'cougar', 'whale']

ary.slice_before{|e| e=='x'}.map{|t| t.drop(1)}

#==> [["cat", "dog"], ["dolphin", "cougar", "whale"]]

ary.slice_when{|i,j| j=='x'}.map{|t| t.drop(1)}

#==> [["cat", "dog"], ["dolphin", "cougar", "whale"]]

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.