19
\$\begingroup\$

Add++, the Language of the Month, has the "collect" builtin as BC. Your task is to implement this builtin.


Consider a non-empty array, where each element is either:

  • A positive digit between 1 and 9 inclusive, or
  • A non-empty list of positive digits between 1 and 9

For example, all the following such arrays meet this definition:

[1, 2, 3, 4, 5]
[7, [5, 3], 2]
[1, [2, 2], 3, [4, 4], 5]
[[1], 2, 3, [4], 5, [4], 2]
[[9, 4, 2]]
[[1], 2, 3, 4, 5, [6], 7]
[6, [1, 9, 4]]

Note that elements can be repeated, both the digits, the inner lists and the elements of the inner lists.

The BC builtin "collects" adjacent digits in the array, and groups them into a list, while leaving the existing lists untouched. Applying this to the above lists, the examples make this clear:

[[1, 2, 3, 4, 5]]
[[7], [5, 3], [2]]
[[1], [2, 2], [3], [4, 4], [5]]
[[1], [2, 3], [4], [5], [4], [2]]
[[9, 4, 2]]
[[1], [2, 3, 4, 5], [6], [7]]
[[6], [1, 9, 4]]

For example, with [[1], 2, 3, 8, 9, [6], 7], we group the 2, 3, 8, 9 together, and the 7 to give [[1], [2, 3, 8, 9], [6], [7]]

You should take a ragged list as input, that meets the list format described above, and output a list of lists after the BC builtin has been applied to the input. This is , so the shortest code in bytes wins.

Test cases

[[9, 4, 2]] -> [[9, 4, 2]]
[6, [1, 9, 4]] -> [[6], [1, 9, 4]]
[7, [5, 3], 2] -> [[7], [5, 3], [2]]
[1, 2, 3, 4, 5] -> [[1, 2, 3, 4, 5]]
[1, [2, 2], 3, [4, 4], 5] -> [[1], [2, 2], [3], [4, 4], [5]]
[[1], 2, 3, 8, 9, [6], 7] -> [[1], [2, 3, 8, 9], [6], [7]]
[[1], 2, 3, [4], 5, [4], 2] -> [[1], [2, 3], [4], [5], [4], [2]]
[9, 8, 7, 6, [5, 4, 3], 2, 1] -> [[9, 8, 7, 6], [5, 4, 3], [2, 1]]
[[8, 2], 9, 5, 1, [6, 4], [4, 5]] -> [[8, 2], [9, 5, 1], [6, 4], [4, 5]]
[7, [5, 3], 2, [1, 2, 3], 9, 8, 7, 8, 9, [3, 4], [1], [2]] -> [[7], [5, 3], [2], [1, 2, 3], [9, 8, 7, 8, 9], [3, 4], [1], [2]]
\$\endgroup\$
4
  • \$\begingroup\$ Are spaces between adjacent elements required in the output? I.e., Is [[3,4,5]] the same of [[3, 4, 5]]? Also, how is the input formatted exactly, are spaces always there? \$\endgroup\$ Jan 3 at 2:14
  • 1
    \$\begingroup\$ @RiccardoBucco Spaces are not required - any reasonable format that represents a nested list is fine, see e.g. pxeger's Python answer \$\endgroup\$ Jan 3 at 2:16
  • \$\begingroup\$ What about the input? \$\endgroup\$ Jan 3 at 2:22
  • \$\begingroup\$ @RiccardoBucco Again, the same applies - choose any reasonable format. Keep your golfing in the challenge task, not the I/O formats :) \$\endgroup\$ Jan 3 at 2:23

22 Answers 22

7
\$\begingroup\$

R, 76 75 bytes

Or R>=4.1, 68 bytes by replacing the word function with a \.

Edit: -1 byte thanks to @Dominic van Essen.

function(x){y[]=cumsum(c(T,y<-sapply(x,is.list))|y);Map(unlist,split(x,y))}

