16
\$\begingroup\$

This is a simpler variant of my previous challenge Don't touch the walls!, as suggested by Jonah.

Given a multi-dimensional rectangular array of integers between 0 and 9, output all the elements which are "touching the edges".

Output is very flexible: it can be in any order, and may be flattened or partially nested, but must contain every edge element the correct number of times, and may not contain any others.

Specification

"Touching the edges" is a hopefully fairly intuitive concept, but here is a specification:

Let's use this array, with shape 4, 3.

[[1, 3, 0],
 [2, 6, 7],
 [4, 0, 2],
 [0, 0, 0]]

Using one-based indexing, we can index any element of the array using two integers in the ranges \$ [1, 4] \$ and \$ [1, 3] \$.

The elements which are touching the edge are those with coordinates of the form 1, y, 4, y, x, 1, or x, 3.

So, formally, we can say, for an element to be "touching the edge", at least one part of its multidimensional indices is either 1 (the minimum possible coordinate), or the maximum possible coordinate (which is the size of the array in the respective dimension).

Here is the same array with the elements which are "touching the edge" highlighted:

[[1, 3, 0],
 [2, 6, 7],
 [4, 0, 2],
 [0, 0, 0]]

The output could be anything like:

1 3 0 2 7 4 2 0 0 0
[1, 2, 4, 0, [0, 0, [2, 7, 0], [3]]]
[[[[[[4]]]]], [[[0, 0, 0, 0]]], [[2, 2]], [1, 7, 3]]

For the 1-dimensional array [3, 4, 9, 0, 0], the edges are [3, 4, 9, 0, 0], so the output is [3, 0].


For this 3-dimensional array:

[[[4, 0, 4, 1],
  [0, 0, 5, 8],
  [6, 0, 0, 0],
  [0, 3, 7, 0]],

 [[4, 9, 8, 5],
  [0, 6, 4, 0],
  [0, 0, 0, 3],
  [0, 9, 6, 1]],

 [[0, 4, 7, 9],
  [0, 2, 0, 6],
  [0, 0, 0, 0],
  [7, 0, 3, 0]],

 [[0, 5, 3, 5],
  [0, 0, 0, 0],
  [6, 5, 3, 0],
  [4, 6, 0, 8]]]

the edges are:

[[[4, 0, 4, 1],
  [0, 0, 5, 8],
  [6, 0, 0, 0],
  [0, 3, 7, 0]],

 [[4, 9, 8, 5],
  [0, 6, 4, 0],
  [0, 0, 0, 3],
  [0, 9, 6, 1]],

 [[0, 4, 7, 9],
  [0, 2, 0, 6],
  [0, 0, 0, 0],
  [7, 0, 3, 0]],

 [[0, 5, 3, 5],
  [0, 0, 0, 0],
  [6, 5, 3, 0],
  [4, 6, 0, 8]]]

The output is something like:

[4, 0, 4, 1, 0, 0, 5, 8, 6, 0, 0, 0, 0, 3, 7, 0, 4, 9, 8, 5, 0, 0, 0, 3, 0, 9, 6, 1, 0, 4, 7, 9, 0, 6, 0, 0, 7, 0, 3, 0, 0, 5, 3, 5, 0, 0, 0, 0, 6, 5, 3, 0, 4, 6, 0, 8]

Imagine each inner 2-dimensional array as a cross-section of a 3-dimensional box. The only elements which are not touching the edges are the ones on the inside of the box.

Note that arrays which have one of their dimensions as only 1, such as [4] or [[3, 6, 5]], may need to be treated with care. Their respective outputs should be [4] and [3, 6, 5].

Rules

\$\endgroup\$
12
  • \$\begingroup\$ Sandbox \$\endgroup\$
    – pxeger
    Commented Jun 21, 2022 at 16:23
  • \$\begingroup\$ May we take a second input specifying the shape of the first? \$\endgroup\$
    – chunes
    Commented Jun 21, 2022 at 16:49
  • 1
    \$\begingroup\$ My answer currently fails for arrays with 1 number, e.g. [5] or [[[5]]]. If we are supposed to handle these it would make a good test case. \$\endgroup\$
    – chunes
    Commented Jun 21, 2022 at 19:44
  • 1
    \$\begingroup\$ @LuisMendo That's not what I meant; if it's a standard IO method, you can use it. \$\endgroup\$
    – pxeger
    Commented Jun 22, 2022 at 5:46
  • 1
    \$\begingroup\$ @Sandra your solution just work in theory for any number of dimensions, although it's fine if in practice you're limited by your language's capabilities \$\endgroup\$
    – pxeger
    Commented Jun 22, 2022 at 6:52

