ruby picture

RCR 294: Array#interpolate

Submitted by neoneye (Sun Feb 20 08:02:48 UTC 2005)

Abstract

Sometimes I need to insert an element between every element. This behavior is half of what Array#join does, except that it doesn't convert into a string.

This rcr was discussed here [].

Problem

It is possible to accomplish this with the following code.

[1, 2, 3].inject(nil){|a,b| a ? (a+[0, b]) : [b]}  #=> [1, 0, 2, 0, 3]

[1,2,3].map{|x|[x,0]}.flatten[0..-2] #=> [1, 0, 2, 0, 3]

However I like the following because of its readability.

res = []
[1, 2, 3].each_with_index do |a,i|
  res << 0 if i!=0
  res << a
end
res    #=> [1, 0, 2, 0, 3]

None of these are really nice solutions.

Proposal

1 argument, without block:
[1, 2, 3, 4].interpolate("a") #=> [1, "a", 2, "a", 3, "a", 4]

no arguments, with a block:
[8,6,4,2,0].interpolate{|a,b|(a+b)/2}  #-> [8,7,6,5,4,3,2,1,0]

More refined versions could be:

no arguments, with block:
[0, 10, 100, 1000].interpolate{|a,b,index|"#{(a+b)/2} #{index}"}
#=> [0, "5 0", 10, "55 1", 100, "550 2", 1000]

with arguments and block:
[0, 10, 100, 1000,
10000].interpolate(+1,-1){|a,b,index,sign|"#{(a+b)/2*sign} #{index}"}
#=> [0, "5 0", 10, "-55 1", 100, "550 2", 1000, "-5500 3", 10000]

2+ arguments, without block:
[1, 2, 3, 4, 5].interpolate("a","b","c") 
#=> [1, "a", 2, "b", 3, "c", 4,
"a", 5]

Analysis

It won't break anything, and will simplify code.

Implementation

Jannis Harder made this implementation.

class Array
  def interpolate(*args,&block)
    out = Array.new
    first = true
    last = nil
    if not block and args.empty?
      raise ArgumentError,"wrong number of arguments (0 for 1)"
    elsif not args.empty? and block
      apos = 0
      self.each_index do |i|
        if not first
            out << block.call(last,self[i],i-1,args[apos])
          apos+=1
          apos%=args.size
        end
        out << self[i]
        last = self[i]
        first=false
      end
      out
    elsif not args.empty?
      apos = 0
      self.each do |i|
        if not first
          out << args[apos]
          apos+=1
          apos%=args.size
        end
        out << i
        last = i
        first=false
      end
      out
    else
      self.each_index do |i|
        if not first
            out << block.call(last,self[i],i-1)
        end
        out << self[i]
        last = self[i]
        first=false
      end
      out
    end
  end
  def interpolate!(*args,&block)
    self.replace(self.interpolate(*args,&block))
  end
end
ruby picture
Comments Current voting
How about letting zip take a singular argument?

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


I like the idea, but I have a couple issues with the implementation:

  1. The name: using 'interpolate' would overload the word's meaning in a programming context, imho. It belongs with string literals, and I feel the word isn't specific enough for the action that is proposed.

  1. I think the behavior of the proposed implementation is too complex. It looks like an attempt to throw as much of the environment as possible into the block.

A slightly simpler and more specific implementation:


          
  1. with arguments
[1,10,100,1000].intersperse(:a)
  ==>[1, :a, 10, :a, 100, :a, 1000]
[1,10,100,1000].intersperse(:a, :b)
  ==>[1, :a, 10, :b, 100, :a, 1000]

  1. with block
[1,10,100,1000].intersperse{|pre, post| "#{pre}:#{post}" }
  ==>[1, "1:10", 10, "10:100", 100, "100:1000", 1000]
[1,10,100,1000].intersperse(:a,:b){|pre, val, post| "#{pre}:#{val}:#{post}" }
  ==>[1, :a, 10, :a, 100, :a, 1000]

          

Also, I think it should be an Enumerable method rather than just Array.


I am voting in favor of this, on the condition that the above name ("intersperse") is used instead of "interpolate".

I would also support the name "sprinkle" :) - GavinKistner


English is not my native language. "Intersperse" sounds good to me :-)

-- Simon Strandgaard


Strongly opposed 0
Opposed 7
Neutral 3
In favor 1
Strongly advocate 0
ruby picture
If you have registered at RCRchive, you may now sign in below. If you have not registered, you may sign up for a username and password. Registering enables you to submit new RCRs, and vote and leave comments on existing RCRs.
Your username:
Your password:

ruby picture

Powered by .