Try it online!

Explanation (outdated a bit)

function(x,         # take x as input
s=sapply(x,is.list) # which elements of x are lists themselves
) 
Map(unlist,         # unlist every element of the following list
split(x,            # split x using...
 cumsum(            # cumsum of
  head(c(T,s),-1)   # s moved one to the right (filled with TRUE in the front)
  |s                # or s
 )                  # this creates a mask on the positions of lists or first non-list elements 
))
\$\endgroup\$
4
  • \$\begingroup\$ I got almost the same (but with one more little trick)... \$\endgroup\$ Jan 3 at 10:49
  • \$\begingroup\$ @DominicvanEssen, nice one - i also tried the y[]= approach, but this ended longer than the ugly head. \$\endgroup\$
    – pajonk
    Jan 3 at 11:05
  • \$\begingroup\$ Perhaps unlist(split(),F) would be acceptable instead of the Map? \$\endgroup\$
    – Giuseppe
    Jan 3 at 14:55
  • 1
    \$\begingroup\$ @Giuseppe - this has wrong depth for to-be-lists, eg. in the last test case. Try it online! \$\endgroup\$
    – pajonk
    Jan 3 at 15:41
7
\$\begingroup\$

Python 2, 68 bytes

lambda L:eval(re.sub("[^\d]{3,}","],[",`[[0,L,0]]`))[1:-1]
import re

Textually replaces every stretch of more than 2 non digit characters (i.e. everything that is not ", ") with "],[". A little trickery at the ends is required to guarantee balanced brackets.

Attempt This Online!

Old Python 2, 73 bytes

lambda L:[map(int,x[1::3])for x in re.findall("(?:.\d.)+",`L`)]
import re

Attempt This Online!

Just a stoopid regex that finds stretches of digits alternating with two arbitrary other characters (in the repr of the input).

\$\endgroup\$
6
\$\begingroup\$

Wolfram Language (Mathematica), 30 27 bytes

##&@@@#&/@Split[#,#>0<#2&]&

