# Map the cheaters!

After all assignments are submitted, a dictionary is created that maps student number to the hash of their file.

This dictionary, or hashmap, or mapping (whatever your language calls it) will look as follows:

{100: "aabb", 104: "43a", 52: "00ab", 430: "aabb", 332: "43a"}

The key is the student number, and the value is the hash.

Our task is to pick out the cheaters! The cheaters are the ones which have identical hashes.

Given the input {100: "aabb", 104: "43a", 52: "00ab", 430: "aabb", 332: "43a"}, the function should return (or print) the following text:

100 has identical files to 430

104 has identical files to 332


Notice how the files where the hashes are unique are not mentioned.

Also, the order is important here:

{100: "aabb", 202: "aabb", 303: "ab", 404: "aabb"} should return (print) the following text:

100 has identical files to 202,404

It is incorrect to print any of the following:

202 has identical files to 100,404

100 has identical files to 404, 202

You should print it in terms of how it appears in the dictionary. In some languages, going through a dictionary is random, so in this special case, you are allowed to change the method of input such that you can go through it in an orderly fashion.

More Examples:

{} # prints nothing

{100: "ab", 303: "cd"} # prints nothing again

{100: "ab", 303: "cd", 404: "ab"}

100 has identical files to 404

{303: "abc", 304: "dd", 305: "abc", 405: "dd", 606: "abc"}

303 has identical files to 305,606

304 has identical files to 405


Shortest code wins!

• "You should print it in terms of how it appears in the dictionary" -- I'm not entirely sure what this means. Otherwise I do like the challenge. – Giuseppe Dec 6 at 22:52
• Might I also suggest using the sandbox before posting to the main site? always helpful to get revisions to a question before posting instead of getting a million comments for clarification on main :-) – Giuseppe Dec 6 at 22:55
• In the case where multiple groups of cheaters are found, is there a required order between groups? For example, in the last test case can "304 has..." be printed before "303 has..."? – Kamil Drakari Dec 7 at 1:46
• Are we allowed to output 303 has identical files to [305, 606] instead of 303 has identical files to 305,606? – Kevin Cruijssen Dec 7 at 9:35
• In languages where a dictionary, map, or hashmap type does not exist, are lists of tuples (or equivalent) allowed? – Rogem Dec 10 at 20:19

# JavaScript (Babel Node), 113 bytes

Takes input as an array of arrays in [key, value] format. Go go gadget double flatMap!

o=>o.flatMap(([x,h],i)=>(a=o.flatMap(([y,H],j)=>j>i&H==h?(o[j]=[,j],[y]):[]))+a?x+' has identical files to '+a:a)


Try it online!

# JavaScript (Babel Node), 114 bytes

Takes input as a native JS object.

o=>Object.keys(o).flatMap((x,i,a)=>(a=a.filter(y=>i--<0&o[y]==o[x]&&(o[y]=y)))+a?x+' has identical files to '+a:a)


Try it online!

• Really nice! Quite a large file but then again I did not expect this problem to be as easy as the other ones. Great work! I'll look more into this flatMap – K Split X Dec 7 at 0:22
• @KSplitX flatMap is not widely supported yet. I'm sure there are shorter ways, but it's getting late and I can't think anymore. :p – Arnauld Dec 7 at 0:35

# Python 2, 127 126 bytes

def f(x):
for l in{[K for K,V in x if v==V][1:-1]for k,v in x}:
if','in l:print l.replace(',',' has identical files to',1)


Try it online!

Takes a list of ordered pairs (<studentNumber>,<hash>) as input.

• Missed a small golf: if','in – Vedant Kandoi Dec 7 at 7:58
• @Vedant Kandoi: Thx! – Chas Brown Dec 7 at 21:14

# Retina 0.8.2, 71 bytes

+m((:.+)$(¶|.)+?)^(.+)\2$
,$4$1
:.*

G,
%1,
has identical files to


Try it online! Takes input on separate lines but link includes test suite that splits the examples for you. Explanation:

