26
\$\begingroup\$

Your challenge is to, given a matrix of nonnegative integers, remove all rows and columns that contain a 0.

For example, with this matrix:

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

Columns 3 and 5, along with rows 2 and 6 (in bold), contain a 0, so they should be removed, leaving:

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

You may assume the input and the output are nonempty. This is , shortest wins!

Testcases

[[1, 2, 3], [4, 5, 6]] -> [[1, 2, 3], [4, 5, 6]]
[[1, 2, 3], [4, 5, 0]] -> [[1, 2]]
[[3, 6, 19], [4, 0, 18], [2, 19, 3]] -> [[3, 19], [2, 3]]
[[5, 3, 2, 4, 1], [3, 2, 0, 4, 7], [7, 1, 9, 8, 2], [3, 2, 1, 5, 7], [6, 4, 6, 1, 2], [9, 3, 2, 4, 0]] -> [[5, 3, 4], [7, 1, 8], [3, 2, 5], [6, 4, 1]]
[[4, 9, 4, 1], [2, 0, 6, 0], [3, 4, 1, 2], [9, 7, 8, 5], [3, 5, 2, 0]] -> [[4, 4], [3, 1], [9, 8]]
\$\endgroup\$
2
  • 3
    \$\begingroup\$ You should probably add a test case where there are multiple zeros in a row and/or column. \$\endgroup\$ Feb 10 at 6:30
  • 1
    \$\begingroup\$ Related (harder) \$\endgroup\$
    – Arnauld
    Mar 7 at 19:37

23 Answers 23

8
\$\begingroup\$

R, 35 bytes

\(x,`?`=\(d)apply(x,d,all))x[?1,?2]

Attempt This Online!


Alternative solution if the input contained NAs instead of 0s:

R, 34 bytes

\(x,`?`=complete.cases)x[?x,?t(x)]

Attempt This Online!

\$\endgroup\$
1
  • \$\begingroup\$ That's a neat trick with defining a function without needing parentheses \$\endgroup\$
    – qwr
    Feb 11 at 5:05
6
\$\begingroup\$

Python 3, 82 74 65 60 bytes

-13 bytes thanks to Mukundan314. -9 bytes thanks to att.

lambda m:[q for*q,r in zip(*filter(all,zip(*m)),m)if all(r)]

Try it online!

I wasn't going to post two answers but since nobody else is posting a Python answer that doesn't rely on NumPy... Port of my Haskell answer, but I can't really take any credit for the final result. It all goes to Mukundan314 and att. I'll try my best to explain this wizardry.

Explanation

Said wizardry really takes advantage of Python's splat operator *. What it does essentially is unpack an iterable (in this case a two-dimensional list) into separate arguments for a function call. Let's start with a simple matrix m, and I'll document the transformations that happen to it along the way.

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

First, zip(*m) transposes the list. Instead of passing m to the zip function as the list of rows it is, it's passed as separate arguments, effectively zipping the rows of the matrix together to create a list of the columns.

[(4, 2, 3, 3),
 (9, 0, 4, 5),
 (4, 6, 1, 2),
 (1, 0, 2, 0)]

Next, filter(all, ...) removes every column that has a zero in it by checking all of the column against Python's boolean logic: 0 is falsy and every other number is truthy. (Hey, that's half the challenge sorted!)

[(4, 2, 3, 3),
 (4, 6, 1, 2)]

Cool, that's the truthy columns. After that, zip(*..., m) is once again splatting the previous result and zipping it together with the original input. This basically transposes it back to the rows, except without the falsy columns, and then prepends it to the original rows.

[(4, 4, [4, 9, 4, 1]),
 (2, 6, [2, 0, 6, 0]),
 (3, 1, [3, 4, 1, 2]),
 (3, 2, [3, 5, 2, 0])]

Here comes an interesting bit. Remember what I said about the splat operator? Yeah, it does more than that. Kinda. for *q, r in ... effectively groups the rows-without-falsy-columns together, while keeping the original rows separate. The splat operator "collects" elements into q when iterating over something until another argument r is required.

[([4, 4], [4, 9, 4, 1]),
 ([2, 6], [2, 0, 6, 0]),
 ([3, 1], [3, 4, 1, 2]),
 ([3, 2], [3, 5, 2, 0])]

Now all that's left to do is get rid of the falsy rows and to do this, q ... if all(r) employs a similar trick to when the falsy columns were removed. While iterating over the previous result, if and only if r (or the original row) is truthy, keep q.

[[4, 4],
 [3, 1]]
