20
\$\begingroup\$

Although it's done a few times as sub-challenge of a larger challenge, and we also have a challenge to remove the borders of a square matrix, I couldn't find a challenge to output the borders of a given matrix, so here it is.

Challenge:

Given a rectangular-shaped matrix containing integers as input, output its borders.

Challenge rules:

  • The input is guaranteed to be a rectangular-shaped matrix (or empty)
  • The input can contain negative and duplicated integers
  • The output format and type is flexible and can be in any order (e.g. [[1,2,3],[4,5,6],[7,8,9]] may result in [1,2,3,4,6,7,8,9]; or [[1,2,3],[6,9],[8,7],[4]]; or 9\n8\n7\n6\n4\n3\n2\n1\n; etc.). As long as it's clear that just the border of the input-matrix is output, almost every output-format is allowed. (If you're unsure whether an output format is allowed, feel free to ask in the comments.)

General Rules:

  • This is , so the shortest answer in bytes wins.
    Don't let code-golf languages discourage you from posting answers with non-codegolfing languages. Try to come up with an as short as possible answer for 'any' programming language.
  • Standard rules apply for your answer with default I/O rules, so you are allowed to use STDIN/STDOUT, functions/method with the proper parameters and return-type, full programs. Your call.
  • Default Loopholes are forbidden.
  • If possible, please add a link with a test for your code (e.g. TIO).
  • Also, adding an explanation for your answer is highly recommended.

Test Cases:

Input Output Visualized
[[1,2,3],
[4,5,6],
[7,8,9]]
[1,2,3,4,6,7,8,9] enter image description here
[[]] [] N/A
[[0]] [0] enter image description here
[[ 9, 2,-5, 3],
[18, 3, 8, 0],
[ 2, 7,21,-3],
[ 9,-5,10,99]]
[9,2,-5,3,18,0,2,-3,9,-5,10,99] enter image description here
[[1,2,3]] [1,2,3] enter image description here
[[9],
[8],
[8],
[9]]
[9,8,8,9] enter image description here
[[ 10, 20, 30, 40, 50, 60],
[ 70, 80, 90,-90,-80,-70],
[-60,-50,-40,-30,-20,-10]]
[10,20,30,40,50,60,70,
-70,-60,-50,-40,-30,-20,-10]
enter image description here
[[ 1, 2, 3, 4, 5, 6],
[ 7, 8, 9,10,11,12],
[13,14,15,16,17,18],
[19,20,21,22,23,24],
[25,26,27,28,29,30],
[31,32,33,34,35,36],
[37,38,39,40,41,42],
[43,44,45,46,47,48],
[49,50,51,52,53,54],
[55,56,57,58,59,60]]
[1,2,3,4,5,6,7,12,13,18,
19,24,25,30,31,36,37,42,43,
48,49,54,55,56,57,58,59,60]
enter image description here
[[-11, 18,  3,-40,123],
[ 18,-33, 9,765,100]]
[-11,18,3,-40,123,18,-33,9,765,100] enter image description here
[[14, 3],
[ 3,14],
[14, 3],
[ 3,14],
[14, 3]]
[14,3,3,14,14,3,3,14,14,3] enter image description here
\$\endgroup\$
7
  • 4
    \$\begingroup\$ I think it would be good to add 2xN or Nx2 example to the test cases. \$\endgroup\$ May 15, 2023 at 19:53
  • 2
    \$\begingroup\$ The visualization in the last test case seems really garbled. \$\endgroup\$
    – erfink
    May 16, 2023 at 0:20
  • 1
    \$\begingroup\$ @erfink Fixed, thanks for noticing. \$\endgroup\$ May 16, 2023 at 6:31
  • 1
    \$\begingroup\$ @FryAmTheEggman Fixed. The hyperlinks is something SE does automatically for [1], [2], etc. for the images.. I changed the [8] and [9] image ids to 80 and 90 so I won't have to change the test case.. :/ \$\endgroup\$ May 16, 2023 at 14:32
  • 2
    \$\begingroup\$ @AZTECCO No sorry, the amount of items should correspond with the amount of items in the borders (so it's not allowed to simply output the first and last rows and columns, since the corners would be twice each in the output). How you format those different items is completely up to you, though (see the third challenge rule, or the existing answers, for examples of what's allowed). \$\endgroup\$ May 16, 2023 at 15:42

