18
\$\begingroup\$

Let us say that we have a particular set of functions on strings. These functions are kind of like fill in the blanks or madlibs, except that they only take one input and use that to fill in all of their blanks. For example we might have a function that looks like

I went to the ____ store and bought ____ today.

If we applied this function to the string cheese the result would be:

I went to the cheese store and bought cheese today.

We can represent these functions as a non-empty list of strings, where the blanks are simply the gaps in between strings. For example our function above would be:

["I went to the ", " store and bought ", " today."]

With this representation there is only one representation for every function of this sort and only one function for each representation.

A really neat thing is that the set of such functions is closed under composition. That is to say composition of two of our functions is always another one of these functions. For example if I compose our function above with

["blue ", ""]

(the function that prepends blue to the input) We get the function:

["I went to the blue ", " store and bought blue ", " today."]

These can get a little more complex though. For example if we compose the first function with

["big ", " and ", ""]

The result is

["I went to the big ", " and ", " store and bought big ", "and", " today."]

Task

Your task is to take two functions as described as non-empty lists of strings and output their composition as a non-empty list of strings.

For the purpose of this challenge a list can be any ordered container that permits duplicates and a string may be a native string type, a list of characters or a list of integers.

This is answers will be scored in bytes with fewer bytes being better.

Test cases

["","xy"] ["ab",""] -> ["ab","xy"]
["x","y","z"] ["a","b"] -> ["xa","bya","bz"]
["xy"] ["ab"] -> ["xy"]
["","",""] ["a",""] -> ["a","a",""]
["x",""] ["","",""] -> ["x","",""]
["x","y","z"] ["a","b","c"] -> ["xa","b","cya","b","cz"]
["x","x","x"] ["a"] -> ["xaxax"]
["w","x","y","z"] ["ab","cd","e"] -> ["wab","cd","exab","cd","eyab","cd","ez"]
\$\endgroup\$
  • 1
    \$\begingroup\$ All 3 existing answers currently fail if some non-printable ASCII character is used in the input (SOH, TAB or LF, depending on the answer). So I think you should really decide if the input is restricted to printable ASCII or not. \$\endgroup\$ – Arnauld Jul 29 at 15:51
  • \$\begingroup\$ @Arnauld Ok well as of now it is unrestricted and I have not seen a reason to change that so it will remain. \$\endgroup\$ – Sriotchilism O'Zaic Jul 29 at 15:57
  • 2
    \$\begingroup\$ @KevinCruijssen mine's valid since zero is not a character. Lucky language feature helping out. \$\endgroup\$ – Jonathan Allan Jul 29 at 16:56
  • 1
    \$\begingroup\$ @SriotchilismO'Zaic My 05AB1E was joining/splitting by newlines. The JavaScript and Haskell answers are joining/splitting by tabs, the C# answer by the unprintable character `` (SOH), so those are all invalid as well. I don't know Perl 5 well enough either, though. So that one might be valid. \$\endgroup\$ – Kevin Cruijssen Jul 29 at 17:03
  • 3
    \$\begingroup\$ @Roman You cannot assume that any character will not appear in the input so that you may use it as a separator. You must actually solve the challenge. \$\endgroup\$ – Sriotchilism O'Zaic Jul 30 at 13:07

12 Answers 12

11
\$\begingroup\$

Jelly, 6 bytes

j0j@ṣ0

A dyadic Link accepting the first function representation on the right and the second function representation on the left which yields the resulting function representation. Each function representation is a list of lists of characters (Jelly has no other strings).