\$\endgroup\$
6
  • \$\begingroup\$ The >0 in the all is not required right? \$\endgroup\$ Feb 11 at 4:31
  • 1
    \$\begingroup\$ 74 bytes by iterating the transpose with zip(*r) in the inner loop \$\endgroup\$ Feb 11 at 4:52
  • \$\begingroup\$ @Mukundan314 Ooh, I forgot you could do that for transposing. Thanks! \$\endgroup\$ Feb 11 at 4:58
  • 2
    \$\begingroup\$ 65 bytes \$\endgroup\$
    – att
    Feb 11 at 7:55
  • 3
    \$\begingroup\$ 60 bytes (from att's) by merging the outer zips \$\endgroup\$ Feb 11 at 12:45
5
\$\begingroup\$

Python + numpy, 32 bytes

lambda a:a[a.all(1)][:,a.all(0)]

Attempt This Online! -6 bytes thanks to att

\$\endgroup\$
2
  • \$\begingroup\$ Trying to figure out why I have to do a[a.prod(1)>0][:,a.prod(0)>0] instead of just a[a.prod(1)>0,a.prod(0)>0] \$\endgroup\$
    – Stef
    Feb 9 at 23:05
  • 2
    \$\begingroup\$ prod( )>0->all( ) \$\endgroup\$
    – att
    Feb 9 at 23:11
5
\$\begingroup\$

Jelly, 11 bytes

¬ŒṪZœP@ẎZɗƒ

A monadic Link that accepts the matrix and yields the reduced matrix.

Try it online!

How?

¬ŒṪZœP@ẎZɗƒ - Link: list of lists of integers, M
¬           - logical NOT (vectorises)
 ŒṪ         - truthy multidimensional indices
   Z        - transpose -> [zero row indices, zero column indices]
          ƒ - start with {M} and reduce {that} by:
         ɗ  -   last three links as a dyad - f(Current, Indices):
      @     -     with swapped arguments:
    œP      -       partition {Current} at {Indices}
       Ẏ    -     tighten
        Z   -     transpose
\$\endgroup\$
5
\$\begingroup\$

MATL, 10 bytes

t!XAyXA3$)

Try at MATL online! Or verify all test cases.

How it works

t      % Implicit input. Duplicate
!      % Transpose
XA     % Vertical-all: gives a logical vector containing true
       % for columns without zeros, or false otherwise
y      % Duplicate from below: pushes copy of input 
XA     % Vertical-all
3$)    % 3-input indexing: keeps the selected rows and columns
       % Implicit display
\$\endgroup\$
5
\$\begingroup\$

K (ngn/k), 18 16 15 bytes

-1 thanks to coltim