Try it online!

          Split[#        ]  group adjacent items which
                 ,#>0<#2&     are both positive (non-lists)
##&@@@#&/@                  remove extraneous brackets

At times like this, it'd be nice if @@, @@@ generalized to more @s...

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

J, 22 19 bytes

<@;;.1~2+./\1,#@$&>

Try it online!

-3 thanks to Bubbler

This is a little tricky in J, which requires boxes for heterogeneous arrays. The task becomes to "collect" iff the box contents are atoms. Contents which are already lists (either 1 items or more) should remain as single boxes.

Our strategy is then to "cut" (start a new group) unless adjacent elements are both atoms.

\$\endgroup\$
3
  • \$\begingroup\$ A bit of restructuring to get 19. Also ,:1 is not a valid representation of a singleton list here; ,1 is. \$\endgroup\$
    – Bubbler
    Jan 3 at 3:34
  • \$\begingroup\$ Thanks, nice edits. One q: other than , being shorter than ,: why do you say that it's not a valid representation? Afaict ,1 and ,:1 have identical shapes... am I missing something? \$\endgroup\$
    – Jonah
    Jan 3 at 4:10
  • 1
    \$\begingroup\$ Oh sorry, I guess I mixed it up with ,.. Yeah, ,: works identically to , here. \$\endgroup\$
    – Bubbler
    Jan 3 at 4:17
5
\$\begingroup\$

Factor + splitting.extras, 43 42 bytes

[ [ array? ] split*-when [ flatten ] map ]

Try it online!

Explanation

  • [ array? ] split*-when Split on arrays (sublists) and keep the delimiters.
  • [ flatten ] map Flatten each element. flatten doesn't touch arrays of depth 1 but otherwise decreases depth to 1.
                        ! { 1 { 2 2 } 3 { 4 4 } 5 }
[ array? ] split*-when  ! { { 1 } { { 2 2 } } { 3 } { { 4 4 } } { 5 } }
[ flatten ] map         ! { { 1 } { 2 2 } { 3 } { 4 4 } { 5 } }
\$\endgroup\$
5
\$\begingroup\$

Japt, 6 5 bytes

If the sub-arrays in the input could be nested another level deep in the output then the last 3 bytes could be removed.

ó* mc

Try it

\$\endgroup\$
4
\$\begingroup\$

J-uby, 46 bytes

:slice_when+:|%([~:*&0|:!=&0]*2)|:map+:flatten

Try it online!

Today I learned about slice_when, and J-uby's +.

-4 from translating GB's idea.

Ruby, 49 bytes

->a{a.slice_when{_1*0!=0||_2*0!=0}.map &:flatten}

Attempt This Online!

-6 bytes from GB.

\$\endgroup\$
5
  • \$\begingroup\$ This doesn't appear to do the task specified? Take a look at the test cases vs your outputs \$\endgroup\$ Jan 3 at 2:12
  • 2
    \$\begingroup\$ Btw, Attempt This Online has a more up to date Ruby (3.0) \$\endgroup\$
    – pxeger
    Jan 3 at 2:25
  • \$\begingroup\$ thanks for the feedback caird and @pxeger \$\endgroup\$
    – Razetime
    Jan 3 at 4:30
  • \$\begingroup\$ x*0==[] -> is an array, x*0==0 -> is a number. And: why do you need bracket around (_2==[*_2])? \$\endgroup\$
    – G B
    Jan 3 at 9:36
  • \$\begingroup\$ I'd written the ruby solution so i could simplify to J-uby easier, so i added unnecessary parens. Thanks for the suggestions! They helped both answers. \$\endgroup\$
    – Razetime
    Jan 3 at 10:02
4
\$\begingroup\$

Python 2, 84 79 bytes

s=[]
for x in input()+[{}]:
 if x>9:
    if s:print s
    s=x[:0];print x
 else:s+=x,

Attempt This Online!

-5 bytes thanks to @ovs

\$\endgroup\$
1
  • 1
    \$\begingroup\$ 79 bytes, terminates with an error \$\endgroup\$
    – ovs
    Jan 3 at 15:47
4
\$\begingroup\$

Haskell + hgl, 21 bytes

In order to do this I needed to add support for Either types to hgl. I was planning on doing this eventually and I don't think knowledge of this challenge influenced the way things were implemented. So this is non-competing for whatever that means to you.

fR p<tv(mst p)<+gB ø

This takes a List (Either Int (List Int)) to represent a ragged array. Surprisingly we can do flip the order of the Either at no cost to bytes.

fL p<tv(mst p)<+gB ø

Relfections

There are some things here that could be improved.

  • m<mst should get it's own command. It's not used here but if I had had it this could have been 1 byte shorter:
    fR p<sQ<m2t p<+gB ø
    
    where m2t = m<mst. (Implemented in 871c2406)
  • There needs to be a Bitraversable class. The whole reason we have mst p a very costly construct is that we need to massage the correct monoid instance onto the left hand side of the Either. If we had Bitraversable, the entire tv(mst p) could just be a bisequence.
  • Other Bifunctor classes might have also helped here and are probably needed in the long run. Either isn't the only type that would benefit from this. We have other bifunctors like (,) and These which would benefit as well.
  • Here I expressed ragged lists as lists of either the element or more lists. This works for ragged lists of max depth 2 fine, but it would also be possible to represent ragged lists more generally using the free monad combinator. If I had that I could have tried an approach with that, and even so I should probably have that for future challenges where the depth restriction might not exist.

Most of these basically boil down to "better bifunctor" support, so I think some of that is warranted.

\$\endgroup\$
4
\$\begingroup\$

Jelly, 12 11 bytes

ŒḊ€aJŒg⁸ṁF€

Try it online!

ŒḊ€   Depth of each (1 for lists, 0 for numbers)
aJ    Replace each 1 with its index  [0 0 1 0 1 1] -> [0 0 3 0 5 6]
Œg    Group runs of equal elements [[0 0] [3] [0] [5] [6]]
⁸ṁ    Reshape the original input to this list
F€    Flatten each element (turns entries like [[1 2]] back into [1 2])

This approach feels elegant in theory, but a lot of bytes are wasted on €@µ€, so there might be saves.

Jonathan Allan saved a byte! Thanks.

\$\endgroup\$
1
  • \$\begingroup\$ I tried this out too but could not get it to ten, but ŒḊ€aJŒg⁸ṁF€ saves one. \$\endgroup\$ Jan 4 at 1:59
3
\$\begingroup\$

Vyxal 2.7.1, 6 bytes

ƛf⁼;Ġ•

Try it Online!

The link uses the latest version of vyxal (2.7.3 at the time of writing) which has a bug fix where molding no longer wraps singletons in lists, hence the extra vf.

Explained

ƛf⁼;Ġ•
ƛf⁼;     # For each item in the input, does the flattened version equal the item? For integers, this returns a list, which does not equal the item. For lists, this just returns the list - this wouldn't work if there wasn't any depth restriction. 
   Ġ    # Group on consecutive items
    •   # and mold the input to that shape
\$\endgroup\$
3
\$\begingroup\$

Retina 0.8.2, 34 bytes

(?<=^\[|], )[^][]+(?=, \[|]$)
[$&]

Try it online! Link includes test cases. Would be 32 bytes if I could be bothered to remove all of the spaces from the lists. Explanation: Simply matches sublists composed of non-lists that are between two lists or positioned at the start and/or end of the main list and wraps them in a list. Using lookarounds to anchor the match avoids having to explicitly capture the sublist thus saving a byte.

(?<=^\[|], )

Match at the beginning of the main list or after a list.

[^][]+

Match anything that isn't a list.

(?=, \[|]$)

Match at the end of the main list or before a list.

[$&]

Wrap the sublist in a list.

\$\endgroup\$
3
\$\begingroup\$

Ruby, 46 bytes

->l{l.chunk{|x|x*0}.flat_map{|a,b|a==0?[b]:b}}

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ .chunk{|x|x*0} is really clever! \$\endgroup\$
    – Lynn
    Jan 3 at 19:41
3
\$\begingroup\$

JavaScript (Node.js), 57 bytes

a=>a.flatMap(c=n=>n[0]?c=[,n]:c[0]?c.push(n)&&[]:[c=[n]])

Try it online!

\$\endgroup\$
3
\$\begingroup\$

05AB1E, 6 bytes

.γd}€˜