12 Answers 12

7
\$\begingroup\$

Python 2, 38 bytes

def f(a):a[1:-1]*=a>[f];map(f,a[1:-1])

Try it online!

Expects a nested list and removes all non surface elements inplace.

How?

a>[f] uses that in Python 2 every list is greater than every function is greater than every number to check whether a is a nested or flat list.

If nested the middle of a is multiplied by one, in other words, nothing changes. The recursion will then descend one level.

If flat the middle of a is multiplied by zero, in other words, excised. This will stop the recursion because map will run over an empty list.

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

J, 25 24 bytes

#~&,(<:0&e.@:|"1]#:i.)@$

Try it online!

  • Create multidimensional indices for each element using mixed base binary
  • Mod each of those by the (input dimensions- 1)
  • Check if the multidimensional index has a 0. If so, keep that element. Otherwise, remove it.
\$\endgroup\$
4
\$\begingroup\$

Wolfram Language (Mathematica), 38 bytes

MapAt[#0,#,2;;-2]~Check~#/._MapAt->{}&

Try it online!

Replaces non-edge elements with {}.

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

JavaScript, 44 bytes

f=a=>a.at?[a.shift(),a.pop()??a,a.map(f)]:[]

f=a=>a.at?[a.shift(),a.pop()??a,a.map(f)]:[]

test = a => console.log(JSON.stringify(f(a)))
test([[[4, 0, 4, 1],
  [0, 0, 5, 8],
  [6, 0, 0, 0],
  [0, 3, 7, 0]],

 [[4, 9, 8, 5],
  [0, 6, 4, 0],
  [0, 0, 0, 3],
  [0, 9, 6, 1]],

 [[0, 4, 7, 9],
  [0, 2, 0, 6],
  [0, 0, 0, 0],
  [7, 0, 3, 0]],

 [[0, 5, 3, 5],
  [0, 0, 0, 0],
  [6, 5, 3, 0],
  [4, 6, 0, 8]]])
test([[1, 3, 0],
 [2, 6, 7],
 [4, 0, 2],
 [0, 0, 0]])
test([4])
test([[3, 6, 5]])

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

R, 60 59 bytes

Edit: -1 byte thanks to @Giuseppe.

\(a,i=which(a|1,T))a[i[apply(i,1,\(x)!all(x-1&x-dim(a))),]]

Attempt This Online!

\$\endgroup\$
2
  • \$\begingroup\$ 59 bytes \$\endgroup\$
    – Giuseppe
    Commented Jun 22, 2022 at 3:23
  • \$\begingroup\$ @Giuseppe thanks! I tried this one and it didn't work for me, but checking it now - I forgot to change | to & (facepalm). \$\endgroup\$
    – pajonk
    Commented Jun 22, 2022 at 6:52
2
\$\begingroup\$

Factor, 73 72 83 bytes

: f ( x -- ) dup real? [ drop ] [ 1 cut 1 short cut* rot append . [ f ] each ] if ;

Try it online!

Recursive function that (maybe) slices both ends off the input, prints them, then calls itself on all the remaining elements until an atom (real number) is reached. ((Now handles arrays with a single element properly at the cost of 11 bytes...))

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

APL (dzaima/APL), 20 bytes

⊢⌿⍥,⍨1∊¨⍳∘⍴∊¨¨∘⊂1,¨⍴

Try it online!

 shape (length along each dimension) of the array

1,¨ prepend 1 to each

∊¨¨∘⊂ for each of each of the following, is it a member of the corresponding element of that?

⍳∘⍴ the indices of an array of the shape of the argument

1∊¨ for each of those, is 1 a member?

⌿⍥,⍨ use that flattened, to filter the flattened…

 argument

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

BQN, 19 bytesSBCS

1¨⌾⊏⟜⍉⟜⌽⍟=⍟2∘≠˜⥊⊸/⥊

 one for each

⌾⊏ of the elements of the first major cells (n−1 dimensional sub-array)

⟜⍉ of the single-step-rotated-axes version

⟜⌽ of the reverse

⍟= done as many times as there are dimensions

⍟2 done twice

∘≠˜ of the self-inequality (an all-zero array of same shape)

⥊⊸/ when flattened, filters

 the flattened argument

Run online!

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

Retina, 84 bytes

L`(?<=\[(\[(?(3)$)((])|(?<-3>\[)|\d|,)+)*)\d|\d(?=(((\[)|(?<-6>])|\d|,)+(?(6)^)])*])

Try it online! Outputs each edge element on its own line but link is to test suite that joins on commas for convenience. Explanation:

L`

List matches.

(?<=\[(\[(?(3)$)((])|(?<-3>\[)|\d|,)+)*)\d|

Similar to the following, but using a lookbehind to find whether the element is the first in any of its dimensions. Due to the way lookbehinds are evaluated, this version is harder to explain, so you'll just have to take my word for it that it's the mirror image of the following test.

\d(?=

Match a digit. In Retina 1 it's possible to use overlapping matches and captures to avoid using a lookahead but it costs 7 bytes to do that and you only save 4 so it's not worth it.

(((\[)|(?<-6>])|\d|,)+(?(6)^)

Match any number of brackets, digits, or commas, but there must be the same number of opening and closing brackets.

])*

Match the close bracket at the end of this subarray, thus considering the parent subarray. Go out as many dimensions as necessary.

])

Match an extra close bracket, showing that this element or one of its container subarrays was the last in its parent.

Replacing the L with ! results in a version that works in Retina 0.8.2 but in this case it's no longer possible to use the same test suite.

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

Python3, 95 bytes:

f=lambda x,t=0:[x]*t if x*0==0 else f(x[0],1)+[j for k in x[1:-1]if(j:=f(k,t or 0))]+f(x[-1],1)

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ int==type(x) can be x*0==0, [[],[x]][t] can be t*[x] \$\endgroup\$
    – naffetS
    Commented Jun 21, 2022 at 17:02
  • \$\begingroup\$ you can change [x]*t to t*[x] and remove the space after it, and remove the space after x*0==0 \$\endgroup\$
    – naffetS
    Commented Jun 21, 2022 at 18:29
1
\$\begingroup\$

Jelly,  12  11 bytes

ŒJ%þ`ṪỊ§TịF

A monadic Link that accepts a rectangular multi-dimensional list and yields the elements that are on its "surface".

Try it online!

How?

ŒJ%þ`ṪỊ§TịF - Link: rectangular multi-dimensional list, M
ŒJ          - multi-dimensional indices of M -> [[1,1,1,...],...,[height,width,depth,...]]
    `       - use as both arguments of:
   þ        -   make a table of:
  %         -     modulo (vectorises)
     Ṫ      - tail -> [...[x%height,y%width,z%depth,...]...]
      Ị     - abs(x)<=1? (vectorises)
       §    - sums (of those)
        T   - truthy indices (of that)
          F - flatten (M) (N.B. same order as the indices produced by ŒJ)
         ị  - index-into
\$\endgroup\$
1
\$\begingroup\$

05AB1E, 26 bytes

˜s"DdiëdƶZ%2‹εZ≠i®.V"©.V˜Ï

05AB1E is very verbose with recursive approaches, and it's also not too good in most matrix-based challenges, so the combination ends up as this horrendously long program (although can probably still be golfed a bit).

Try it online or verify all test cases.

Explanation:

˜            # Flatten the (implicit) multi-dimensional input-list
 s           # Swap to get the actual input again
  "..."      # Push the recursive string explained below
       ©     # Store it in variable `®` (without popping)
        .V   # Evaluate and execute it as 05AB1E code,
             # using the multi-dimensional list as argument
          ˜  # Flatten the result as well
           Ï # Keep all values of the flattened input at the truthy (==1) indices
             # (after which the result is output implicitly)

D            # Copy the current list or integer
 dië         # If it's a list:
  i          #  If
 d           #  the current value is truthy after a >=0 check:
             #  (only a non-negative integer would be truthy, and lists falsey)
             #   Do nothing
   ë         #  Else (it's a list):
    d        #   Transform each integer to a 1 with a >=0 check
     ƶ       #   Multiply each 1 by its 1-based index
      Z      #   Push the flattened maximum integer (without popping)
       %     #   Modulo
        2‹   #   Check which are smaller than 2 (first row=1s; last row=0s)
             #   (all first and last items are now 1s)
    ε        #   Then map over each inner list:
     Z       #    Push the flattened maximum (without popping)
      ≠i     #    If this maximum is NOT 1:
        ®.V  #     Do a recursive call with the current inner list
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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