11
\$\begingroup\$

Given an input of two strings with sequences of underscores representing corresponding words, output the sentences with the "blanks" filled in.

The best way to describe this challenge is by example. Here is an example input:

programming _____________ and code golf
programming puzzles ______ code ____

And here is the corresponding output:

programming ___puzzles___ and code golf
programming puzzles _and__ code golf

For the purposes of this challenge, a "word" is defined as a sequence of one or more lowercase letters, and a "blank" is defined as one or more underscores (the input will always contain only lowercase letters, spaces, and underscores). Words and blanks in the input strings are separated by single spaces, and the sum of the number of words and blanks in the sentences will always be equal.

The objective of the challenge is to fill all of the blanks with the correct words, which are the words that occupy the same index in the other string when split by spaces.

  • The word must be centered in the blank, as shown with the word "puzzles" in the example above—an equal number of underscores remain on either side.

  • If the word cannot be exactly centered, the extra underscore may go either on the left or the right (ex. the word "and" in the example above).

  • There will always be enough underscores for the word to fit, but there may be exactly as many as the length of the word (ex. the word "golf" in the example above).

  • There will never be a blank in the same position in both strings.

Input/output may be any of the following (input/output do not necessarily have to be via the same method):

  • single string separated by any character that is not alphabetic, a space, or an underscore (ex. newline or comma separated string)

  • an array/list/etc. of two strings

  • two function/command line arguments (input only)

Since this is , the shortest code in bytes will win.

The example above can be used as a test case. Here is a larger test case (second string in output may vary slightly due to different centering behavior):

lorem _____ dolor _____ amet _______________ adipiscing elit mauris dapibus tincidunt _____________________________ accumsan fringilla proin vulputate viverra lorem fermentum dictum
lorem ipsum ______ sit _______ consectetur _______________ elit mauris dapibus tincidunt metus accumsan fringilla proin vulputate viverra lorem ____________________________ dictum
lorem ipsum dolor _sit_ amet __consectetur__ adipiscing elit mauris dapibus tincidunt ____________metus____________ accumsan fringilla proin vulputate viverra lorem fermentum dictum
lorem ipsum dolor_ sit _amet__ consectetur __adipiscing___ elit mauris dapibus tincidunt metus accumsan fringilla proin vulputate viverra lorem _________fermentum__________ dictum
asked Jan 29, 2016 at 12:34
\$\endgroup\$
1
  • \$\begingroup\$ Nice bidiurnal challenge. \$\endgroup\$ Commented Jan 29, 2016 at 17:50

9 Answers 9

7
\$\begingroup\$

Retina, (削除) 102 (削除ここまで) (削除) 100 (削除ここまで) (削除) 93 (削除ここまで) 88 bytes

Byte count assumes ISO 8859-1 encoding.

$
!¶$`
m`(?<=^(\w+ )*)(?=_.*¶(?<-1>\w+ )*(\w+))
2ドル
(([a-z])+)(?<-2>_)*(_*)3円|!\D+
3ドル1ドル3ドル

The strings will are separated by a linefeed. If there is an odd number of underscores left, the extraneous one will be after the word.

Try it online!

Explanation

I guess this is the "duplicate-append-lookback-match-add-centre approach", or something close...

$
!¶$`

We start by duplicating the input (separated with a ! and a linefeed). The purpose of this is that we can then process both lines by fetching words from the next line (instead of having to treat the second line separately).

m`(?<=^(\w+ )*)(?=_.*¶(?<-1>\w+ )*(\w+))
2ドル

This prepends the correct word to each gap. We start by counting the current word position with the lookbehind (?<=^(\w+ )*) (the position is stored as the depth of group 1). Then the lookahead a) ensures that we're at the beginning of a gap by matching _, then skips to the next line with .*¶, matches the correct number of words with (?<-1>\w+ )* to get to the right position, and then matches the word found there with (\w+) into group 2.

(([a-z])+)(?<-2>_)*(_*)3円|!\D+
3ドル1ドル3ドル

This stage does three things:

  • It removes the underscores corresponding to each word length. This is done by counting the word length into group 2 with ([a-z])+ and then matching that many underscores (which are never written back).
  • It shifts the word to the centre of the gap by capturing half of the remaining underscores with (_*)3円 and writing 3ドル1ドル3ドル back.
  • It removes the duplicated input by matching !\D+ and replacing it with nothing.
