# What numbers are these?

While I was writing numbers I noticed after a while that my keyboard had the Shift key pressed and blocked and all I wrote was $%&-like characters. And even worse, I had been switching between the English and Spanish keyboard layouts so I don't know which one I used for each number. ### Challenge Given a string containing symbol characters, try to guess which number I wrote. My keyboard produces the following characters for the numbers when the Shift is pressed: 1234567890 ---------- !"·$%&/()=  Spanish layout
!@#$%^&*() English layout  • The input will be a non-null, non-empty string composed of the symbols above. • The output will be a single number if the keyboard layout can be inferred from the string (i.e. if the string contains a @ an English layout was used, and if the string contains a " a Spanish layout was used) or if the number is the same for both layouts (i.e. the input is !$ which translates as 14 for both layouts); otherwise the output will be the two possible numbers for both layouts if it cannot be inferred and the resulting numbers are different.
• The input string will always be written in a single layout. So you don't need to expect "@ as input.

### Examples

Input  -->  Output
------------------
/()         789        (Spanish layout detected by the use of /)
$%& 456,457 (Layout cannot be inferred) !@# 123 (English layout detected by the use of @ and #) ()&! 8961,9071 (Layout cannot be inferred) ((·)) 88399 (Spanish layout detected by the use of ·) !$          14         (Layout cannot be inferred but the result is the same for both)
!!$$%% 114455 (Layout cannot be inferred but the result is the same for both) ==" 0042/42 (Spanish layout, if a number starts with 0 you can choose to omit them in the result or not) Single character translations: ------------------------------ ! 1 " 2 · 3  4 % 5 & 6,7 / 7 ( 8,9 ) 9,0 = 0 @ 2 # 3 ^ 6 * 8  This is , so may the shortest code for each language win! • Dang it, that · is challenging... Oct 12, 2018 at 20:39 • @EriktheOutgolfer in fact the · is useless for Spanish, it is only used in the Catalan language. Oct 12, 2018 at 20:41 • Is output like {(8, 9, 6, 1), (9, 0, 7, 1)} (for the 4th test case) acceptable? – lynn Oct 12, 2018 at 21:01 • @Lynn yes, it is. Oct 12, 2018 at 21:03 • When outputting 2 numbers, does the order matter? Oct 12, 2018 at 22:30 ## 14 Answers # Jelly, 32 31 bytes O“=!"Ṣ%&/()“)!@#%^&*(‘iⱮ€PƇ’Q  Try it online! • -1 bytes thanks to Erik the Outgolfer O“!"Ṣ%&/()=“!@#%^&*()‘iⱮ€PƇ%⁵Q O ord of each character in the input “!"Ṣ%&/()=“!@#%^&*()‘ Constant that yields the list: [[33, 34, 183, 36, 37, 38, 47, 40, 41, 61], [33, 64, 35, 36, 37, 94, 38, 42, 40, 41] € For each list of numbers: Ɱ For each ord of the characters in the input: i Find the index of the ord of the character in the list of numbers. If the number is not found, i returns zero which means it's a character from only one keyboard. There are now two lists of numbers 1-10. Ƈ Keep the list(s) that: P have nonzero product. %⁵ Modulo 10. This maps 10->0. Q Unique elements. This removes duplicates if the two numbers are the same.  • Oct 13, 2018 at 11:33 # Python 3, 76 bytes lambda s:{(*map(k.find,s),)for k in['=!"·%&/()',')!@#%^&*(']if{*s}<={*k}}  Try it online! # Perl 6, 62 bytes {set grep {!/\D/},TR'=!"·%&/()'0..9',TR')!@\x23%^&*('0..9'}  Try it online! Returns a Set. (Could be made two or three bytes shorter if there wasn't a bug in Rakudo's handling of # in search lists.) # Java (JDK), 173 bytes Golfed c->{String s="",e=s;var m="21#3457#908###6##12#456389###0#7".split("");for(int l:c){e+=m[l=l%16];s+=m[l+16];}return s.equals(e)|s.contains("#")?e:e.contains("#")?s:s+","+e;}  Try it online! Ungolfed c->{ // Lamdba taking char array as input String s="",e=s; // Initialise Spanish and English strings var m="21#3457#908###6##12#456389###0#7".split(""); // Create magic hashing lookup array (see below) for(int l:c){ // Loops through all chars in input e+=m[l=l%16]; // Get english number from array and append s+=m[l+16]; // Get Spanish number from array and append } return s.equals(e)|s.contains("#")?e: // If equal or Spanish is invalid return english e.contains("#")?s: // If English is invalid return Spanish s+","+e; // If both are valid but not equal, return both }  The Magic Hashing Lookup Array After some experimenting with values I realised that each of the ASCII values of the characters !"·%&/()=@#^* modulo 16 returns a unique number. The 'magic hashing lookup array' stores the English numbers associated with each character at this unique index, and each of the Spanish numbers at this index offset by 16, making fetching the required number from the array trivial for each language. A hash is stored for values that are invalid for either language. • I don't suppose you could use toCharArray() and the int values to make this shorter? (Just an idea, I haven't tried it yet.) Oct 13, 2018 at 0:46 • @Quintec I tried it, but the extra bytes from toCharArray() and calculating the exponent to be applied to the int value made it far longer than both the .contains() statements. Oct 13, 2018 at 16:35 • s.equals(e)|s.contains("#") can be s.matches(e+"|.*#.*"). Oct 15, 2018 at 6:41 • Suggest l%=16 instead of l=l%16 Jan 3, 2020 at 1:26 # Japt, 38 bytes Outputs an array of strings with the Spanish layout first. "=!\"·%&/())!@#%^&*("òA £ËXbD kø'- â  Try it # Jelly, 38 bytes 183Ọ“=!"“%&/()”j,“)!@#%^&*(”iⱮ€⁸ẠƇ’Q  Try it online! • Nice! Just one question, I have tried your code with () or (()) as input, but your code then return nothing. I suppose that's a limitation with what Jelly receives as input? Oct 12, 2018 at 21:27 • @Charlie Try with '()' and '(())' respectively. Yes, if you don't quote the argument, it's only inputted as a string if it can't be evaluated to a Python 3 value. Oct 12, 2018 at 21:28 # Retina 0.8.2, 60 bytes .+ &¶& T=!"·%&/()d^.+ T)!@#%^&*(d.+ D Gm^\d+  Try it online! Link includes test cases. Explanation: .+ &¶&  Duplicate the input. T=!"·%&/()d^.+ T)!@#%^&*(d.+  Try to translate each line according to a different keyboard layout. D  Deduplicate the result. Gm^\d+  Only keep lines that only contain digits. • Do you need the m in your last stage? – ovs Oct 13, 2018 at 10:38 • @ovs Yes, the matches run first, and then the lines are split and lines containing matches are kept. – Neil Oct 13, 2018 at 12:12 # JavaScript (ES6), 99 bytes s=>(g=l=>a=s.replace(/./g,c=>l.indexOf(c)))('=!"·%&/()',b=g(')!@#%^&*('))>=0?a-b&&b>=0?[a,b]:a:b  Try it online! ### How? The helper function $$\g\$$ attempts to convert the input string using a given layout. Invalid characters are replaced with $$\-1\$$, which results in either a valid but negative looking numeric string (if only the first character was missing), or an invalid numeric string. Either way, the test x >= 0 is falsy. # 05AB1E, 42 41 bytes •Hhç₁d©u÷^Σ(“ðΣèõĆ -•184в2äεIÇk}ʒ®å_}>T%Ù  Port of @dylnan's Jelly answer. Explanation: •Hhç₁d©u÷^Σ(“ðΣèõĆ -•184в # Compressed list [33,34,183,36,37,38,47,40,41,61,33,64,35,36,37,94,38,42,40,41] 2ä # Split into two parts: [[33,34,183,36,37,38,47,40,41,61],[33,64,35,36,37,94,38,42,40,41]] ε } # Map each inner list to: IÇ # Get the input, and convert each character to its unicode value k # Then get the index of each unicode value in the current map-list # (this results in -1 if the item doesn't exist) ʒ } # Filter the resulting list of indices by: ®å_ # If the inner list does not contain any -1 > # Increase each index by 1 to make it from 0-indexed to 1-indexed T% # Take modulo-10 to convert 10 to 0 Ù # Uniquify the result-lists (and output implicitly)  See this 05AB1E tip of mine (section How to compress integer lists?) to understand why •Hhç₁d©u÷^Σ(“ðΣèõĆ\n-•184в is [33,34,183,36,37,38,47,40,41,61,33,64,35,36,37,94,38,42,40,41]). This (together with the 2ä) is 1 byte shorter than taking the unicode values of the string: '""!ÿ·%&/()=""!@#%^&*()"‚Ç. • The ! and !!$$%% cases should output only one number as the result is the same for both layouts and there is no ambiguity. Oct 15, 2018 at 7:17
• @Charlie Oops, fixed Oct 15, 2018 at 7:21

