# Spot all (anti)diagonals with duplicated values

## Challenge:

Given a matrix input, determine the amount of diagonals and anti-diagonals with duplicated numbers.
So if we have a matrix like this:

[[aa,ab,ac,ad,ae,af],
[ba,bb,bc,bd,be,bf],
[ca,cb,cc,cd,ce,cf],
[da,db,dc,dd,de,df]]


All diagonals and anti-diagonals would be:

[[aa],[ab,ba],[ac,bb,ca],[ad,bc,cb,da],[ae,bd,cc,db],[af,be,cd,dc],[bf,ce,dd],[cf,de],[df],


Example:

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


All diagonals and anti-diagonals would be:

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


Removing all diagonals and anti-diagonals only containing unique numbers:

[[2,3,5,2],[1,4,4,1],[2,5,3,2],[1,4,2,1],[2,3,3,2],[1,2,4,1]]


So the output is the amount of diagonals and anti-diagonals containing duplicated numbers:

6


## Challenge rules:

• If the input matrix is empty, contains only 1 number, or contains only unique numbers across the entire matrix, the output is always 0.
• Input is guaranteed to only contain positive digits [1,9] (unless it's completely empty).
• The matrix will always be rectangular (i.e. all the rows are the same length).
• I/O is flexible. Input can be taken as a list of lists of integers, or 2D array of integers, or a Matrix-object, as a string, etc. etc. You are also allowed to take one or both of the dimensions of the matrix as additional input if it would save bytes in your language of choice.

## General rules:

• This is , so 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.

## Test cases:

Input:                     Output:

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

[[]]                       0

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

[[1,1],                    2
[1,1]]

[[9,9,9],                  6
[9,9,9],
[9,9,9]]

[[7,7,7,7],                8
[7,7,7,7],
[7,7,7,7]]

[[1,1,1],                  1
[2,3,4],
[2,5,1]]

[[1,8,4,2,9,4,4,4],        12
[5,1,2,7,7,4,2,3],
[1,4,5,2,4,2,3,8],
[8,5,4,2,3,4,1,5]]

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


# Husk, 9 bytes

#S≠uṁ∂Se↔


Try it online!

## Explanation

#S≠uṁ∂Se↔
S   hook: S f g x = f x (g x)
e  pair the matrix
↔ with its reverse
ṁ∂    get the antidiagonals of each, in a single list
#         count all diagonals which satisfy the following:
S        hook: S f g x = f x (g x)
≠       diagonal does not equal
u      itself uniquified?

• Would you mind adding an explanation? Let me know when you've done so, then I will move the accepted mark to your answer, since it's shorter than the current Jelly answer. :) – Kevin Cruijssen Oct 9 at 17:34
• @KevinCruijssen added it in. – Razetime Oct 10 at 5:07

# R, 928682 78 bytes

function(m,x=row(m),y=col(m),|=split,^=Map)sum(max^table^c(m|x-y,m|x+y)>1)


Try it online!

### Explanation

First, we declare additional variables $$\x\$$ and $$\y\$$ that stand for row and column indices, respectively. Then we can delineate the diagonals and anti-diagonals by taking their difference and sum. E.g., for a 4x4 matrix:

$$\x-y\$$ gives:

0 -1 -2 -3 1 0 -1 -2 2 1 0 -1 3 2 1 0

$$\x+y\$$ gives:

2 3 4 5 3 4 5 6 4 5 6 7 5 6 7 8

Now split(m, x-y) and split(m, x+y) produce the actual lists of diagonals and anti-diagonals, which we join together.

Finally, we count the entries of the resulting list where duplicates are present.

Thanks for bytes saved:

-4 by CriminallyVulgar
-4 by digEmAll

• Guess I can add row and col to my list of 'extremely situational functions'. Really clever solution. – CriminallyVulgar Jan 25 '19 at 14:03
• I think you can move the c(m|x-y,m|x+y) straight into the sapply call, remove the l= part. I don't see any failed tests. Try it online! – CriminallyVulgar Jan 25 '19 at 14:11
• Yep, that's correct, I just missed that after my first golf, there was only a single l instance remaining. – Kirill L. Jan 25 '19 at 14:17
• They must have added the row and column functions to R this morning, because I've never heard of them. – ngm Jan 25 '19 at 15:05

# J, 21 20 bytes

-1 byte thanks to Jonah!

1#.|.,&((~:&#~.)/.)]


Try it online!

## Explanation:

1#.                   find the sum of the
,                concatenation of
(          )   the result of the verb in the parentheses applied to
]  the input
&               and
|.                 the reversed input
(      )/.    for each diagonal
~:&#~.       check if all elements are unique and negate the result