answered Jan 29, 2016 at 12:59
\$\endgroup\$
5
\$\begingroup\$

Pyth, 30

jL;Cmm|*}J\_k.[lkhx#JdJkdCcR;Q

Takes input and outputs as a list of two strings. Uses a pretty basic split–zip–double map–centre–zip–join approach.

Try it here

Expanded:

jL;Cmm|*}J\_k.[lkhx#JdJkdCcR;Q ##
 cR;Q ## split
 C ## zip
 mm ## double map
 |*}J\_k.[lkhx#JdJkd ## centre
 C ## zip
jL; ## join

I'll explain more once I'm really satisfied that I can't golf this any more, although it should be pretty clear, given the ubiquity of the split–zip–double map–centre–zip–join approach and all.

answered Jan 29, 2016 at 14:38
\$\endgroup\$
3
  • 8
    \$\begingroup\$ Ahhh, the classic split-zip-double map-centre-zip-join approach. I fondly remember it being used as an introductory example in my Algorithms 101 lecture. \$\endgroup\$ Commented Jan 29, 2016 at 14:48
  • 3
    \$\begingroup\$ @MartinBüttner Yeah, I have some bad memories of it because I slept through that class, had to solve problems on the exam using the duplicate-append-lookback-match-add-centre approach instead. \$\endgroup\$ Commented Jan 29, 2016 at 14:54
  • 4
    \$\begingroup\$ I'll go apply some cold water to the burn. \$\endgroup\$ Commented Jan 29, 2016 at 15:11
4
\$\begingroup\$

Python 2, 109

def f(a,b):exec"print' '.join([x,y][x<'`'].center(len(x),'_')for x,y in zip(a.split(),b.split()));a,b=b,a;"*2

The function takes two strings as arguments and prints the output as in the examples. It uses a boring approach, with str.center(width, fillchar) doing most of the work.

Try it online.

answered Jan 29, 2016 at 14:03
\$\endgroup\$
1
  • 1
    \$\begingroup\$ I don't think you need z, unless I'm missing something you can just do the swap after the print and inline z. \$\endgroup\$ Commented Jan 29, 2016 at 15:54
2
\$\begingroup\$

Ruby, (削除) 111 (削除ここまで) 109 characters

->l{l.map(&:split).transpose.map{|c|c[m=c[0]<c[1]?0:1]=c[1-m].center c[m].size,?_
c}.transpose.map{|s|s*' '}}

Input: array of 2 strings; output: array of 2 strings.

Sample run:

2.1.5 :001 > puts ->l{l.map(&:split).transpose.map{|c|c[m=c[0]<c[1]?0:1]=c[1-m].center c[m].size,?_;c}.transpose.map{|s|s*' '}}[[
2.1.5 :002 > 'programming _____________ and code golf',
2.1.5 :003 > 'programming puzzles ______ code ____',
2.1.5 :004 > ]]
programming ___puzzles___ and code golf
programming puzzles _and__ code golf
answered Jan 29, 2016 at 15:35
\$\endgroup\$
1
\$\begingroup\$

JavaScript, (削除) 194 (削除ここまで) 185 bytes

f=(m,n)=>(m=m.split` `,n=n.split` `,G=(x,i,a)=>x[0]!='_'?x:(b=(a?n:m)[i],s=x.length-b.length,(k='_'.repeat(s/2))+b+k+(s%2?'_':'')),H=(e,y)=>e.map((x,i)=>G(x,i,y)).join` `,[H(m,1),H(n)])

Takes two strings as parameters and outputs two strings as array/list

f=(m,n)=>(
 m=m.split` `,
 n=n.split` `,
 G=(x,i,a)=>
 x[0]!='_'?x:
 (
 b=(a?n:m)[i],
 s=x.length-b.length,
 (k='_'.repeat(s/2))+b+k+(s%2?'_':'')
 ),
 H=(e,y)=>e.map((x,i)=>G(x,i,y)).join` `,
 [H(m,1),H(n)]
)
document.body.innerHTML = '<pre>' +
 "f('lorem _____ dolor _____ amet _______________ adipiscing elit mauris dapibus tincidunt _____________________________ accumsan fringilla proin vulputate viverra lorem fermentum dictum', 'lorem ipsum ______ sit _______ consectetur _______________ elit mauris dapibus tincidunt metus accumsan fringilla proin vulputate viverra lorem ____________________________ dictum')<br>" +
 f('lorem _____ dolor _____ amet _______________ adipiscing elit mauris dapibus tincidunt _____________________________ accumsan fringilla proin vulputate viverra lorem fermentum dictum', 'lorem ipsum ______ sit _______ consectetur _______________ elit mauris dapibus tincidunt metus accumsan fringilla proin vulputate viverra lorem ____________________________ dictum') + "<br><br>" +
 "f('programming _____________ and code golf', 'programming puzzles ______ code ____')<br>" +
 f('programming _____________ and code golf', 'programming puzzles ______ code ____')
'</pre>'

answered Jan 29, 2016 at 15:28
\$\endgroup\$
1
\$\begingroup\$

Mathematica 223

There must be a shorter way to do this.

k=StringLength;m=StringSplit;
g=Partition[Riffle[m@#,m@#2],2]/.{{a_,a_}:> a<>" ",{a_,b_/; StringTake[b,1]=="_"}:> a<>" ",
{a_,b_}:>Table["_",Ceiling[z=(k@a-k@b)/2]]<>b<>""<>Table["_",Floor@z]<>" "}&;
s_~h~t_:={""<>g[s,t],""<>g[t,s]}

Sample run

h["programming _____________ and code golf", "programming puzzles ______ code ____"]

enter image description here

answered Jan 29, 2016 at 17:05
\$\endgroup\$
0
0
\$\begingroup\$

Gema, (削除) 208 (削除ここまで) 203 characters

\B=@set{i;0}
<I>=@push{${v;f};0ドル}@incr{i}
\n=@set{v;s}@set{i;0}
 =
\E=@repeat{$i;@cmps{$f;$s;@set{f;@fill-center{$f;$s}};;@set{s;@fill-center{$s;$f}}}@set{F;$f ${F;}}@set{S;$s ${S;}}@pop{f}@pop{s}}$F\n$S

Just because Gema has the perfect function for this task: @fill-center{background;value}.

Input: 2 newline separated lines (no final newline); output: 2 newline separated lines (with trailing spaces – not seem to be forbidden).

Sample run:

bash-4.3$ echo -ne 'programming _____________ and code golf\nprogramming puzzles ______ code ____' |
> gema '\B=@set{i;0};<I>=@push{${v;f};0ドル}@incr{i};\n=@set{v;s}@set{i;0}; =;\E=@repeat{$i;@cmps{$f;$s;@set{f;@fill-center{$f;$s}};;@set{s;@fill-center{$s;$f}}}@set{F;$f ${F;}}@set{S;$s ${S;}}@pop{f}@pop{s}}$F\n$S'
programming ___puzzles___ and code golf 
programming puzzles _and__ code golf 
answered Jan 29, 2016 at 18:03
\$\endgroup\$
0
\$\begingroup\$

C, 197 bytes

#define c(w,y)l=strspn(w,"_"),r=strcspn(y," "),memcpy(w+(l-r)/2,y,r),w+=l,y+=r;
main(l,v,w,y,r)char**v,*w,*y;{for(w=v[1],y=v[2];*w;w++,y++)if(*w^*y)if(*w^95)c(y,w)else c(w,y)puts(v[1]);puts(v[2]);}

Output

$ ./a.out "lorem _____ dolor _____ amet _______________ adipiscing elit mauris dapibus tincidunt _____________________________ accumsan fringilla proin vulputate viverra lorem fermentum dictum" "lorem ipsum ______ sit _______ consectetur _______________ elit mauris dapibus tincidunt metus accumsan fringilla proin vulputate viverra lorem ____________________________ dictum"
lorem ipsum dolor _sit_ amet __consectetur__ adipiscing elit mauris dapibus tincidunt ____________metus____________ accumsan fringilla proin vulputate viverra lorem fermentum dictum
lorem ipsum dolor_ sit _amet__ consectetur __adipiscing___ elit mauris dapibus tincidunt metus accumsan fringilla proin vulputate viverra lorem _________fermentum__________ dictum
answered Jan 29, 2016 at 21:19
\$\endgroup\$
0
\$\begingroup\$

ES6, 122 bytes

a=>a.map(s=>s.split` `).map((s,n,a)=>s.map((w,i)=>w<'a'?(l=w.length,t=w+a[n^1][i]+w,t.substr(t.length-l>>1,l)):w).join` `)

Takes an array of two strings as a single parameter and returns another array of two strings.

answered Jan 29, 2016 at 21:28
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.