# Fridge magnet substitution

When writing a message with fridge magnets, you'll often find yourself substituting a 1 for an I. In this challenge, your goal is to find out if a message can be written using the letters of another message. The allowed substitutions are:

A = 4
B = 8
C = U
E = M = W = 3
G = 6 = 9
I = 1
L = 7
N = Z
O = 0
R = 2
S = 5


For example, the message CIRCA 333 can be rearranged to spell ICE CREAM, where the first two 3s are rotated 180 degrees to make two Es, and the last 3 is rotated 90 degrees counterclockwise to make an M. Whitespaces can be included in the messages, but they should not be accounted for in your solution, as they're made by placing the magnets on the fridge.

## Input

Two strings (or character arrays). All messages will match ^[A-Z0-9 ]+$ ## Output Truthy if the two input strings are valid rearrangements of each other, falsey otherwise. ## Examples ["CIRCA 333", "ICE CREAM"] => true ["DCLV 00133", "I LOVE CODE"] => true ["WE ARE EMISSARIES", "33 423 3315542135"] => true ["WE WANT ICE CREAM", "MET CIRCA 334 MEN"] => true ["I HAVE ICE CREAM", "HAVE 2 ICE CREAMS"] => false  ## More thruthy examples These are all the 15+ letter words that map to another word. Some are trivial substitutions, but I included all that I found. ["ANTHROPOMORPHISE","ANTHROPOMORPHISM"] ["ANTIPHILOSOPHIES","ANTIPHILOSOPHISM"] ["CIRCUMSTANTIALLY","ULTRAMASCULINITY"] ["DECENTRALIZATION","DENEUTRALIZATION"] ["DIMETHYLNITROSAMINE","THREEDIMENSIONALITY"] ["INSTITUTIONALISE","INSTITUTIONALISM"] ["INTERCRYSTALLINE","INTERCRYSTALLIZE"] ["INTERNATIONALISE","INTERNATIONALISM"] ["OVERCENTRALIZATION","OVERNEUTRALIZATION"] ["OVERCENTRALIZING","OVERNEUTRALIZING"] ["PREMILLENNIALISE","PREMILLENNIALISM"] ["TRANSCENDENTALIZE","TRANSCENDENTALIZM"]  As this is a code golf challenge, the shortest solution wins! I will accept the shortest solution in 7 days from posting. Happy golfing! Sample solution, non-golfed ## Related EDIT: Made an error in the substitutions, had G = 6 and 6 = 9 as separate substitutions, merged them into one. • I think 48 hours is a bit short. What about people who only golf on weekends? – Adám May 15 '18 at 7:49 • Good point, I'll give it a week. – maxb May 15 '18 at 7:57 • Other substitutions that you might consider allowing are 7 for T, H for I (but not 1 for H), and 2 for N or Z – Jeff Zeitlin May 15 '18 at 11:39 • @JeffZeitlin We had a discussion about this in the challenge sandbox, and I chose to exclude all substitutions which are not reversible. Since T and L are not exchangeable, I can't add T = 7. The same goes for N = Z = 2, which would imply that N = Z = R = 2. However, the kind of substitutions you suggest would make for a more difficult version of this challenge, which I might post at a later time. I just wanted to see if these kinds of challenges would be well received first. – maxb May 15 '18 at 11:55 • @3D1T0R yes, the substitution must go both ways. The check is if two strings are valid rearrangements of each other. Your example would return false. – maxb May 15 '18 at 17:53 ## 15 Answers # Japt, 383633 30 bytes Takes input as an array of 2 strings. ®dz³mioiglbgí)Ôu)ñ xÃr¶  3 bytes saved thanks to ETHProductions ®dz...mio...gí)Ôu)ñ xÃr¶ :Implicit input of array U ® :Map each Z d : For each pair of characters in the following string, : replace all occurrences of the 1st character in Z with the 2nd z...m : The compressed string "znewem" i : Prepend o...g : The compressed string "oireasglbg" í : Interleave 0-based indices ) : End prepend Ô : Reverse u : Convert to uppercase ) : End replace ñ : Sort x : Trim Ã :End map r :Reduce ¶ : By testing equality  • Nice, currently beating Jelly! I think you can do r¶ at the end to save 3 bytes. – ETHproductions May 16 '18 at 4:45 • Oh, yeah, I was forgetting that. Thanks, @ETHproductions. – Shaggy May 16 '18 at 8:42 # Python 2, 145131130129 125 bytes lambda*a:all(len({sum(map(w.count,x))for w in a})<2for x in'A4 B8 CU EMW3 G69 I1 L7 NZ O0 R2 S5'.split()+list('DFHJKPQTVXY'))  Try it online! Alt: # Python 2, 125 bytes lambda*a:len({(x,sum(map(w.count,x)))for x in'A4 B8 CU EMW3 G69 I1 L7 NZ O0 R2 S5'.split()+list('DFHJKPQTVXY')for w in a})<23  Try it online! # Ruby, 9972 71 bytes ->a{!!a.map{|x|x.tr("0-9UZMW","OIREASGLBGCNE").chars.sort-[" "]}.uniq!}  Try it online! Takes an array of strings, assumes input in uppercase as in all test cases. -1 byte golfed by benj2240. # JavaScript (ES6), 102 100 bytes Takes input as two arrays of characters in currying syntax (a)(b). Returns a boolean. a=>b=>(g=s=>s.map(c=>'648UD3F6H1JK73Z0PQ25TUV3'[parseInt(c,36)-9]||c).sort().join.trim())(a)==g(b)  Try it online! ### How? Using the helper function g(), for each input s: • Digits 0 to 8 and letters X, Y and Z are left unchanged. Everything else is explicitly translated. 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ .........648UD3F6H1JK73Z0PQ25TUV3...  Code: s.map(c => '648UD3F6H1JK73Z0PQ25TUV3'[parseInt(c, 36) - 9] || c)  • We sort the characters (which brings all spaces at the beginning), join them and remove all leading whitespace. Code: .sort().join.trim()  Finally, we compare both outputs. # Retina 0.8.2, 42 bytes T dUZMW_\OIR\EASG\LBGCN\E %O. ^(.*)¶\1$