+


Repeat this match until no more replacements can be made.

m((:.+)$(¶|.)+?)^(.+)\2$
,$4$1


Look for pairs of matching hashes and append the key of the second match to that of the first match with a comma separator.

:.*


Delete all of the hashes.

G,


Keep only the lines with commas.

%1,
has identical files to


Replace the first comma on each line with the desired text (including trailing space).

# R, 145132129126 124 bytes

function(m,!=names)for(e in !(t=table(m))[t>1])cat(el(n<-!m[m==e]),'has identical files to',paste(n[-1],collapse=','),'
')


Try it online!

It takes a named vector as input (names are the keys)

• -2 bytes thanks to Giuseppe

If ", " separator (with a space after the comma) is allowed in case of multiple duplicates, we can use this code and save 10 bytes :

# R, 114 bytes

function(m,!=names)for(e in !(t=table(m))[t>1])cat(el(n<-!m[m==e]),'has identical files to',toString(n[-1]),'
')


Try it online!

• 124 bytes although my gut tells me a different approach might yield something in the 115 range... – Giuseppe Dec 7 at 21:22

# Japt, 36 bytes

üÌl>1 ®mgÃ®Î+ •s ÅÁÈól fÅC ‘ +ZÅq,


Try it online!

Right now it's a bit inconsistent in the ordering of lines, but within a line it outputs correctly. If the lines of the output need to be in a specific order, it will take a few more bytes. Input is just an array of [id, hash] pairs

Explanation:

üÌ                                      :Group by hash
l>1                                   :Remove the ones that are unique
®mgÃ                              :Get just the Ids
®                             :Generate a string for each hash:
Î                            : The first Id with that hash
+ •s ÅÁÈól fÅC ‘          : Plus " has identical files to " compressed
+ZÅ      : Plus the remaining Ids
q,    :  Comma delimited


# 05AB1E, 34 bytes

Σθ}.γθ}vyg1›iy€нć“ÿ€°Ê¼‡œ€„ “?',ý,


Explanation:

Σθ}                   # Sort the (implicit) input by the string
.γθ}                  # Then group it by the string
v                     # Loop y over each grouped inner list
yg1›i                #  If the group contains more than 1 key-value pairs:
y€н             #   Only leave the keys
ć               #   Pop and push the head and rest of the list separately
#   (with the head being at the top of the stack now)
“ÿ€°Ê¼‡œ€„ “   #   Compressed string "ÿ has identical files to "
#   where the "ÿ" is automatically replaced with the top of the stack
?  #   Print it (without trailing newline)
',ý           '#   Join the remaining numbers by a comma
,           #   And output it as well (with trailing newline)


See this 05AB1E answer of mine (section How to use the dictionary?) to understand why “ÿ€°Ê¼‡œ€„ “ is "ÿ has identical files to ".

# Ruby, 98 96 bytes

->h{h.group_by{|k,v|v}.map{|k,v|x,*y=v.to_h.keys;p"#{x} has identical files to #{y*?,}"if y[0]}}


Try it online!

Takes input as Ruby Hash, returns by printing.

# Perl 6, 115 110 bytes

-2 bytes thanks to Jo King

{unique map {.[0]~" has identical files to "~join ',',.skip},grep *>1,.map:{.grep(*.value eq\$^p.value)>>.key}}


Try it online!

Takes a list of Pairs since hashes are unordered. A list of two-element lists would save a few bytes but seems unidiomatic. Returns a list of lines.

95 bytes if the order of lines in the result doesn't matter:

*.classify(*.value).values>>.key.grep(*>1).map:{.[0]~" has identical files to "~join ',',.skip}


Try it online!

• .[1..*] to .skip – Jo King Dec 8 at 1:19

# Jelly, 39 bytes

Ṫ€ĠḊƇṢịẎµḢ;“]⁶ḅhṬyU⁹æṇL[Oþ⁹cƘƓ»,ṾK)j⁾¶¶


Try it online!