• it's kind of crazy that you can't do better than (-.@-:~.) for "the unique items don't match" in J but i've encountered this many times too and i don't think you can... we have = and ~:, on one hand, and -: and <this is missing>. – Jonah Jan 25 '19 at 18:23
• Actually, managed to shave 1 more byte off: 1#.|.,&((~:&#~.)/.)]. Try it online! – Jonah Jan 25 '19 at 23:40
• @Jonah: cool use of &, thanks! – Galen Ivanov Jan 26 '19 at 7:46

# Japt, 31 bytes

ËcUî
ËéEÃÕc¡XéYnÃÕ mf fÊk_eZâÃl


Try all test cases

Explanation:

Ëc                            #Pad each row...
Uî                          #With a number of 0s equal to the number of rows

ËéEÃÕ                         #Get the anti-diagonals:
ËéEÃ                          # Rotate each row right a number of times equal to the row's index
Õ                         # Get the resulting columns
¡XéYnÃÕ                 #The diagonals:
¡XéYnÃ                  # Rotate each row left a number of times equal to the row's index
Õ                 # Get the resulting columns
mf              #Remove the 0s from each diagonal
fÊ           #Remove the all-0 diagonals
k_   Ã     #Remove the ones where:
eZâ      # The list contains no duplicates
l    #Return the number of remaining diagonals


I also tried a version based on Kirill L.'s Haskell answer, but couldn't find a good way to "group by a function of the X and Y indices" and the alternative I found wasn't good enough.

• 31 bytes – Shaggy Jan 26 '19 at 21:38

# Jelly, 10 bytes

ŒD;ŒdQƑÐḟL


Alternatives:

ŒD;ŒdQƑ€¬S
ŒD;ŒdQƑ€ċ0


### How it works?

ŒD;ŒdQƑÐḟL – Monadic link / Full program.
;        – Join:
ŒD           – The diagonals
with
Œd        – The anti-diagonals.
Ðḟ  – Discard the lists that are not:
QƑ      – Invariant under deduplication.
L – Length (count them).


# JavaScript (ES6),  107 105 101  98 bytes

f=(m,d=s=1)=>(m+0)[s-=~d/2]?m.some(o=(r,y)=>!r.every((v,x)=>x+d*y+m.length-s?1:o[v]^=1))+f(m,-d):0


Try it online!

### Note

The way this code is golfed, the anti-diagonal consisting of the sole bottom-left cell is never tested. That's OK because it can't possibly contain duplicated values.

### Commented

f = (                    // f = recursive function taking:
m,                     //   m[] = input matrix
d =                    //   d   = direction (1 for anti-diagonal or -1 for diagonal)
s = 1                  //   s   = expected diagonal ID, which is defined as either the sum
) =>                     //         or the difference of x and y + the length of a row
(m + 0)[               //
s -= ~d / 2          // increment s if d = -1 or leave it unchanged otherwise
] ?                    // if s is less than twice the total number of cells:
m.some(o =           //   o = object used to store encountered values in this diagonal
(r, y) =>            //   for each row r[] at position y in m[]:
!r.every((v, x) => //     for each cell of value v at position x in r[]:
x + d * y +      //       x + d * y + m.length is the ID of the diagonal
m.length - s ?   //       if it's not equal to the one we're looking for:
1              //         yield 1
:                //       else:
o[v] ^= 1      //         toggle o[v]; if it's equal to 0, v is a duplicate and
//         every() fails which -- in turn -- makes some() succeed
)                  //     end of every()
)                    //   end of some()
+ f(m, -d)           //   add the result of a recursive call in the opposite direction
:                      // else:
0                    //   stop recursion