# Ruby, 68 bytes

->s{%w[=!"·$%&/() )!@#$%^&*(].map{|a|s.tr a,'0-9'}.grep_v(/\D/)|[]}


Try it online!

# Clean, 116 bytes

import StdEnv,Text
$s=removeDup[foldl(<+)""d\\r<-["=!\"·$%&/()",")!@#$%^&*("],d<-[[indexOf{c}r\\c<-s]]|all((<) -1)d]  Try it online! Takes input and is encoded in CP437. TIO only supports UTF-8, so an escape is used in the demo code to get the literal byte value 250 corresponding to the centre dot (counted as one byte). • The !$% input should output only one number, not two, as the result is the same for both layouts. Oct 15, 2018 at 7:15
• @Charlie Fixed it. Oct 15, 2018 at 8:30

# APL (Dyalog), 40 bytes

Anonymous tacit prefix function. Though unused, · is in the Dyalog single byte character set. Assumes 0-based indexing (⎕IO←0) which is default on many systems.

{∪⍵/⍨~10∊¨⍵}'=!"·$%&/()' ')!@#$%^&*('⍳¨⊂


Try it online!

⊂ the entire argument

'=!"·$%&/()' ')!@#$%^&*('⍳¨ indices of the characters in each of these strings

{∪⍵/⍨~10∊¨⍵} apply the following lambda (⍵ is the argument):

10∊¨⍵ for each list of digits, whether 10 (indicating "not found") is a member thereof

~ local negation (i.e. only those where all digits are found)

⍵/⍨ filter the argument by that

∪ find the unique elements of that

# Dart, 125 bytes

f(s)=>['=!"·$$\%&/()',')!@#\$$%^&*('].map((b)=>s.split('').map((e)=>b.indexOf(e)).join()).where((e)=>!e.contains('-')).toSet();


Ungolfed :

f(s){
['=!"·$$\%&/()',')!@#\$$%^&*(']
.map(
(b)=>s.split('').map((e)=>b.indexOf(e))
.join()
).where(
(e)=>!e.contains('-')
).toSet();
}

• Creates an array with the two specified key values, from 0 to 9
• For each of them, convert the input string to the corresponding number by using the characters' indexes
• Join the resulting array to create a number
• Remove any number having a '-' (Dart returns -1 when indexOf can't find a char)
• Return as a Set to remove duplicates

SELECT DISTINCT*FROM(SELECT TRY_CAST(TRANSLATE(v,m,'1234567890')as INT)a
FROM i,(VALUES('!"·$%&/()='),('!@#$%^&*()'))t(m))b

Joins the input table with the two different character strings, then uses the new SQL 2017 function TRANSLATE to swap out individual characters, and TRY_CAST to see if we end up with a number. If not, TRY_CAST returns NULL.
The final outer SELECT DISTINCT combines identical results and filters out the NULLS.