Try it online or verify all test cases.

Explanation:

.γ     # Adjacent group-by:
  d    #  Transform every integer into a 1 (with a >=0 check)
   }€  # After the group-by: map over each group:
     ˜ #  Flatten it
       # (after which the list of lists is output implicitly as result)
\$\endgroup\$
2
\$\begingroup\$

Add++, 6 bytes

L*~,BC

Try it online!

Wow holy crap a challenge where Add++ will probably win. Nevermind I tied myself with vyxal.

Explained

L*~,BC
L*~,   ; a lambda that dumps its arguments onto the stack and returns the entire stack that:
    BC ; calls the built-in this challenge is all about. 
\$\endgroup\$
2
  • 2
    \$\begingroup\$ Wow, who knew Add++ could be this concise?! \$\endgroup\$ Jan 3 at 0:49
  • 2
    \$\begingroup\$ @cairdcoinheringaahing I know right!! It's almost as if this challenge was designed around Add++!!! \$\endgroup\$
    – lyxal
    Jan 3 at 0:50
2
\$\begingroup\$

Jelly, 11 bytes

Really not sure if this is optimal, it's just the first way I though of...

,⁻;ɗƝŻœṗ⁸F€

Try it online!

How?

,⁻;ɗƝŻœṗ⁸F€ - Link: list, A
    Ɲ       - for each of the neighbours:
   ɗ        -   last three links as a dyad, f(left, right):