# 05AB1E, 25 bytes

í‚εεygÅ0«NFÁ]€ø«ʒ0KDÙÊ}g


Try it online! or as a Test Suite

Explanation

í                          # reverse each row in input
‚                         # and pair with the input
ε                        # for each matrix
ε                       # for each row in the matrix
ygÅ0«                  # append len(row) zeroes
NFÁ               # and rotate it index(row) elements to the right
]              # end loops
€ø            # transpose each matrix
«          # append them together
ʒ     }   # filter, keep only rows that
0K       # when zeroes are removed
DÙÊ    # are not equal to themselves without duplicate values
g  # push length of the result


I feel like I've missed something here.
Need to try and golf this more later.

• Doesn't help at all, but rotate N left would be N._ now. So í‚εεygÅ0«N._] also works. Can also remove the flatten with this new change... still no byte savings though: í‚vyεygÅ0«N._}ø}«ʒ0KDÙÊ}g – Magic Octopus Urn Jan 29 '19 at 19:45
• @MagicOctopusUrn: Interesting. I had missed that command. Only a left though. Thats weird. – Emigna Jan 29 '19 at 21:24
• @Emigna You can go right with N(._ I guess, but your NFÁ} is the same length, and even shorter in this case due to ] closing the loop and maps simultaneously. Overall the use of ._ is only useful when going left to save 1 byte, in comparison to NFÀ}. – Kevin Cruijssen Jan 31 '19 at 8:26
• @KevinCruijssen: Ah, cool. Although as you say, not very useful. – Emigna Jan 31 '19 at 8:59

# Python 2, 144 136 bytes

lambda m:sum(l(set(d))<l(d)for d in[[r[i*x+o]for i,r in enumerate(m)if-1<i*x+o<l(r)]for o in range(-l(m),l(m))for x in[-1,1]])
l=len


Try it online!

# Octave, 98 bytes

@(A)nnz([(q=@(Q)arrayfun(@(n)nnz(z=diag(Q,n))-nnz(unique(z)),-([m,n]=size(Q)):n))(A),q(rot90(A))])


Try it online!

• Are arrays indeed fun? ;p – Kevin Cruijssen Jan 25 '19 at 14:28
• And thanks for preparing the test cases in Octave format! – Luis Mendo Jan 26 '19 at 1:05
• @KevinCruijssen Not just arrays! You can have cellfun too, and for the masochistic, structfun as well. In Octave, it's either a for-loop or having fun! – Sanchises Jan 26 '19 at 7:06
• And don’t forget b-sx-fun! – Luis Mendo Jan 26 '19 at 10:24

import Data.List
r#(a:b)=sum[1|(/=)=<<nub$[h|h:_<-a:r]]+[t|_:t<-a:r]#b []#_=0 a#_=a#[[]] h x=[]#x+[]#(reverse x)  Try it online! r#(a:b) -- function '#' calculates the ant-diagonals of a matrix -- where 'a' is the first row and 'b' all the others -- as we recursively walk down the rows of the matrix, -- 'r' holds the rows from before with the respective -- head dropped -- [h|h:_<-a:r] -- if the heads of the the current row and the rows -- before (/=)=<<nub$           -- contain duplicates
[1|                ]     -- make a singleton list [1] (else the empty list)
sum                         -- and take the sum thereof
#               -- a recursive call with
[t|_:t<-a:r]                -- the tails of the current row and the rows before
b              -- and the rows below
--
[]#_=0                       -- base case if there aren't any tails anymore, return 0
a#_=a#[[]]                   -- if there are tails, but no further rows below,
-- continue with tails

