8

I have an Array and want to insert a new element in between all elements, someway like the join method. For example, I have

[1, [], "333"]

and what I need is

[1, {}, [], {}, "333"]

Note a new empty hash was inserted in between all elements.

Edit: Currently what I have is:

irb(main):028:0> a = [1, [], "333"]
=> [1, [], "333"]
irb(main):029:0> a = a.inject([]){|x, y| x << y; x << {}; x}
=> [1, {}, [], {}, "333", {}]
irb(main):030:0> a.pop
=> {}
irb(main):031:0> a
=> [1, {}, [], {}, "333"]
irb(main):032:0>

I want to know the best way.

1
  • x.push(y, {}) is shorter, both << and push return array, so you don't need ;x, inject is slow Commented Feb 23, 2012 at 22:31

8 Answers 8

18
[1, 2, 3].flat_map { |x| [x, :a] }[0...-1]
#=> [1, :a, 2, :a, 3]

FYI, that function is called intersperse (at least in Haskell).

[Update] If you want to avoid the slice (that created a copy of the array):

[1, 2, 3].flat_map { |x| [x, :a] }.tap(&:pop)
#=> [1, :a, 2, :a, 3]
Sign up to request clarification or add additional context in comments.

3 Comments

I wouldn't do [0..-1] in Haskell (and probably in Ruby too)
@VictorMoroz, what would you do instead?
@VictorMoroz: Added an inplace snippet. Ruby has no abstraction for this, so I've used tap+pop.
2

Another similar solution uses #product :

[1, 2, 3].product([{}]).flatten(1)[0...-1]
# => [ 1, {}, 2, {}, 3 ]

Comments

2

One approach is to zip another array of desired elements and then flatten it with depth = 1:

> arr = [1, [], "333"]
> element = {}
> interspersed = arr.zip([element] * (arr.size - 1)).flatten(1).compact
> # [1, {}, [], {}, "333" ]

You can extend Array to make this behavior more accessible.

class Array
  def intersperse(elem)
    self.zip([elem] * (self.size - 1)).flatten(1).compact
  end
end

e.g.,

[43] pry(main)> [1,2,3].intersperse('a')
=> [1, "a", 2, "a", 3]

Comments

1
a = [1,2,3]
h, *t = a
r = [h]
t.each do |e|
  r.push({}, e)
end
r #=> [1, {}, 2, {}, 3]

Comments

1

You could do something like:

a = [1, [], "333"]
new_a = a.collect {|e| [e, {}]}.flatten(1)
=> [1, {}, [], {}, "333", {}]

You need to do .flatten(1) because it will flatten your blank array without it.

Or as @David Grayson says in the comment, you can do a flat_map which will do the same thing.

a.flat_map {|e| [e, {}]}
=> [1, {}, [], {}, "333", {}]

@tokland has the correct answer if the last {} is not necessary. You return a slice from 0 to length - 1 or [0..-1].

3 Comments

Shorter version: a.flat_map { |e| [e, {}] }
Oh, nice! And it doesn't flatten the blank array. Good call.
only last {} was not required
1

Another one that's similar to Tokland's:

xs.inject([]){|x,y| x << y << {}}[0...-1]

Comments

1
[1, 2, 3, 4, 5].inject { |memo, el| Array(memo) << {} << el }
#=> [1, {}, 2, {}, 3, {}, 4, {}, 5]

inject will use the first element to start with, so you don't need to mess with indices.

Comments

0
irb(main):054:0* [1, 2, 3, 4, 5, 6, 7, 8, 9].each_slice(1).flat_map {|e| e << "XXX"}[0...-1]
=> [1, "XXX", 2, "XXX", 3, "XXX", 4, "XXX", 5, "XXX", 6, "XXX", 7, "XXX", 8, "XXX", 9]
irb(main):055:0> [1, 2, 3, 4, 5, 6, 7, 8, 9].each_slice(2).flat_map {|e| e << "XXX"}[0...-1]
=> [1, 2, "XXX", 3, 4, "XXX", 5, 6, "XXX", 7, 8, "XXX", 9]
irb(main):056:0> [1, 2, 3, 4, 5, 6, 7, 8, 9].each_slice(3).flat_map {|e| e << "XXX"}[0...-1]
=> [1, 2, 3, "XXX", 4, 5, 6, "XXX", 7, 8, 9]
irb(main):057:0> [1, 2, 3, 4, 5, 6, 7, 8, 9].each_slice(4).flat_map {|e| e << "XXX"}[0...-1]
=> [1, 2, 3, 4, "XXX", 5, 6, 7, 8, "XXX", 9]
irb(main):058:0> [1, 2, 3, 4, 5, 6, 7, 8, 9].each_slice(5).flat_map {|e| e << "XXX"}[0...-1]
=> [1, 2, 3, 4, 5, "XXX", 6, 7, 8, 9]
irb(main):059:0> [1, 2, 3, 4, 5, 6, 7, 8, 9].each_slice(6).flat_map {|e| e << "XXX"}[0...-1]
=> [1, 2, 3, 4, 5, 6, "XXX", 7, 8, 9]
irb(main):060:0> [1, 2, 3, 4, 5, 6, 7, 8, 9].each_slice(7).flat_map {|e| e << "XXX"}[0...-1]
=> [1, 2, 3, 4, 5, 6, 7, "XXX", 8, 9]
irb(main):061:0> [1, 2, 3, 4, 5, 6, 7, 8, 9].each_slice(8).flat_map {|e| e << "XXX"}[0...-1]
=> [1, 2, 3, 4, 5, 6, 7, 8, "XXX", 9]
irb(main):062:0> [1, 2, 3, 4, 5, 6, 7, 8, 9].each_slice(9).flat_map {|e| e << "XXX"}[0...-1]
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
irb(main):063:0>

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.