,           -     pair them        (e.g. [1,2] and 3 -> [[1,2],3]])
  ;         -     concatenate them (e.g. [1,2] and 3 -> [1,2,3])
 ⁻          -     are these not equal? (i.e. was either left or right a list?)
     Ż      - prefix a zero
        ⁸   - use A as the right argument of...
      œṗ    - partition right at truthy indices of left
         F€ - flatten each
\$\endgroup\$
2
\$\begingroup\$

Retina, 29 bytes

["[["|"], ["]"]]"L`\b[^][]+\b

Try it online! Link includes test cases. Explanation: Based on @loopywalt's idea. Would be 28 bytes if I could be bothered to remove all the spaces from the lists.

L`\b[^][]+\b

List all runs of digits and separators (but excluding []s).

["[["

Precede the entire result with [[.

|"], ["

Separate each run with ], [.

]"]]"

Suffix ]] to the result.

\$\endgroup\$
2
\$\begingroup\$

Python 3, 87 bytes

lambda L:[[*x]for t,g in groupby(L,type)for x in[g,[g]][t==int]]
from itertools import*

Try it online!

Group the list by type (int or list), then wrap the int groups in list.

I thought groupby would be great for this job, however the itertools import ended up costing too many characters.

Python 2, 84 bytes

def f(L,s=[]):t=s and[s];return L and[f(L[1:],s+L[:1]),t+L[:1]+f(L[1:])][L>[[]]]or t

Try it online!

f=lambda L,s=[]:L and[f(L[1:],s+L[:1]),(s and[s])+L[:1]+f(L[1:])][L[0]>9]or s and[s]

Try it online!

I tried to golf @pxeger's answer by using recursive function, but wasn't able to save any bytes.

These 2 functions simply loop through each element. If the current element is a digit, accumulate it in the list s. If the current element is a list, add the accumulated elements (s) if not empty to the result, followed by the current element.

\$\endgroup\$
2
\$\begingroup\$

Burlesque, 13 bytes

{n!z?}gBm{FL}

Try it online!

{
 n!   # Not for ints (if 0 then 1 else 0); most common element for lists
 z?   # Is zero
}gB   # Group by is list
m{FL} # Flatten each resulting list (double groups those already as lists)

Since the lists are always non-zero we can get away with this hack.

An alternative is doing a list/int only op and then seeing if there's an error (e.g tpis transpose-iserr) unfortunately no single op I know can test for is list

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

Charcoal, 29 bytes

⊞υ⟦⟧FA¿⁺ι⟦⟧⊞⊞Oυι⟦⟧⊞§υ±¹ι⭆¹Φυλ

Try it online! Link is to verbose version of code. Explanation:

⊞υ⟦⟧

Push an empty list to the result in case the first element of the input is not a list.

FA

Loop through all of the elements of the input list.

¿⁼ι⟦⟧«

If adding the empty list to the element doesn't produce an empty list, then...

⊞⊞Oυι⟦⟧

... the element was a list, so push it to the result, plus another empty list in case the next element of the input is not a list, otherwise...

⊞§υ±¹ι

... append the element to the last list of the result.

⭆¹Φυλ

Filter out any empty lists (e.g. from consecutive lists in the input) and pretty-print the result.

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

Perl 5 & probable regex polyglot, 53 bytes

s/(?<!\]),\[|],(?!\[)/],[/g;s/^\[+(.*[^]])]+$/[[\1]]/

Try it online!

First change all "]," or ",[" to "],["; then make sure it's all enclosed in "[[...]]".
I forgot the second bit on my first attempt, so it seemed a bit golfier than it turned out...

\$\endgroup\$
0

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.