Phrogz
Phrogz

Reputation: 303244

Concatenate two multi-line strings line-by-line

I have two multi-line strings (representing ASCII charts). I would like to concatenate them so that they are side-by-side.

For example:

s1 = "aaaa
a
aaaa"
s2 = "bbb
bbbb"
puts s1.multi_concat(s2)
#=> aaaa 
#=> a    bbb
#=> aaaa bbbb

As seen above, I'd like them to be bottom-aligned. I tried this:

class String
  def multi_concat(s2)
    lines.map(&:chomp).zip(s2.lines.map(&:chomp)).map(&:join).join("\n")
  end
end

But it has three problems:

#=> aaaabbb
#=> abbbb
#=> aaaa

How can I "block concatenate" them?

Upvotes: 1

Views: 332

Answers (3)

Cary Swoveland
Cary Swoveland

Reputation: 110685

I implemented "top" and "bottom" align, but not "middle", though I don't expect it would be difficult to add that.

def multi_concat_top(s1,s2,sep=' ')
  multi_concat(s1.split("\n"), s2.split("\n"), sep).join("\n")
end

def multi_concat_bottom(s1,s2,sep=' ')
  multi_concat(s1.split("\n").reverse, s2.split("\n").reverse, sep).
    reverse.join("\n")
end

def multi_concat(a1,a2,sep)
  mx1 = a1.max_by(&:size).size
  [a1.size, a2.size].max.times.map { |i|
    "%s%s%s" % [a1.fetch(i,'').ljust(mx1), sep, a2.fetch(i,'')] }
end

s1 = "aaaa\na\naaaa"
s2 = "bbb\nbbbb"

puts s1
# aaaa
# a
# aaaa

puts s2
# bbb
# bbbb

puts multi_concat_top(s1,s2,' -> ')
# aaaa -> bbb
# a    -> bbbb
# aaaa -> 

puts multi_concat_bottom(s1,s2,' -> ')
# aaaa -> 
# a    -> bbb
# aaaa -> bbbb

Tests performed by phrogs (except "middle") follow.

s1 = "aaa\naaaa"
s2 = "bbbb\n\n\nbb"

puts s1
# aaa
# aaaa
puts s2
# bbbb
#
#
# bb

puts a.multi_concat_top(s1,s2)
# aaa  bbbb
# aaaa 
#     
#      bb

puts a.multi_concat_top(s1,s2,' -> ' )
# aaa  -> bbbb
# aaaa -> 
#      -> 
#      -> bb

puts a.multi_concat_bottom(s1,s2)
#      bbbb
#      
# aaa  
# aaaa bb

puts multi_concat_top(b,a)
# bbbb aaa
#      aaaa
#      
# bb   

Upvotes: 1

Keith Bennett
Keith Bennett

Reputation: 4970

I could swear this question was asked and answered here a few weeks ago but cannot find it.

You can use Ruby's printf-style formatting:

#!/usr/bin/env ruby

def format(s1, s2)
  lines1 = s1.split("\n")
  lines2 = s2.split("\n")
  max_s1 = lines1.map { |s| s.length }.max
  format_string = "%-#{max_s1 + 2}s%s"
  lines1.zip(lines2).each do |s1, s2|
    puts format_string % [s1, s2]
  end
end


s1 = "aaaa
a
aaaa"

s2 = "bbb
bbbb"

format(s1, s2)

Outputs:

aaaa  bbb
a     bbbb
aaaa

Upvotes: 1

Phrogz
Phrogz

Reputation: 303244

Here's a robust multi-line concatenator that supports:

  • Arbitrary strings used to join the two blocks
  • Top, bottom, and middle alignment
class String
  def multi_concat(str2,options={})
    options[:pad]   ||= ''
    options[:align] ||= :top

    chomped1, chomped2 = [self,str2].map{ |s| s.lines.map(&:chomp) }
    template = "%-#{chomped1.map(&:length).max}s#{options[:pad]}%s"
    delta = chomped2.length - chomped1.length
    unless delta==0
      shorter = delta>0 ? chomped1 : chomped2
      delta = delta.abs
      padding = ['']*delta
      case options[:align]
      when :top    then shorter.concat(padding)
      when :bottom then shorter.unshift(*padding)
      when :middle 
        s1,s2 = *padding.each_cons((delta/2.0).ceil)
        shorter.unshift(*s2)
        shorter.concat(s1)
      end
    end
    chomped1.zip(chomped2).map{ |a| template % a }.join("\n")
  end
end

In action:

a = "aaa\naaaa"
b = "bbbb\n\n\nbb"
puts a.multi_concat( b )
#=> aaa bbbb
#=> aaaa
#=>     
#=>     bb

puts a.multi_concat( b, pad:' -> ' )
#=> aaa  -> bbbb
#=> aaaa -> 
#=>      -> 
#=>      -> bb

puts a.multi_concat( b, pad:' ', align: :bottom )
#=>      bbbb
#=>      
#=> aaa  
#=> aaaa bb

puts a.multi_concat( b, pad:' ', align: :middle )
#=>      bbbb
#=> aaa  
#=> aaaa 
#=>      bb

puts b.multi_concat( a, pad:' ' )
#=> bbbb aaa
#=>      aaaa
#=>      
#=> bb   

Upvotes: 1

Related Questions