{x.&'1&/''+:\x}

Try it online!

     1&/''+:\x      row-,column-wise zero-free?
   &'               indices where true
 x.                 submatrix
\$\endgroup\$
1
  • 2
    \$\begingroup\$ I think a byte can be trimmed with {x.&'1&/''+:\x} \$\endgroup\$
    – coltim
    Feb 10 at 15:10
4
\$\begingroup\$

APL (Dyalog Extended), 15 bytes

((⍸0≠∧/)¨⊢⍮⍉)⌷⊢

Try it online!

APL (Dyalog APL), 19 18 17 16 bytes

-1 byte thanks to @att

⊢⌷⍨B∘⍉,B←⊂∘⍸0≠∧⌿

Attempt This Online!

⊢⌷⍨B∘⍉,B←⊂∘⍸0≠∧⌿­⁡​‎⁠⁠⁠⁠⁠⁠‎⁡⁠⁢⁤‏⁠‎⁡⁠⁣⁡‏⁠‎⁡⁠⁣⁢‏⁠‎⁡⁠⁣⁣‏⁠‎⁡⁠⁣⁤‏⁠‎⁡⁠⁤⁡‏⁠‎⁡⁠⁤⁢‏⁠‎⁡⁠⁤⁣‏⁠‎⁡⁠⁤⁤‏‏​⁡⁠⁡‌⁢​‎⁠⁠‎⁡⁠⁤⁣‏⁠‎⁡⁠⁤⁤‏‏​⁡⁠⁡‌⁣​‎⁠⁠‎⁡⁠⁤⁡‏⁠‎⁡⁠⁤⁢‏‏​⁡⁠⁡‌⁤​‎⁠‎⁡⁠⁣⁤‏‏​⁡⁠⁡‌⁢⁡​‎‎⁡⁠⁣⁢‏‏​⁡⁠⁡‌⁢⁢​‎‎⁡⁠⁤‏⁠‎⁡⁠⁢⁡‏⁠‎⁡⁠⁢⁢‏⁠⁠‏​⁡⁠⁡‌⁢⁣​‎‎⁡⁠⁢⁣‏⁠‏​⁡⁠⁡‌⁢⁤​‎‎⁡⁠⁢⁤‏⁠⁠⁠‏​⁡⁠⁡‌⁣⁡​‎‎⁡⁠⁡‏⁠‎⁡⁠⁢‏⁠‎⁡⁠⁣‏⁠⁠‎⁡⁠⁢⁡⁡‏‏​⁡⁠⁡‌­
       B←⊂∘⍸0≠∧⌿  ⍝ ‎⁡B = Monadic function to get indices of columns without zeros:
              ∧⌿  ⍝ ‎⁢  reduce each column on lcm
            0≠    ⍝ ‎⁣  check if not equal to 0 (vectorized)
           ⍸      ⍝ ‎⁤  get indices of true values
         ⊂        ⍝ ‎⁢⁡  enclose
   B∘⍉            ⍝ ‎⁢⁢Apply B on transpose (first result)
      ,           ⍝ ‎⁢⁣catenate with
       B          ⍝ ‎⁢⁤Apply B on argument (second result)
⊢⌷⍨               ⍝ ‎⁣⁡Keep only rows and columns in first and second result respectively
💎

Created with the help of Luminespire.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ 18 bytes \$\endgroup\$
    – att
    Feb 10 at 7:29
4
\$\begingroup\$

Matlab, 74 bytes(?) 52 bytes

-22 bytes thanks to Luis Mendo because my first attempt was too readable.

Try it online! Input for first test case would be F([1,2,3;4,5,6])

To be honest, I just wanted to try the challenge and am pleasantly surprised to how neatly it can be solved Matlab, so I just wanted to share it.

function[m]=F(m);[r,c]=find(~m);m(r,:)=[];m(:,c)=[];
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Welcome to the site! Nice to see more MATLAB programmers around here. You can remove indentation, linefeeds and end to save bytes. Also, you might want to use logical indexing instead of find (see my MATL answer), which probably results in shorter code \$\endgroup\$
    – Luis Mendo
    Feb 11 at 19:32
  • \$\begingroup\$ thanks for the advice! If I may ask, is it correct to take the byte-size by just looking at the properties of the .m-file? \$\endgroup\$
    – Xenox
    Feb 11 at 19:46
  • 1
    \$\begingroup\$ Yes, it’s just the number of bytes. Or better yet, use Try It Online to obtain the byte count and include an online demo (it uses Octave but your MATLAB code will work in Octave): tio.run/#octave \$\endgroup\$
    – Luis Mendo
    Feb 11 at 20:57
4
\$\begingroup\$

JavaScript (ES6), 62 bytes

-2 thanks to @l4m2

m=>(g=a=>a.filter((v,x)=>(+v?m:v).every(v=>v[x]|v)))(m).map(g)

Try it online!

Commented

m => (               // m[] = input matrix
  g = a =>           // g is a helper function taking an array a[]
  a.filter((v, x) => // for each value v at index x in a[]:
    (+v ? m          //   if v is atomic, we use m[] (2nd pass on columns)
            : v)     //   otherwise, we use v (1st pass on rows)
    .every(v =>      //   for each value v in the array chosen above:
      v[x]           //     test either v[x] (when removing columns)
      | v            //     or v (when removing rows)
    )                //   end of every()
  )                  // end of filter()
)(m)                 // invoke g on m[] to remove rows
.map(g)              // invoke g on each row to remove columns
\$\endgroup\$
2
3
\$\begingroup\$

Haskell, 81 63 62 59 bytes

-3 bytes thanks to Wheat Wizard.

n=notElem 0
f m=[[c|(x,c)<-zip[0..]r,n$map(!!x)m]|r<-m,n r]

Try it online!

I really want to use the fact that Haskell has product in Prelude but... Never mind, I did it. :P Never mind, the original approach allows for code shorter by a byte due to syntactical rearrangement.

Explanation (slightly outdated)

f m                            -- define function f that takes matrix m
  | n <- notElem =             -- alias notElem (element not in list) to p
    [[c                        --     store c
      | (x, c) <- zip [0..] r  --   for every pair of index x and value c in r...
      , n 0 $                  --   if 0 is not in the...
      map (!! x) m)            --   ...xth element of every row in m (column)
    | r <- m                   -- for every row r in m...
    , n 0 r]                   -- if 0 is not in r (truthy)
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Both times you use n you use it with 0, so you can save 2 bytes by doing n<-notElem 0. And since n doesn't depend on m you can save another byte by defining it on its own line. \$\endgroup\$
    – Wheat Wizard
    Feb 10 at 17:28
  • 1
    \$\begingroup\$ Also just for future reference, spaces are require before numbers but not after so n 0r works just as well as n 0 r, but that doesn't matter if you do the above. \$\endgroup\$
    – Wheat Wizard
    Feb 10 at 17:32
  • \$\begingroup\$ @WheatWizard I do that second one all the time in Python golfs, I dunno why I just assumed I can't in Haskell... Thanks! (Also wow, your name is just Wheat Wizard and not something unusual like Heeby Jeeby Man. :P) \$\endgroup\$ Feb 11 at 3:57
3
\$\begingroup\$

Google Sheets, 72 bytes

=let(a,A1:E6,_,lambda(x,min(x)),filter(filter(a,byrow(a,_)),bycol(a,_)))

Put the array in cells A1:E6 and the formula in cell G1.

filter rows and columns.png

In Google Sheets, a formula cannot return a completely empty 2D array. When all rows or columns contain a zero, the formula will error out.

\$\endgroup\$
2
  • \$\begingroup\$ Is there a way for Google Sheets to take a variable-size array, or for it to determine a range of cells based on some length input? \$\endgroup\$
    – emanresu A
    Feb 11 at 17:52
  • \$\begingroup\$ Sure, but that is not necessary with the formula in this answer — you can replace A1:E6 with an arbitrarily large range reference. When the array is smaller than the range, there will be blank cells at the bottom and right. The formula will ignore such blank cells. \$\endgroup\$ Feb 11 at 18:00
3
\$\begingroup\$

APL (Dyalog Extended), 11 bytes

(×∧/⍮∧⌿)⍛/⍨

Try it online!

 using the argument as both parameter (list of masks, one per dimension) and data

/ filter the data using the parameter

()⍛ first pre-processing the paramter as follows:

× signum of

∧/ row-wise LCM

 juxtaposed with

∧⌿ column-wise LCM

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

J, 15 bytes

*@*/#"1*@*/"1#]

Try it online!

  • *@*/"1 Boolean multiply across columns...
  • #] and use that to filter rows.
  • #"1 then filter each of those remaining rows...
  • *@*/ by the boolean filter obtained by multiplying all the rows together
\$\endgroup\$
2
\$\begingroup\$

Haskell + hgl, 38 bytes

f y=cr<<fn(e 0<(tx y!)<st)<eu<fn(e 0)y

Attempt This Online!

Alternate, 43 bytes

cr<<<g st<g cr<m(sQ<oti mn)
g=tx><fn<e 0<<m

Attempt This Online!

I kind of understand how it works but it's not easy to explain. First it tags each value with the minimum in its row, then it removes every column with a zero, then it removes every row with a zero tag.

  • m(sQ<oti mn) is the part that adds the tags.
  • g cr is the part that removes the columns (it also transposes the matrix)
  • g st is that part that removes the rows (it also transposes the matrix back)
  • cr removes the tags.

Reflection

  • There should be a non-infix version of (!).
  • There should be precomposed versions of e and ne.
  • oti should probably have a 2 byte name.
  • The precendence of @! makes it incompatible with composition. I should tweak this.
  • There is already an "index map" a couple of index filters would be nice.
\$\endgroup\$
2
\$\begingroup\$

Charcoal, 15 14 bytes

⭆¹EΦθΠλΦλ⬤θ§πξ

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

    θ          Input array
   Φ           Filtered where
      λ        Current row
     Π         Product (is truthy if the row contains no zeros)
  E            Map over remaining rows
        λ       Current row
       Φ        Filtered where
            π   Inner row
           §    Indexed by
             ξ  Current column
         ⬤      Are non-zero for each row of
          θ     Input array
⭆¹              Pretty-print the result
\$\endgroup\$
2
\$\begingroup\$

APL+WIN, 17 bytes

Prompts for input of matrix

(×/×m)⌿(×⌿×m)/m←⎕

Try it online! Thanks to Dyalog Classic

\$\endgroup\$
2
  • \$\begingroup\$ it seems 16 {(×/×⍵)⌿⍵/⍨×⌿×⍵} or (×/×m)⌿m/⍨×⌿×m←⎕ \$\endgroup\$
    – Rosario
    Feb 12 at 10:00
  • 2
    \$\begingroup\$ @Rosario. Thanks but my ancient APL does not have the functionality you have used to save the byte. \$\endgroup\$
    – Graham
    Feb 12 at 10:30
2
\$\begingroup\$

05AB1E, 10 bytes

øDĀPÏøIĀPÏ

Try it online or verify all test cases.

Explanation:

ø           # Zip/transpose the (implicit) input-matrix; swapping rows/columns
 D          # Duplicate this
  Ā         # Check for each value whether it's NOT 0 (0 if 0; 1 otherwise)
   P        # Take the product of each inner row
    Ï       # Only leave the rows of the transposed matrix at the truthy (==1) positions
     ø      # Zip/transpose the remaining rows back
      I     # Push the input-matrix again
       ĀPÏ  # Do the same
            # (after which the filtered matrix is output implicitly as result)
\$\endgroup\$
2
\$\begingroup\$

R, 32 bytes

\(x)x[!rowSums(!x),!colSums(!x)]

Attempt This Online!

Test suite stolen from pajonk's answer.

\$\endgroup\$
1
  • \$\begingroup\$ Nice idea, I wish I had thought of it! Also, -1 byte. \$\endgroup\$
    – pajonk
    Feb 13 at 20:48
2
\$\begingroup\$

Husk, 8 bytes

fm▼¹Tf▼T

Try it online!

Explanation

fm▼¹Tf▼T
       T  Transpose the input
     f▼   Filter out those rows (were columns) with a falsy minimum value
    T     Transpose back
f         Filter the rows based on
 m▼¹       the minimum values of the rows in the original input
\$\endgroup\$
1
\$\begingroup\$

Uiua SBCS, 15 14 bytes

⍉▽⊙⍉⊙▽∩/↧⟜⍉≠0.

Try it!

-1 thanks to the new ⟜ on modifier that dropped this morning.

Keeps two masks on the stack of which rows should be kept on each axis, and filters the input matrix based on these.

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

APL(NARS), 43 chars

{(c d)←1⍴⍨¨⍴⍵⋄c[↑¨k]←d[2∘⊃¨k←⍸0=⍵]←0⋄d/c⌿⍵}

test:

  arr←3 3⍴⍳10⋄arr1←6 5⍴5 3 2 4 1 3 2 0 4 7 7 1 9 8 2 3 2 1 5 7 6 4 6 1 2 9 3 2 4 0
  g←{(c d)←1⍴⍨¨⍴⍵⋄c[↑¨k]←d[2∘⊃¨k←⍸0=⍵]←0⋄d/c⌿⍵}
  g arr1
5 3 4
7 1 8
3 2 5
6 4 1
  g arr
1 2 3
4 5 6
7 8 9
\$\endgroup\$
1
\$\begingroup\$

Perl 5 -MList::Util=product, 88 bytes

sub{map{[@$_[grep{//;product(map{$$_[$']}@_)}0..$#$_]]}@_[grep{product@{$_[$_]}}0..$#_]}

Try it online!

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

Nekomata, 9 bytes

Z:Ť‼Ť$ᶻ¿‼

Attempt This Online!

Z:Ť‼Ť$ᶻ¿‼   Take [[3,6,19],[4,0,18],[2,19,3]] as an example
Z           Check if nonzero
                [[3,6,19],[4,0,18],[2,19,3]] -> [[3,6,19],[4,Fail,18],[2,19,3]]
 :          Duplicate
                [[3,6,19],[4,Fail,18],[2,19,3]] -> [[3,6,19],[4,Fail,18],[2,19,3]],[[3,6,19],[4,Fail,18],[2,19,3]]
  Ť         Transpose
                [[3,6,19],[4,Fail,18],[2,19,3]] -> [[3,4,2],[6,Fail,19],[19,18,3]]
   ‼        Remove rows that contain Fail
                [[3,4,2],[6,Fail,19],[19,18,3]] -> [[3,4,2],[19,18,3]]
    Ť       Transpose
                [[3,4,2],[19,18,3]] -> [[3,19],[4,18],[2,3]]
     $      Swap
                [[3,6,19],[4,Fail,18],[2,19,3]],[[3,19],[4,18],[2,3]] -> [[3,19],[2,3],[4,18]],[[3,6,19],[4,Fail,18],[2,19,3]]
      ᶻ     Zip with
       ¿        a, b -> if b contains Fail then a else Fail
                [[3,19],[2,3],[4,18]],[[3,6,19],[4,Fail,18],[2,19,3]] -> [[3,19],Fail,[4,18]]
        ‼   Remove rows that contain Fail
                [[3,19],Fail,[4,18]] -> [[3,19],[4,18]]
\$\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.