Try it online! (the full-program arguments are given in Python notation; strings become lists. The footer shows a Python representation of the Link's output.)

Here is a test-suite which reformats the Link's output like the inputs.

How?

Takes advantage of Jelly's mixed type lists to allow the entire domain of representations (any list of lists of characters) by using the integer zero as a place-holder:

j0j@ṣ0 - Link: b, a        e.g.    b = [['a','b'],['c','d'],['e']]
       -                   ...and  a = [['w'],['x'],['y'],['z']]
                             (i.e. test-case ["w","x","y","z"] ["ab","cd","e"])
j0     - join b with zeros         ['a','b',0,'c','d',0,'e']    
  j@   - join a with that          ['w','a','b',0,'c','d',0,'e','x','a','b',0,'c','d',0,'e','y','a','b',0,'c','d',0,'e','z']
    ṣ0 - split at zeros            [['w','a','b'],['c','d'],['e','x','a','b'],['c','d'],['e','y','a','b'],['c','d'],['e','z']
                             (i.e.: ["wab","cd","exab","cd","eyab","cd","ez"])

If we needed to deal with any of Jelly's mixed lists (including those of any depth or shape) we could use this eight byter: j,©⁹jœṣ® which uses the paired arguments as the place-holder.

\$\endgroup\$
5
\$\begingroup\$

Haskell, 78 bytes

(a:b:r)#t@(x:s)|s>[]=(a++x):init s++((last s++b):r)#t|z<-a++x++b=(z:r)#t
x#_=x

Try it online!

\$\endgroup\$
5
\$\begingroup\$

Python 3.8 (pre-release),  60  58 bytes

lambda a,b:(v:='&'.join(a+b)+'$').join(b).join(a).split(v)

An unnamed function accepting two lists of strings, a and b, which returns a list of strings.

Try it online! Or see the test-suite.

How?

First forms a separator string, v, which cannot be found inside a or b. Then forms a string by joining up the strings in b with copies of v. Then forms a string by joining up the strings in a with copies of that. Finally splits that string at instances of v to give a list of strings.

While ensuring v is not in a or b we must also ensure that v wont make us split early in the case where all the strings in a and b are equal. To do so we form v by joining all the strings in both lists with instances of a string (here '&') and add an extra, different character (here '$'). Note that doing either in isolation is not enough as all strings in the inputs could equal the chosen character.

\$\endgroup\$
  • \$\begingroup\$ Could you give an example input where & is required? and using ''.join(a+b)+'$' is not enough? \$\endgroup\$ – Sriotchilism O'Zaic Jul 31 at 14:26
  • \$\begingroup\$ It took me a while but ['$','$'] ['$','$'] would be one. \$\endgroup\$ – Sriotchilism O'Zaic Jul 31 at 14:32
  • \$\begingroup\$ Yeah if all strings equal the chosen '$' character and the result will be more than one string we need a different character in there to avoid splitting early. \$\endgroup\$ – Jonathan Allan Jul 31 at 15:35
2
\$\begingroup\$

05AB1E, 4 15 19 9 11 bytes

«TýR©ý¹sý®¡

Unlike the Jelly answer, 05AB1E's string "0", integer 0, and float 0.0 are all (somewhat) equal, so I can't split/join by an integer. This is why we had the +15 bytes as workarounds, although I've golfed it back to 9 bytes now. Thanks to @JonathanAllan for finding 2 bugs.

Try it online or verify all test cases.

Explanation:

«            # Merge the two (implicit) input-lists together
 Tý          # Then using a "10" delimiter join all strings together
   R         # Reverse this string
    ©        # Store this string in variable `®` (without popping)
     ý       # Use this string as delimiter to join the second (implicit) input-list
      ¹sý    # Then join the first input-list by this entire string
         ®¡  # And split it back on variable `®` so it's the expected list of strings
             # (after which this result is output implicitly)
\$\endgroup\$
  • 2
    \$\begingroup\$ This fails if the input has newlines (OP has said that the input is currently unrestricted). \$\endgroup\$ – Erik the Outgolfer Jul 29 at 14:36
  • \$\begingroup\$ @EriktheOutgolfer Every other answer has the same issue btw. \$\endgroup\$ – Kevin Cruijssen Jul 29 at 16:53
  • \$\begingroup\$ @EriktheOutgolfer Can definitely be golfed some more, but made a quick and dirty fix for now. \$\endgroup\$ – Kevin Cruijssen Jul 29 at 17:18
  • 1
    \$\begingroup\$ Uh, sorry about this ...it wont work if the input lists contain strings only containing newlines :( (it'll split early) \$\endgroup\$ – Jonathan Allan Jul 29 at 18:55
  • 1
    \$\begingroup\$ @JonathanAllan Lol.. Ah well, I guess I can only thank you for finding these bugs.. I hope it's fixed now and you won't find anything else.. Although I have the feeling you might.. \$\endgroup\$ – Kevin Cruijssen Jul 29 at 20:37
2
\$\begingroup\$

Wolfram Language (Mathematica), 62 61 bytes

""<>#&/@Flatten[#~(R=Riffle)~I/.I->#2~R~I]~SequenceSplit~{I}&

Try it online!

-1 thanks to Roman


Though it's not a valid output, this returns a function that actually does the job.. (34 bytes)

(g=a""<>a~Riffle~#&)[#]@*g[#2]&

Try it online!

\$\endgroup\$
  • 1
    \$\begingroup\$ 61 bytes by recycling Riffle. \$\endgroup\$ – Roman Jul 30 at 18:46
2
\$\begingroup\$

Japt, 8 bytes

Adapts Jonathan's approach.

qVqN²)qN

Try it

qVqN²)qN     :Implicit input of arrays U & V (N=[U,V])
q            :Join U with
 Vq          :  V joined with
   N²        :    Push 2 to N (modifying the original), which gets coerced to a string
             >     e.g., N=[["a","b"],["c","d"]] -> N=[["a","b"],["c","d"],2] -> "a,b,c,d,2"
     )       :End join
      qN     :Split on the modified N, which, again, gets coerced to a string
             > e.g., N=[["a","b"],["c","d"],2] -> "a,b,c,d,2"
\$\endgroup\$
  • \$\begingroup\$ What is N in this case? If I understand it correctly (using the search functionality of the TryIt-link), it repeats N two times (). It then uses that to join the second input V (VqN²), and then uses that entire string to join the first (implicit) input U (q...)). And finally splits the resulting string on N (qN). But what is N in this case? \$\endgroup\$ – Kevin Cruijssen Jul 30 at 8:41
  • \$\begingroup\$ Ah wait, I think I've looked at the wrong p(...) method in the search. It appends the 2 to both inputs paired together doesn't it. Only results in [["w","x","y","z"],["ab","cd","e"],2], and it uses that entire list to join. Why does the final qN not leave the 2 to the result-list in that case? Or does modify the original N? \$\endgroup\$ – Kevin Cruijssen Jul 30 at 8:43
  • 1
    \$\begingroup\$ @KevinCruijssen, added an explanation but you pretty much have it figured out. And, yes, pushing elements to an array in JS modifies the original array. \$\endgroup\$ – Shaggy Jul 30 at 9:05
1
\$\begingroup\$

Haskell, 62 bytes

[a]#(c:d)=(a++c):d
(a:b)#c=a:b#c
[a]%b=[a]
(a:b)%c=[a]#c#(b%c)

Try it online!

Here is my Haskell answer. It works on any type of list.

\$\endgroup\$
1
\$\begingroup\$

J, 44 43 42 29 bytes

_<;._1@,(;@}:@,@,.(,_&,)&.>/)