Try it online! Takes input on separate lines, but Link includes test cases and header. Explanation:

T dUZMW_\OIR\EASG\LBGCN\E


Map all the letters to a minimal set, deleting the spaces.

%O.


Sort each string into order.

^(.*)¶\1$ Compare the two values. # APL (Dyalog Unicode), 49 bytesSBCS -1 thanks to ngn. Anonymous tacit prefix function. (≡/(⍋⌷¨⊂)¨)(,¨⎕D,'UMWZ ')⎕R('OIREASGLBGCEEN',⊂⍬)  Try it online! ⎕R PCRE Replace: 'UMWZ ' these five characters ⎕D, preceded by the digits ,¨ separately (make each into a string rather than being a single character) with: ⊂⍬ nothing 'OIREASGLBGCEEN', preceded by these characters () apply the following tacit function to that: ()¨ apply the following tacit function to each: ⊂ enclose it (to treat it as a whole) ⍋⌷¨ use each of the indices that would sort it to index into the whole string (sorts) ≡/ are they identical? (lit. match reduction) • '' -> ⍬­­­­ – ngn May 15 '18 at 19:51 • @ngn Not sure why that works, but thanks anyway. – Adám May 15 '18 at 20:33 # Python 2, 108 bytes lambda a,b:g(a)==g(b) g=lambda s:sorted('85930A4614012B3C4D5EF6378GH9AI2J3KL7'[int(c,36)]for c in s if'!'<c)  Try it online! There are 23 equivalence classes of characters. Using the 36-character string '85930A4614012B3C4D5EF6378GH9AI2J3KL7', we map each character to its equivalence class (ignoring spaces), then sort the resulting array. Two strings are equivalent iff the resulting lists are equal. # Java 10, 262260258216208 174 bytes a->b->n(a).equals(n(b));String n(String s){return s.chars().mapToObj(x->x<48?"":"OIREASGLBG.......ABCDEFGHIJKLENOPQRSTCVEXYN".split("")[x-48]).sorted().reduce("",(a,b)->a+b);}  -2 bytes thanks to @Arnauld. -76 bytes thanks to @OlivierGrégoire. Try it online. Explanation: a->b-> // Method with two String parameters and boolean return-type n(a).equals(n(b)) // Return if both Strings are equal in the end String n(String s){ // Separated method with String as both parameter return-type return s.chars() // Loop over all characters as integers .mapToObj(x->x<48?// If the current character is a space: "" // Replace it with an empty String to skip it : // Else: "OIREASGLBG.......ABCDEFGHIJKLENOPQRSTCVEXYN".split("")[x-48] // Convert multi-substitution characters to a single one .sorted() // Sort all of the converted characters .reduce("",(a,b)->a+b);} // And join all of them together as single String  • "A4B8CUEMEWE3G6G9I1L7NZO0R2S5".split("(?<=\\G.{2})") seems to work ... although I'm not even sure exactly how. :p – Arnauld May 15 '18 at 17:42 • @Arnauld Thanks, -2 bytes, and {2} can be . for another -2 bytes. I'm also not 100% sure how it works. I know (?<= ... ) is used to split but keep trailing delimiter per item. But I'm a bit confused why (?=\\G..) (keep leading delimiter) doesn't work in that regard. And also don't really know how the \\G.. vs .. acts in the split here. Will see if I can figure it out somewhere, because not I'm curious. ;p Thanks either way for the saved bytes. Need to remember \\G when splitting on even-sized blocks. :) – Kevin Cruijssen May 16 '18 at 7:12 • @Arnauld If you want some background about (?<=\\G..) inside a split, I've made a StackoverFlow question which shed some light. Basically it's undefined behavior working differently in almost every language. Although \G is zero-length, in Java with the positive look-behind inside the split, it kinda conflicts both rules causing the behavior we see here. It's still all a bit vague to me personally, but at least it saved 4 bytes in this answer. ;) – Kevin Cruijssen May 16 '18 at 9:23 • 217 bytes. Can save more bytes, though. – Olivier Grégoire May 16 '18 at 10:43 • – Olivier Grégoire May 16 '18 at 12:21 # R, 123 bytes function(x,y=chartr("48UMW36917Z025","ABCEEEGGILNORS",gsub(" ","",x)))all(g(y[1])==g(y[2])) g=function(z)sort(utf8ToInt(z))  Try it online! utf8ToInt converts a string into a vector of Unicode code points. !sd(a-b) is one byte shorter than all(a==b) but that doesn't help here because I'm actually dealing with integers and not logicals. • Very nice! I think you need !any in place of !sd as the elements could be all equal, but to 1. Try: f(list("BCDEF","ABCDE")) – JayCe May 16 '18 at 19:12 # J, 56 bytes -:&(-.&' '/:~@rplc'0123456789UMWZ';"0'OIREASGLBGCEEN'"1)  Try it online! ## Explanation: & for both left and right arguments -.&' ' removes spaces from the input, rplc replaces '0123456789UMWZ';"0'OIREASGLBGCEEN'"1 the characters in the input by substitution of the chars in the left column with the ones in the right one: (here transposed for saving space)  |:'0123456789UMWZ';"0'OIREASGLBGCEEN' ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐ │0│1│2│3│4│5│6│7│8│9│U│M│W│Z│ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤ │O│I│R│E│A│S│G│L│B│G│C│E│E│N│ └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘  /:~@ and sorts the resulting strings -: are the sorted strings equal? ## My initial solution: # J, 77 73 bytes -:&(1#.(' '-.~])e."1[:(,a.,.@-.,)'EMW3','G69',9 2$'A4B8CUI1L7NZO0R2S5'"1)