24 Answers 24

8
\$\begingroup\$

Python 3, 38 bytes

def f(x):
 for c in x[1:-1]:c[1:-1]=[]

Outputs by modifying arguments. The code also works on Python 2.

Explanation: deletes the center part of each center row by modifying the original input.

Attempt This Online! (additional test cases taken from loopy walt's answer)

\$\endgroup\$
3
  • \$\begingroup\$ On the output of test case print(test(5*[6*[0]])), shouldn't the first and last items have 6 zeroes? \$\endgroup\$
    – Jonah
    May 16, 2023 at 14:46
  • 1
    \$\begingroup\$ @Jonah Oh, that appears to be an issue with the test case, not with the solution itself (due to the list containing multiple references to the same sublist). I've replaced it with a fixed version. \$\endgroup\$ May 16, 2023 at 16:20
  • \$\begingroup\$ Got it, nice solution. \$\endgroup\$
    – Jonah
    May 16, 2023 at 16:26
7
\$\begingroup\$

Japt, 10 8 bytes

I/O as a 2D array of integers, with output sorted anti-clockwise starting in the bottom right corner.

4Æ=z)oÃf

Try it

4Æ=z)oÃf     :Implicit input of 2D array U
4Æ           :Map the range [0,4)
  =          :  Reassign to U
   z         :   Rotate U 90° clockwise
    )        :  End reassignment
     o       :  Pop last sub-array, mutating the array
      Ã      :End map
       f     :Filter, removing null elements
\$\endgroup\$
6
\$\begingroup\$

jq, 57 46 20 bytes

del(.[1:-1][][1:-1])

Online demo

\$\endgroup\$
1
  • 4
    \$\begingroup\$ Welcome to CGCC! Nice first answer, so +1 from me. :) Enjoy your stay and best of luck with other challenges. \$\endgroup\$ May 16, 2023 at 6:44
5
\$\begingroup\$

Wolfram Language (Mathematica), 47 45 bytes

-2 bytes thanks to @alephalpha

Try it online!

