# Disassemble tables

Given a string that represents a bunch of tables stacked on top of each other and/or arranged side-by-side, disassemble all the tables.

## Tables

A table looks like this:

-------
|     |
|     |

More formally, a table has a tabletop, composed of n dashes, and two visible legs. (Obviously, there are two hidden behind, but for the purposes of this challenge, a table has two legs. (Think of the legs as being thick legs that span the entire length of the table.)

The visible legs are both m pipes (|) high, and are separated by n-2 spaces. (Thus, the string representation of a table has m+1 lines of exactly n characters per line.)

Does it sound easy to disassemble a table? Think again.

## Stacked tables and tables arranged side-by-side

The table above is just one of the many tables you could be given as input. In actuality, oftentimes you will have a huge number of tables you wish to disassemble, and to save space in your home, you might decide to stack them on top of each other, and sometimes arrange these stacks side-by-side.

This would not be a problem if the tables were all the same height and length. However, obviously there is the possibility of having a messy arrangement of tables, and you should be able to disassemble these arrangements as easily as you would disassemble a single table.

This is an example of an arrangement:

---------
|       |             ---
|       |             | |
-------------          -------------------
|           |          |                 |
---------------         |                 |
|             |         |                 |
|             |-----------------------------------
|             ||                                 |
|             ||                                 |

## How to disassemble tables, or arrangements thereof?

Disassembling tables consists of separating the top, leg one, and leg two from each other. Your answer to this challenge should output these separated parts in their appropriate orientation.

For instance, when disassembling the single table shown in the section "Tables", you should get the following:

-------

| |
| |

Disassembling the complex arrangement above gives this:

--------- --- ------------- ------------------- --------------- -----------------------------------

| | | | | | | | | | | |
| |         | | | | | |
| | | |
| |

Your answer to this challenge can choose to output in this neat format, where the tops are laid next to each other at the top of the output and all the legs are laid next to each other below the tops, with all the parts in their correct orientation, or you can choose to output a list of all the parts, but in the correct orientation. What you may not do, however, is output the input unchanged and say "look, all the parts are there". To assess whether an output format is reasonable, determine whether or not the output format shows a clear separation between the parts. You may output the parts in any order, as long as the output format remains reasonable. (No outputting full tables, for instance.) Note also that if outputting in the neat format, all tabletops must be at the top of the output, and the legs below all the tabletops. However, the order of the tabletops and the order of the legs are both flexible.

Additionally, output may also be in the form of two lists, one representing a list of tabletop lengths and one representing a list of leg lengths. This output form may only be used if the lists are clearly separate.

Input may be ASCII art as shown above, a list of lines, or a character matrix.

## More examples

Input:

--
||

Output, which will hereinafter be in the neat format:

--

| |

Why? Your answer should not treat || as one leg; a leg is only one pipe "thick".

Input:

----
||||

Output:

-- --

| | | |

Why? Tables have only two legs. Once your answer sees the first leg of a table, it should only treat the area from that leg until the next leg it sees, as a table. Thus, there are two tables in the above example, not one.

Input:

---------
|   ||  |

Output:

----- ----

| | | |

Why? See above.

Input:

---
| |---
---| |
| || |

Output:

--- --- ---

| | | | | |
| |

Input:

---
| |
--------
|||    |

Output:

--- -- ------

| | | | | |

Input:

---
---------------------------| |
|                         || |
|                         || |
|                         || |
------------------------------
||||||||||||||||||||||||||||||
||||||||||||||||||||||||||||||
||||||||||||||||||||||||||||||

Output:

--------------------------- --- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
| |

## More notes

You may assume that tables will not be "completely underneath tables", that is, no input like this will be given:

--------------------
|                  |
|    -------       |
|    |     |       |
|    |     |       |

You may assume that the arrangement is "stable", so there are no legs "hanging in midair":

-----------------
|               | <- this leg is hanging in midair
|               |
----------
|        |

On a related note, the heights of the legs of a table are the same (this is stated above, but I want to make it extremely clear). This means that this input will not be given:

-----------
|         |
-------   |
|     |   |

You may assume that tables are stacked only in the correct orientation, so no arrangements like this:

|-------
|
|
|-------
-----------
|         |

or this:

|           |
|           |
|           |
-------------
-------------
|           |

This is , so the shortest code, measured in bytes, wins.

Related

• In the output list, if tabletop 3 is in position x in the tabletop list, do legs 3 also need to be in position x in the legs list? Or do we just need a list of tops and a list of legs, in any order? Feb 20, 2022 at 16:43
• @Jonah The latter. Order is flexible. I'll make that really clear. Feb 20, 2022 at 16:44
• Probably a good challenge for Grime. Feb 20, 2022 at 17:40
• @Downvoter, please explain. Feb 20, 2022 at 18:37
• Is this pushing output flexibility too far? Try it online! Feb 20, 2022 at 18:50

# J, 55 bytes

([:(1+_2-~/\])@I.,.@'-|'E.]);&(0-.~,)'\|+'#&>@rxall"1|:

Attempt This Online!

-12 after seeing rule change allowing lists of lengths as the output.

## original answer outputting art, 78 67 bytes

([:('-'<@#~"+1+_2-~/\])@I.,.@'-|'E.]),&(a:-.~,)'\|+',.&.>@rxall"1|:

Try it online!

Note: Regex in J is broken on TIO, so the link above is just for the byte count. I will paste below some output run on my computer on J903.

Also, note that we pre-process the input into lines of text, and that J requires boxing for heterogeneous lists, hence the boxed output.

echo f ];._2 ] 0 :0
---    ----
| |--- |  |
---| |
| || |
)

echo f ];._2 ] 0 :0
---
| |
--------
|||    |
)

Outputs:

┌───┬────┬───┬───┬─┬─┬─┬─┬─┬─┬─┬─┐
│---│----│---│---│|│|│|│|│|│|│|│|│
│   │    │   │   │ │ │ │ │|│|│ │ │
└───┴────┴───┴───┴─┴─┴─┴─┴─┴─┴─┴─┘
┌───┬──┬──────┬─┬─┬─┬─┬─┬─┐
│---│--│------│|│|│|│|│|│|│
└───┴──┴──────┴─┴─┴─┴─┴─┴─┘

## how

• The "legs" part is just doing a regex search for repeated pipes \|+ in the transpose of the input, and then converting it back to one pipe per line.

• The "tabletop" part is slightly more interesting, because we have to account for tables that are pushed up next to each other -- ie, sometimes we can only find the demarcations between tabletops by looking at the legs. J has a built-in ability to search for box-shape strings withing a 2D grid, and so we use E. to find instances of:

-
|

within the input. We then do some arithmetic on those found indexes to find the length of all tabletops, and convert those lengths back to ascii strings like -----.