Try it online!

## Explanation:

(' '-.~]) removes spaces from both arguments and

e."1 tests each character for membership in the follwing table:

[:(,a.,.@-.,)'EMW3','G69',9 2\$'A4B8CUI1L7NZO0R2S5'"1 the reference:

EMW3
G69
A4
B8
CU
I1
L7
NZ
O0
R2
S5
.
.
.
All other symbols one in a row


1#. adds up the comparison tables for each argument

-:& do they match?

# Perl 6, 55 bytes

{[eq] .map:{sort TR/0..9UZMW/OIREASGLBGCNEE/~~m:g/\S/}}


Try it online!

Works with an arbitrary number of strings.

# Python 2, 111 bytes

lambda*t:2>len({sorted(s.translate(dict(map(None,map(ord,'48UMW36917Z025 '),u'ABCEEEGGILNORS'))))for s in t})


Try it online!

# 116 bytes

lambda a,b:g(a)==g(b)
g=lambda s,y='4A8BUCMEWE3E6G9G1I7LZN0O2R5S ':y and g(s.replace(y[0],y[1:2]),y[2:])or sorted(s)


Try it online!

# Python 3, 105 bytes

lambda a,b:X(a)==X(b)
X=lambda s:sorted(("ABCEEEGGILNORS"+c)["48UMW36917Z025".find(c)]for c in s if' '<c)


Try it online!

# Jelly, 39 34 bytes

ØB“¡Œ5a@ẓRɼ9ẓ“%-/İ"aU5®’ṃyḟ⁶ṢɗⱮ⁸E


Try it online!

# 05AB1E, 38 33 bytes

εðK.•2ΘĆĀÑεÉ•u6«•B/óÕ¦•…CN9«‡{}Ë


Explanation:

ε                   # Map each value in the (implicit) input-list by:
ðK                 #  Remove all spaces
.•2ΘĆĀÑεÉ•      #  Push compressed string "abemwgilorsuz"
u     #  To uppercase: "ABEMWGILORSUZ"
6«   #  Append a 6: "ABEMWGILORSUZ6"
•B/óÕ¦•          #  Push compressed integer 48333917025
…CN9«     #  Append "CN9": "48333917025CN9"
‡                #  Transliterate; map all characters in "ABEMWGILORSUZ" in the
#  map-string to "48333917025CN9" at the same indices
{               #  Then sort all characters
}                   # Close the map
Ë                  # And check if both are equal (which is output implicitly)


See this 05AB1E tip of mine (sections How to compress strings not part of the dictionary? and How to compress large integers?) to understand why .•2ΘĆĀÑεÉ• is "abemwgilorsuz" and •B/óÕ¦• is 48333917025.