If[Tr[1^#]>2,{#[[t={1,-1}]],#[[2;;-2,t]]},#]&

Explanation

Tr[1^#] returns the length of the shortest dimension by computing the Trace of a matrix of 1's with the same dimensions as the input, i.e., Min@Dimensions@#. If we have an edge case with a dimension of 0, 1, or 2, we return the input in its original form.

If Tr[1^#]>2 is true, then we use Mathematica's [[a;;b, c;;d]] syntax to access different parts of the matrix: #[[{1,-1}]] returns the first and last rows, #[[2;;-2,{1,-1}]] returns the first and last columns while excluding the "corners." We return a ragged array as the output: if the input is mxn with m,n>2, the output will be of the format {(2 x n array), ((m-2) x 2 array)}

\$\endgroup\$
6
  • 1
    \$\begingroup\$ Welcome to Code Golf, and nice answer! \$\endgroup\$ May 16, 2023 at 0:19
  • 1
    \$\begingroup\$ @RydwolfPrograms Thanks--longtime listener, first time caller! \$\endgroup\$
    – erfink
    May 16, 2023 at 0:26
  • 2
    \$\begingroup\$ Welcome to Code Golf! According to code-golf's rule, answers should be either a full program or a function. If[Tr[1^m]>2,{m[[{1,-1}]],m[[2;;-2,{1,-1}]]},m] isn't a function by itself. You can write If[Tr[1^#]>2,{#[[{1,-1}]],#[[2;;-2,{1,-1}]]},#]&. \$\endgroup\$
    – alephalpha
    May 16, 2023 at 10:38
  • 2
    \$\begingroup\$ You can save some bytes by storing {1,-1} in a variable: If[Tr[1^#]>2,{#[[t={1,-1}]],#[[2;;-2,t]]},#]&. \$\endgroup\$
    – alephalpha
    May 16, 2023 at 10:38
  • 1
    \$\begingroup\$ @erfink Changing a global variable is allowed as long as your function is reusable. \$\endgroup\$
    – alephalpha
    May 16, 2023 at 23:54
4
\$\begingroup\$

Excel, 77 bytes

=TOCOL(B2#/(MAP(B2#,LAMBDA(b,SUM(SUBTOTAL(2,OFFSET(b,{-1;1},{-1,1})))))<4),2)

Input is spilled range deliberately set to have its top-leftmost cell as B2, rather than the customary A1, such that the four cells above, below, to the right and to the left of that cell exist.

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

Rust, 81 bytes

|a,b,c|(0..).zip(a).filter(move|(d,_)|d%b<1||d%b>b-2||d/b<1||d/b>c-2).map(|a|a.1)

Attempt This Online!

Takes input as a flattened matrix with size given as b and c

\$\endgroup\$
1
  • \$\begingroup\$ d%b%(b-1)<1||d/b%(c-1)<1 saves 6 bytes. \$\endgroup\$
    – Neil
    May 15, 2023 at 23:37
4
\$\begingroup\$

JavaScript (ES6), 54 bytes

m=>m.map((r,y)=>r.filter((_,x)=>x*y/r[x+1]?!m[y+1]:1))

Try it online!

Or 58 bytes (ES10) if we want to flatten the output:

m=>m.flatMap((r,y)=>r.filter((_,x)=>x*y/r[x+1]?!m[y+1]:1))

Try it online!

Commented

m =>                 // m[] = input matrix
m.map((r, y) =>      // for each row r[] in m[] at index y:
  r.filter((_, x) => //   for each value in r[] at index x:
    x * y            //     if neither x nor y is equal to 0 and there's
    / r[x + 1] ?     //     at least one more column after this one:
      !m[y + 1]      //       keep the value only if this is the last row
    :                //     else:
      1              //       keep the value
  )                  //   end of filter()
)                    // end of map()
\$\endgroup\$
2
  • 1
    \$\begingroup\$ The ouput format is pretty loose, so 54 bytes should be valid. \$\endgroup\$
    – Shaggy
    May 15, 2023 at 17:22
  • \$\begingroup\$ @Shaggy I guess you're right. The shortest version is now the main one. \$\endgroup\$
    – Arnauld
    May 15, 2023 at 18:04
4
\$\begingroup\$

J, 24 20 bytes

#~&,_>_(<,~<<0 _1)}]

Try it online!

  • _(<,~<<0 _1)}] Convert all interior points to infinity, by specifying "not first and last (0 and -1)" elements of first axis, and "not first and last" elements of second axis.
  • _> Where is that matrix less than infinity? Returns a 1-0 mask selecting the border
  • #~&, Flatten and filter the input with that mask

diagonal rotation, 24 bytes

#~&,_=[:+/,.~@1 _1|.!._]

Try it online!

Looks like this may be related to Adam's APL answer, but was discovered independently. See alternate below for a different approach.

  1. Diagonally rotate the matrix up-left using infinity as fill.
  2. Same thing down-right
  3. Add the two together (now the borders will all have the value infinity)
  4. Create a border-selecting 1-0 mask with 1 wherever infinity is
  5. Use that mask to filter the input, flattening both the mask and the input

original, 24 bytes

#~&,0=<:@$*/@:|"1$#:i.@$

Try it online!

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

Vyxal, 62 bitsv1, 7.75 bytes

4(∩ḣṘ)_W'

Try it Online!

Port of pyth messed up by the fact that and return 0 on an empty list instead of erroring (which has its use, but is annoying here)

Explained

4(∩ḣṘ)_W'
4(   )    # 4 times
  ∩       #   transpose top of stack
   ḣ      #   separate the head from the rest
    Ṙ     #   and reverse
      _W  # pop the remaining list and wrap the stack in a list
        ' # remove 0s and empty lists
\$\endgroup\$
4
\$\begingroup\$

R, 44 34 bytes

\(m)m[rev(d<-row(m)<2|col(m)<2)|d]

Attempt This Online!

Footer stolen from pajonk's answer, and -10 thanks to them!

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Actually, with this approach you don't really need to rotate the matrix - simple rev is sufficient, because |d does the conversion back to matrix for you. ATO! \$\endgroup\$
    – pajonk
    May 16, 2023 at 4:46
4
\$\begingroup\$

Jelly, 8 bytes

With the lax output requirements I was expecting to find terser in Jelly!

ŒDị@Ḋ¡€.

A monadic Link that accepts the rectangular matrix and yields a list of lists of border values.

Try it online! Or see the test-suite.

How?

ŒDị@Ḋ¡€. - Link: list of lists, M
ŒD       - all NW-SE diagonals of M
       . - set the right argument to 0.5
      €  - for each diagonal:
     ¡   -   repeat...
    Ḋ    -   ...number of times: dequeue (i.e. once if diagonal length > 1)
   @     -   ...action: with swapped arguments - f(0.5, diagonal)
  ị      -                index into*
             
             * fractional indices give the two neighbours in the 
               case of 0.5 that's 0 (the last) and 1 (the first)
\$\endgroup\$
4
\$\begingroup\$

SAS IML, 88 70

Input is a rectangular matrix M e.g.,

M1={
 1  2  3  4  5  6,
 7  8  9 10 11 12,
13 14 15 16 17 18,
19 20 21 22 23 24,
25 26 27 28 29 30,
31 32 33 34 35 36,
37 38 39 40 41 42,
43 44 45 46 47 48,
49 50 51 52 53 54,
55 56 57 58 59 60
};

[EDIT:] According to comments below I updated the answer.

The code (an IML module):

start d(M);if 2<nrow(M)><ncol(M) then M[2:nrow(M)-1,2:ncol(M)-1]=.;M=M[loc(M>.)];finish;

Human readable:

start d(M);
  if 2 < nrow(M)><ncol(M) then 
    M[ 2:nrow(M)-1, 2:ncol(M)-1 ] = .; 
  M = M[ loc(M > .) ];
finish;

To execute the IML module d run the following:

call d(M);

The process:

  1. For a Matrix M with at least 3 rows and columns (2 < nrow(M)><ncol(M), where nrow(M) is number of rows, ncol(M) is number of columns, and >< is operator of minimum of two numbers) replace "everytnig inside" (2:nrow(M)-1, 2:ncol(M)-1) with missing data (.),
  2. select those elements of M where value is not missing.

To print out result, i.e. elements extracted from M, run the following line:

if type(M)="U" then print "M is Empty";
else print M;

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Nice answer, +1 from me. Never heard of this language before, but it's fitting you've solved a matrix-based challenge with a programming language that has an abbreviation IML, which stands for Interactive Matrix Language apparently. :) \$\endgroup\$ May 16, 2023 at 6:40
  • \$\begingroup\$ Yes, IML is SAS dedicated matrix language, in fact it is one of many SAS data processing "dialects". There is great blog series "the DO loop" by Rick Wicklin explaining and presenting various IML use-cases: blogs.sas.com/content/iml Worth reading. \$\endgroup\$ May 16, 2023 at 7:39
  • \$\begingroup\$ Your submission must get input either via function parameter, STDIN, or command-line argument; you may not assume that a variable is defined. \$\endgroup\$
    – noodle man
    May 23, 2023 at 21:01
  • \$\begingroup\$ Updated. The keywords start and finish plus name d(M) extended length by 18 bytes, so ~25.7%. \$\endgroup\$ May 23, 2023 at 21:18
4
\$\begingroup\$

Jelly, 7 bytes

ŒDµżṪḢ)

Try it online!

No shot I could have thought of using diagonals if @Jonathan Allan hadn't first, so go upvote his solution! (Also, two bonus 8-byters: ŒDµṙ-ḣ2), ŒDµḊṖœ^))

ŒDµ   )    For each diagonal of the input:
    Ṫ      take and remove the last element,
   ż       zip with the remaining elements,
     Ḣ     and take the first of those pairs.

My original solutions, to laugh at:

Jelly, 9 bytes

ḊṖ$⁺€Fœ^F

Try it online!

ḊṖ           Remove the first and last rows
  $⁺€        and columns.
     F       Flatten the result,
        F    flatten the input,
      œ^     symmetric multiset difference.

Can't decide if this feels worse...:

Jelly, 9 bytes

ḊUZƊ3СZḢ

Try it online!

   Ɗ3С      Repeat [0, 1, 2, 3] times:
Ḋ            Remove the first row,
 U           reverse each row,
  Z          transpose.
       Z     Transpose the results
        Ḣ    and return the first row of that.
\$\endgroup\$
3
\$\begingroup\$

Pyth, 7 bytes

V4.)=_C

Try it online!

Prints each side as lists separated by newlines. Goes clockwise starting with the left side.

Explanation

V4.)=_CQ    # implicitly add Q
            # implicitly assign Q = eval(input())
V4          # loop 4 times
    =  Q    #   assign Q to
      CQ    #   Q transposed
     _      #   and reversed
  .)        #   pop the last element of Q and print it
\$\endgroup\$
3
\$\begingroup\$

Python 2, 48 bytes

f=lambda a:a[:1]+a[1:][-1:]+map(f,a[1:-(a>[f])])

Attempt This Online!

Takes a LOL and returns a ragged LOL. Output for non degenerate inputs is [top row,bottom row,[1st,last]of 2nd row,[1st,last]of 3rd row,...

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

APL (Dyalog Unicode), 23 22 18 bytes

Thanks to ovs for −4 bytes.

Anonymous prefix lambda.

{⍵[⍸∨∘⌽∘⊖⍨1∊¨⍳⍴⍵]}

Try it online!

{} "dfn"; argument is :

⍵[] select from the argument:

   where indicated by

  ∨∘⌽∘⊖⍨ the following mask when ORed with its mirrored flipped version…

  1∊¨ true only where 1 is a member of

   the indices for an array that has the shape

  ⍴⍵ the shape of the argument

\$\endgroup\$
6
  • 1
    \$\begingroup\$ I think {⍵[⍸∨∘⌽∘⊖⍨1∊¨⍳⍴⍵]} works for 18 \$\endgroup\$
    – ovs
    May 15, 2023 at 22:49
  • \$\begingroup\$ Which file encoding to use for that??? \$\endgroup\$
    – Aconcagua
    May 17, 2023 at 9:55
  • \$\begingroup\$ @Aconcagua See codegolf.meta.stackexchange.com/q/9428/43319 \$\endgroup\$
    – Adám
    May 17, 2023 at 10:22
  • \$\begingroup\$ @Aconcagua No, this is Dyalog APL which can use the SBCS library. \$\endgroup\$
    – Adám
    May 17, 2023 at 10:31
  • \$\begingroup\$ Ah, I see, there's an explicit 8-bit code page for – utf-8 would have had 14 additional bytes ;) \$\endgroup\$
    – Aconcagua
    May 17, 2023 at 10:31
3
\$\begingroup\$

Haskell, 48 bytes

b=zipWith($)[head,map last,last,map head].repeat

Try it online!

Haskell, 74 bytes

import Data.List;(o:t)#(r:s)=r++t#(reverse$transpose$s);_#_=[];b=("1234"#)

Try it online!

At each of the four or less iterations, # peels the top row r from the matrix, and rotates it i-wise by transpose sending corner 2 to 3 and reverse sending corner 2 from 3 to 1 before reiterating for one side o less than before.

\$\endgroup\$
5
  • \$\begingroup\$ I think yo have to add the wanted function ([1..4]#) to the byte count \$\endgroup\$
    – AZTECCO
    May 16, 2023 at 6:44
  • \$\begingroup\$ Thanks for catching that. \$\endgroup\$ May 16, 2023 at 14:30
  • \$\begingroup\$ Sorry for bothering you , although I like the solution, it seems to produce duplicates values, it's not specified if they are permitted, but I don't think they are \$\endgroup\$
    – AZTECCO
    May 16, 2023 at 15:04
  • \$\begingroup\$ indeed. my edit comment was: is this allowed? \$\endgroup\$ May 16, 2023 at 15:05
  • \$\begingroup\$ Oh! I see, maybe It's better to make it more visible like specifying it in the answer or asking the OP. Btw I asked OP for clarification. Let's see \$\endgroup\$
    – AZTECCO
    May 16, 2023 at 15:10
2
\$\begingroup\$

R, 52 bytes

\(m)m[row(m)%in%c(1,nrow(m))|col(m)%in%c(1,ncol(m))]

Attempt This Online!

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

Charcoal, 18 bytes

IΣEθΦι¬∧﹪κ⊖Lθ﹪μ⊖Lι

Attempt This Online! Link is to verbose version of code. Explanation: Uses a filter to keep elements in the first or last row or column i.e. those that are not both in an interior row and in an interior column.

   θ                Input array
  E                 Map over rows
     ι              Current row
    Φ               Filtered where
         κ          Current row
        ﹪           Modulo
            θ       Input array
           L        Length
          ⊖         Decremented
       ∧            Logical And
              μ     Current column
             ﹪      Modulo
                 ι  Current row
                L   Length
               ⊖    Decremented
      ¬             Logical Not
 Σ                  Flatten
I                   Cast to string
                    Implicitly print
\$\endgroup\$
2
\$\begingroup\$

julia, 59 bytes

Expects a Matrix{Any} and returns a Vector{Any} of the border entries.

Each call of this function pops the top row of the matrix, rotates the resulting matrix to the right and recurses. Terminates after 4 iterations or when the resulting matrix is empty.

f(M,c=4)=length(M)c>0 ? [M[1,:];f(M[end:-1:2,:]',c-1)] : []

Attempt This Online!

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

Scala, 98 bytes

Golfed version. Try it online!

(o,q)=>(o,q)match{case (Nil,_)|(_,Nil)=>Seq[Int]()case (_,r::s)=>r++f(o.tail,s.transpose.reverse)}

Ungolfed version. Try it online!

object Main extends App {
  def f(o: List[Int], rs: List[List[Int]]): List[Int] = (o, rs) match {
    case (Nil, _) | (_, Nil) => List[Int]()
    case (_, r :: s) => r ++ f(o.tail, s.transpose.reverse)
  }

  println(f((1 to 4).toList, List(List(1,2,3), List(4,5,6), List(7,8,9))))
  println(f((1 to 4).toList, List(List(9, 2,-5, 3), List(18, 3, 8, 0), List(2, 7,21,-3), List(9,-5,10,99))))
  println(f((1 to 4).toList, List(List(10, 20, 30, 40, 50, 60), List(70, 80, 90,-90,-80,-70), List(-60,-50,-40,-30,-20,-10))))
}

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

Vyxal 3 , 6 bytes

ḣṫ$ᵛÞh

Try it Online!

Abuses formatting quite a bit but there isn't a way to get diagonals yet.

ḣṫ$ᵛÞh­⁡​‎‎⁡⁠⁡‏⁠⁠‏​⁡⁠⁡‌⁢​‎‎⁡⁠⁢‏⁠‎⁡⁠⁣‏‏​⁡⁠⁡‌⁣​‎‎⁡⁠⁤‏⁠‎⁡⁠⁢⁡‏⁠‎⁡⁠⁢⁢‏‏​⁡⁠⁡‌­
ḣ       # ‎⁡head extract
 ṫ$     # ‎⁢tail extract and swap 
   ᵛÞh  # ‎⁣vectorized edges
💎

Created with the help of Luminespire.

\$\endgroup\$
1
  • \$\begingroup\$ uh i just realized i didn't program edges correctly for single element lists so i have to fix that but otherwise it works \$\endgroup\$
    – pacman256
    Feb 7 at 13:55
1
\$\begingroup\$

Retina, 23 bytes

(?<=¶.+)\b\S+\b(?=.+¶)

Try it online!

Expects a matrix as having columns separated by spaces and rows separated by newlines. Works by removing numbers that have a newline more than immediately before and after them. The boundaries are required to avoid deleting digits/signs elsewhere in the boundary numbers since there isn't a way to make lookahead/behind non-greedy/backtrack (at least in fewer than 4 bytes).

Seems shorter than listing all boundary numbers, for the same reason. I've found a slightly crazy version that is a byte shorter, but is also far more abusive of the lax I/O.

L`^.+|.+$|\w+¶|\G[^,]+
\$\endgroup\$
1
\$\begingroup\$

Ruby, 31 29 bytes

Very similar to SuperStormer’s Python 3 answer. Outputs by modifying the given array.

->a{a[r=1..-2].map{_1[r]=[]}}

Attempt This Online!

\$\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.