h x=[]#x+[]#(reverse x)      -- main function, call '#' with input matrix 'x'
-- and the reverse of it to get the number of diagonals
-- and anti-diagonals. Recursion starts with no
-- rows before the 1st row.

-- example trace of function '#'
-- input matrix:
--   [[1,2,3,4],
--    [5,6,7,8],
--    [9,9,9,9]]
--
--  | r         a          b              a:r          heads   tails (r of next call)
-- -+----------------------------------------------------------------------------------
-- 1| []        [1,2,3,4]  [[5,6,7,8],    [[1,2,3,4]]  [1]     [[2,3,4]]
--  |                       [9,9,9,9]]
--  |
-- 2| [[2,3,4]]  [5,6,7,8]  [[9,9,9,9]]   [[5,6,7,8],  [5,2]   [[6,7,8],
--  |                                      [2,3,4  ]]           [3,4  ]]
--  |
-- 3| [[6,7,8],  [9,9,9,9]  []            [[9,9,9,9],  [9,6,3] [[9,9,9],
--  |  [3,4  ]]                            [6,7,8  ],           [7,8  ]
--  |                                      [3,4    ],           [4    ]
--  |
--  | ....


# Charcoal, 6156 53 bytes

Ｆ²ＦＬθＦＬ§θ⁰Ｆ⟦⁻κ×⊖⊗ιλ⟧⊞υ⊞Ｏ⎇∧λ﹪⁺μιＬθ⊟υ⟦⟧§§θμλＩＬΦυ⊙ι‹⌕ιλμ


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

Ｆ²


Loop over forward and reverse diagonals; i=0 represents forward diagonals while i=1 represents reverse diagonals.

ＦＬθ


Loop over each row index. This represents the index of the start of the diagonal.

ＦＬ§θ⁰«


Loop over each column index.

Ｆ⟦⁻κ×⊖⊗ιλ⟧


Calculate the row index of the diagonal at this column index. I use a for loop over a single-element array instead of an assignment as this avoids having to wrap the assignment into a block with the following statement, thus saving a byte.

⎇∧λ﹪⁺μιＬθ


Check whether this is the first column or the diagonal is about to wrap around between bottom and top.

⊟υ


If it isn't then pop the last list from the list of lists.

⟦⟧


if it is then start a new empty list.

⊞Ｏ...§§θμλ


Add the current diagonal entry to that list.

⊞υ


And push that list (back) to the list of lists.

ＩＬΦυ⊙ι‹⌕ιλμ


Count the number of lists that contain duplicates.

Let's take an example when i=0 and k=1. This means that we've already collected two diagonals, [[1,1,5,2],[9,4,3,5]]. Here's our input:

 1 8 4 2 9 4 4 4
[5]1 2 7 7 4 2 3
1 4 5 2 4 2 3 8
8 5 4 2 3 4 1 5


We then loop l from 0 to 7. This advances both the row and column by 1 each time:

 1 8 4 2 9 4 4 4
[5]1 2 7 7 4 2 3
1[4]5 2 4 2 3 8
8 5[4]2 3 4 1 5


The list is now [[1,1,5,2],[9,4,3,5],[5,4,4]]. However when l is 3, we have k+l=4, a multiple of the height of the array. This means that we need to start a new list: [[1,1,5,2],[9,4,3,5],[5,4,4],[]]. We then continue to collect diagonal elements:

 1 8 4[2]9 4 4 4
[5]1 2 7[7]4 2 3
1[4]5 2 4[2]3 8
8 5[4]2 3 4[1]5


The list is now [[1,1,5,2],[9,4,3,5],[5,4,4],[2,7,2,1]]. Now when l is 7, we have k+l=8, another multiple of the height of the array. This means that we need to start a new list, which ends up with the last element of that diagonal: [[1,1,5,2],[9,4,3,5],[5,4,4],[2,7,2,1],[4]].

 1 8 4[2]9 4 4[4]
[5]1 2 7[7]4 2 3
1[4]5 2 4[2]3 8
8 5[4]2 3 4[1]5


By collecting wrapping diagonals starting at the first element of each row we eventually accumulate all of the diagonals of the array.

# Wolfram Language (Mathematica), 99 98 96 94 83 bytes

Count[DuplicateFreeQ@Diagonal[#,i]~Table~{i,-t,t=#~Total~2}&/@{#,Reverse@#},1<0,2]&


Try it online!

• Function[a,a~Diagonal~#&/@Range[t=-#~Total~2,-t]] gets all diagonals of a-- which works because #~Total~2 is larger than any dimension of a.

# APL+WIN, 69 bytes

Prompts for a 2d matrix of the form 4 6⍴1 2 1 2 1 2 1 2 3 4 5 6 6 5 4 3 2 1 2 1 2 1 2 1

This yields:

1 2 1 2 1 2
1 2 3 4 5 6
6 5 4 3 2 1
2 1 2 1 2 1

+/~(v⍳¨v)≡¨⍳¨⍴¨v←(v←⊂[1](⌽0,⍳1↓n)⌽(n⍴0),m,((n←0 ¯1+↑⍴m)⍴0),⌽m←⎕)~¨0


Try it online! Courtesy of Dyalog Classic

Explanation:

(⌽0,⍳1↓n)⌽(n⍴0),m pad m with zeros to isolate diagonals

((n←0 ¯1+↑⍴m)⍴0),⌽m pad rotated m with zeros to isolate anti-diagonals


Yields:

1 2 1 2 1 2 0 0 0 2 1 2 1 2 1 0 0 0
0 1 2 3 4 5 6 0 0 0 6 5 4 3 2 1 0 0
0 0 6 5 4 3 2 1 0 0 0 1 2 3 4 5 6 0
0 0 0 2 1 2 1 2 1 0 0 0 1 2 1 2 1 2

v←(v←⊂[1](.....)~¨0 enclose the diagonals as a nested vector with padded zeros removed

+/~(v⍳¨v)≡¨⍳¨⍴¨v identify diagnols with duplicate entries and sum


# Perl 5, 89 82 bytes

map{$i=0;map{$a[$x+$i].=$_;$b[@F-$x+$i++].=$_}/\d/g;$x++}@F;\$_=grep/(.).*\1/,@a,@b


TIO

# TSQL, 140 128 bytes

Found a way to golf 12 characters. This is no longer the longest solution.

Golfed:

SELECT sum(iif(y+x=j+i,1,0)+iif(y-x=j-i,1,0))FROM
@,(SELECT x i,y j,max(y)over()m,v w
FROM @)d WHERE(x*y=0or m=y)and v=w and x<i


Ungolfed:

DECLARE @ table(v int,x int,y int)
-- v = value
-- x = row
-- y = column
INSERT @ values
(1,0,0),(2,0,1),(1,0,2),(2,0,3),(1,0,4),(2,0,5),
(1,1,0),(2,1,1),(3,1,2),(4,1,3),(5,1,4),(6,1,5),
(6,2,0),(5,2,1),(4,2,2),(3,2,3),(2,2,4),(1,2,5),
(2,3,0),(1,3,1),(2,3,2),(1,3,3),(2,3,4),(1,3,5)

SELECT sum(iif(y+x=j+i,1,0)+iif(y-x=j-i,1,0))
FROM @,(SELECT x i,y j,max(y)over()m,v w FROM @)d
WHERE
(x*y=0or m=y)
and v=w
and x<i


Try it out