Try it online!

-13 bytes thanks to miles!

This approach uses integers and is due to miles.

original approach with strings

g=.[:}.@,,.
f=.(<@0<@;;._1@,];@g<"0@[)<@0<@g]

Try it online!

Note: I've adjusted -3 off the TIO to account for f=.

Uses Jonathan Allen's method, adapted for J.

This was surprisingly difficult to golf, since J doesn't have a built in "join" method, and I'd be curious to see if it can be improved significantly.

g is helper verb that gives us "join"

\$\endgroup\$
  • \$\begingroup\$ Working with list of integers as input, I found a 29 char solution, _<;._1@,(;@}:@,@,.(,_&,)&.>/) uses infinity _ as the sentinel value to know where to split <;._1. Joining is first done using reduce / to form one large box, then its just array shaping. \$\endgroup\$ – miles Aug 7 at 23:38
  • \$\begingroup\$ That's impressive. Thanks @miles. This one definitely felt like there was room to improve but I didn't see how. \$\endgroup\$ – Jonah Aug 7 at 23:42
  • \$\begingroup\$ @miles Shouldn't the g&.:(a.&i.&.>) count toward the bytes or am I missing something? \$\endgroup\$ – Jonah Aug 7 at 23:45
  • \$\begingroup\$ The OP mentioned that input could be as a list of characters or as a list of integers, so that helper function is just to convert from boxes of char arrays to boxes of int arrays for easier viewing \$\endgroup\$ – miles Aug 7 at 23:48
  • \$\begingroup\$ Ah I forgot that, thanks \$\endgroup\$ – Jonah Aug 7 at 23:49
0
\$\begingroup\$

JavaScript (Node.js), 85 79 bytes

([a,...b],[c,...d])=>b.map(e=>r.push(r.pop(r.push(r.pop()+c,...d))+e),r=[a])&&r

Try it online!

\$\endgroup\$
0
\$\begingroup\$

JavaScript, 37 bytes

Also adapts Jonathan's approach.

a=>b=>a.join(b.join(a+=b+0)).split(a)

Try it online

\$\endgroup\$
0
\$\begingroup\$

Perl 5 (-lp), 20 bytes

As @JonathanAllan commented, this is a full-program using, for IO, a tab as a list separator and a newline to separate the two lists.

chop($n=<>);s/  /$n/g

TIO

the tab and newline were chosen because more convenient to check test cases, otherwise could be changed to non-printable characters \1 and \2.

(-02l012p)

chop($n=<>);s//$n/g

TIO

How it works,

  • -02 : to set input record separator to \2
  • -l : to remove input separator from default argument $_ and to add output record separator to default output
  • -012 : to set output record separator to \012 (\n) so that output is easier to check
  • -p : to print default argument

  • $n=<>; : to read next record and to assign to $n

  • chop$n; : to remove separator from $n
  • s/\x1/$n/g : to replace all occurences of \1 with $n
\$\endgroup\$
  • 2
    \$\begingroup\$ My Perl is almost non-existent but I believe that this is a full-program using, for IO, a tab as a list separator and a newline to separate the two lists. How can it accept input with these characters? \$\endgroup\$ – Jonathan Allan Jul 29 at 17:21
  • \$\begingroup\$ @JonathanAllan, you are correct, i did not have the time to add explanation when submitting i will try to do \$\endgroup\$ – Nahuel Fouilleul Jul 30 at 6:28
0
\$\begingroup\$

JavaScript (ES6),  62  59 bytes

Saved 3 bytes thanks to @Shaggy

This is a fixed version of Luis' answer (now deleted) to support all characters.

a=>b=>a.map(e=escape).join(b.map(e)).split`,`.map(unescape)

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.