• Wow, great job! Was hoping it would take a bit more time to get an answer. Feb 20, 2022 at 17:42
• You can use J on ATO now, where regex is hopefully not broken. (It's the latest version of J, at least). [disclaimer: I created and maintain ATO] Feb 20, 2022 at 20:40
• @pxeger Thanks! Updated. Feb 20, 2022 at 20:58

# Jelly, 23 bytes

=”|µZṣ€0Ẏ¹Ƈ,<ƝT€Fr2/ƲẈ€

A monadic Link accepting a list of lines that yields a list of lists of leg lengths and tabletop lengths.

Try it online!

### How?

=”|µZṣ€0Ẏ¹Ƈ,<ƝT€Fr2/ƲẈ€ - Link: list of lists of characters, L
=”|                     - L equals '|'? (vectorises)
µ                    - new monadic chain - f(X=that)
Z                   - transpose X
ṣ€0                - split each at zeros
Ẏ               - tighten
¹Ƈ             - keep truthy (non-empty) ones
-> our legs (as lists of 1s)
Ɲ          -   for neighbours:
<           -     less than?
T€        -   truthy indices of each
F       -   flatten
2/    -   reduce pairs by:
r      -     inclusive range
-> our tabletops (as lists of positive integers)
,            - pair legs and tabletops
Ẉ€ - for each: length of each

# Charcoal, 64 62 bytes

ＷＳ⊞υι≔Ｅθ⪫⪪⭆υ§λκ-|¦#|θＦθＦΦ⪪ι#λ«Ｐ↓№κ|→→»←⸿⸿ＦＥυ⌕Ａ⭆θ§λκ#Ｗι«⊕⁻⊟ι⊟ι→

Try it online! Link is to verbose version of code. Takes input as a rectangular list of newline-terminated strings. Explanation:

ＷＳ⊞υι

Input the stack of tables.

≔Ｅθ⪫⪪⭆υ§λκ-|¦#|θ

Transpose the stack and identify the ends of the tabletops i.e. those parts above the legs.

ＦθＦΦ⪪ι#λ«

For each column of the original stack split it on tabletop ends, dropping the first element (which is the space above the first end) and loop over the resulting legs.

Ｐ↓№κ|→→

Print the length of the leg vertically (which Charcoal automatically translates into a line of |s) without moving the cursor and then allow two columns for the next leg.

»←⸿⸿

Move up to the start of the previous row but one.

ＦＥυ⌕Ａ⭆θ§λκ#

Transpose the stack back and find all of the tabletop ends.

Ｗι«⊕⁻⊟ι⊟ι→

Loop over the ends in pairs, print the inclusive difference between each pair (which Charcoal automatically translates into a line of -s) and then allow a column for the next tabletop.

# 05AB1E (legacy), 27 bytes

ζD„-|T:ζS®Ãƶþια>sS'|QγO0K‚

Input as a list of lines; output as a pair of integer-lists, the first being the width of the table-tops and the second the length of the table-legs.

Explanation:

Uses the legacy version of 05AB1E because it can zip/transpose on a list of strings, whereas the new version of 05AB1E would require a character-matrix.

Step 1: Get the lengths of the table-tops:

ζ         # Zip/transpose the (implicit) input-list; swapping rows/columns
D        # Duplicate this list of columns
„-|T:   # Replace each "-|" with "10"
ζ  # Zip/transpose back to a list of lines
S         # Convert the list to a flattened list of characters
®Ã       # Only keep all "-" and "1" characters
ƶ      # Multiply each by its 1-based index
þ     # Only keep the integers, removing any "-"-strings
ι    # Uninterleave into two lists
# Pop and push both lists separated to the stack
α  # Get the absolute difference between the values at the same positions
# of the two lists
> # Increase these integers by 1

Step 2: Get the lengths of the table-legs:

s         # Swap so the duplicated list of columns is at the top
S        # Convert it to a flattened list of characters
'|Q    '# Check for each character if it's equal to "|"
γ    # Group the list of 0s/1s into equal adjacent values
O   # Sum each group
0K # Remove all 0s

Step 3: Output the result:

‚         # Pair the two lists of lengths together
# (after which it is output implicitly as result)

# SnakeEx, 81 bytes

m:[({a<R>A}{b<R>A}*{a<R>P}~([^|]*(|[^|]*)%{2}*$))(<R>(~\-)|+!|)] a:\-<L>~| b:\-~ Highlights each tabletop and each leg as a separate match. Try it here! ### Explanation Here's a somewhat ungolfed version: main:[{top<>}{leg<>}] leg:<R>(~\-)|+!| top:{left<R>A}{mid<R>A}*{right<R>P}{check<>S} left:\-~| mid:\-~ right:\-<L>~| check:[^|]*(|[^|]*)%{2}*$

The main snake uses brackets to match either a top or a leg.

To match a leg:

<R>           Turn right (initially, the match pointer faces east, so turn south)
\-       Match a literal hyphen
(~  )      but don't mark it as part of the result
|+    Match one or more pipes
!|  Assert that it's impossible to match another pipe

To match a top:

{left    }                                 Match the left edge of a table
<R>                                   turning right (south) to do so
A                                  and advance one step to the east afterward
{mid    }*                       Match 0 or more middle sections
<R>                          turning right (south) to do so
A                         and advance one step to the east each time
{right    }            Match the right edge of the table
<R>              turning right (south) to do so
P             and move the cursor to the end of that match
{check<> }  Check that this isn't part of any other
table (see below)
S   but don't mark that as part of the result

The left edge is:

\-    Literal hyphen
|  Followed by a pipe
~   which isn't marked as part of the result

(Recall that we're moving southwards when we match this.)

Similarly, the mid is \-~ (same thing but with a space). The right edge is the same as the left, except it adds a <L> to turn left (east). This positions the match cursor just to the east of the table leg, ready to run the check.

We need check because unlike regex, SnakeEx uses overlapping matches. So input like this

----
||||

would find not two but three matches for top, counting the two hyphens in the middle. To prevent such incorrect matches, we'll assert that the right side of each table must be followed by an even number of table legs.

[^|]*(|[^|]*)%{2}*$After a table's right leg, proceeding eastward, match: [^|]* 0 or more non-leg characters ( ) This sequence: | Leg character [^|*] 0 or more non-leg characters %{2} Repeated twice * Repeated 0 or more times$  Edge of input

For the golfed version, we basically just combine left with right and inline top, leg, and check into main.

# Python3, 456 bytes:

lambda x:' '.join(a for *_,a in{(a,b+1,I,u[a:b+1])for I,u in E(x.split('\n'))for B,U in E(x.split('\n'))for a,b in g(U)if B-I==1 and set(u[a:b+1])=={'-'}})+'\n\n'+'\n'.join(' '.join(m)for m in Z(*sorted(j for k in Z(*x.split('\n'),fillvalue='')for j in findall('\|+',''.join(k))),fillvalue=' '))
from re import*
from itertools import*
E=enumerate
Z=zip_longest
r=lambda x:[(x[i],x[i+1])for i in range(0,len(x),2)]
g=lambda x:r([i for i,a in E(x)if a=='|'])

Try it online!

# Vyxal, 32 bytes

\|=:∩0v€Þfꜝ\$2lƛÞṡƒ<T›;f2ẇvƒṡ"vvL

Try it Online!

Port of Jelly.

Port of 05AB1E ended up at same byte count: Try it Online!