# Room Number Locator

I have come across an interesting problem solving technique at my job when given the wrong room number from a colleague for a meeting. Every now and then, while on the way to a meeting, a member on my team will send me the wrong room number, typically because they are in a rush at their desk and fat finger the wrong key.

Interestingly, upon arrival at the wrong room, I typically can guess which room they really meant by imagining a Numeric Keypad: and by guessing an adjacent number they meant to press.

## Challenge

Your challenge is to write a function that takes a building office number (000-999) and outputs the possible typo solutions, assuming your colleague only mistypes one digit.

The following table shows which numbers are adjacent to each other on a Numeric Keypad:

0 -> 1,2
1 -> 0,2,4
2 -> 0,1,3,5
3 -> 2,6
4 -> 1,5,7
5 -> 2,4,6,8
6 -> 3,5,9
7 -> 4,8
8 -> 5,7,9
9 -> 6,8


## Input

A 3 digit number: 000-999. Assume input of exactly 3 digits. If the number is less than 100 or less than 10, you will be given the leading zeros. (i.e. 004 & 028).

## Output

A list of possible rooms. This can be in any form you want, as long as there is a delimiter between room numbers. (i.e. space, comma, new line, etc..) If the number is less than 100 or less than 10, you can or cannot have the leading zeros as output, that is up to you. (i.e. 004 can be 004 04 4, and 028 can be 028 28)

Test Cases(leading zeros are optional):

008 -> 108, 208, 018, 028, 005, 007, 009
123 -> 023, 223, 423, 103, 113, 133, 153, 122, 126
585 -> 285, 485, 685, 885, 555, 575, 595, 582, 584, 586, 588
777 -> 477, 877, 747, 787, 774, 778
963 -> 663, 863, 933, 953, 993, 962, 966
555 -> 255, 455, 655, 855, 525, 545, 565, 585, 552, 554, 556, 558


This is , so the shortest code in bytes for each language wins.

• Can we take input as a list of three digits (0-9)? – hyper-neutrino Jan 19 '18 at 15:19
• ...and this is why meeting rooms should have names. – Jonathan Allan Jan 19 '18 at 16:35
• @JonathanAllan It's a lot harder for new people to find "Dolphin Room" than "Room 218" (assuming that room numbers are assigned in order). A compromise would be alphabetically ordering the names, but then you only have 26. – Andrew Jan 19 '18 at 19:57
• @KellyLowder should have been 933 so I've fixed it up. – Jonathan Allan Jan 19 '18 at 20:22
• Related, I once worked in IT where there was a professor who had trouble with the room technology several weeks running. He was in Bradley 210 (which I knew, Bradley being the name of the building. The building next door--Matheson--was connected via a sky bridge on the 3rd floor. Bradley was 5 stories tall, Matheson 4). He could never tell me what room he was in correctly. One time he told me he was in "Matheson 605" which patently didn't exist, and got none of the digits right. – Draco18s no longer trusts SE Jan 19 '18 at 22:26

# Wolfram Language (Mathematica), 112 106 bytes

Recognizing that a numeric keypad is basically a 3x3 GridGraph with edges added for 0, we get the adjacent digits for each input digit with AdjacencyList.

This can be seen below:

EdgeAdd[GridGraph[{3,3},VertexLabels->"Name",GraphLayout->"SpringEmbedding"],{0<->1,0<->2}] yields: Then I use Tuples to figure out all the possible mistakes and pick out those with exactly one error with Select and EditDistance. By the way, this will work for longer room numbers and you can also increase the EditDistance parameter to allow for more than one error. Might be able to golf this down a little further but wanted to show my approach.

h@u_:=Select[Tuples[AdjacencyList[EdgeAdd[GridGraph[{3,3}],{0<->1,0<->2}],#]~Join~{#}&/@u],#~EditDistance~u==1&]


Slightly more golfed version hardcoded to length 3 room numbers (106 Bytes). This will output as a rank 3 list corresponding to each digit:

Thread/@ReplacePart[#~Table~3,{i_,i_}:>(AdjacencyList[GridGraph@{3,3}~EdgeAdd~{0<->1,0<->2},#]&/@#)[[i]]]&


Try it online!

• One could also use other distance functions such as DamerauLevenshteinDistance instead of EditDistance which would also include transposition errors. – Kelly Lowder Jan 19 '18 at 22:19

# Python 2, 89 bytes

lambda r:[r[:i]+[c]+r[i+1:]for i,n in enumerate(r)for c inord(u'ÌЋ>তŧ0ɃD'[n])]


Try it online!

The 1st and 5th characters may not being displayed here (browser dependent), but the full string is equivalent to [21, 204, 1035, 62, 157, 2468, 359, 48, 579, 68]

# 05AB1E, 29 bytes

v•4TË\ªye-³—Ïʒ••Ćδn¼•S£yèʒNǝ,


Try it online!

# R, 190 bytes

function(x){l=list(c(1,2),c(0,2,4),c(0,1,3,5),c(2,6),c(1,5,7),c(2,4,6,8),c(3,5,9),c(4,8),c(5,7,9),c(6,8))
a=do.call(expand.grid, mapply(c,l[x+1],x))
a[apply(a,1,function(y){sum(x==y)==2}),]}


Try it online!

My second attempt at CodeGolf! Pretty long, 190 bytes, but the best I could manage with R. Curious to see if others have feedback or can do better!

• a whole bunch of little things: you have an extra space in the second line; abusing the precedence of : over */+- can shave off a few bytes in the first line, getting rid of do.call, treating a as a matrix and transposing it saves all around 39 bytes: Try it online! – Giuseppe Jan 21 '18 at 19:05
• You are good at this! Thanks for the feedback. – Florian Jan 21 '18 at 19:16

## JavaScript (Firefox 30-57), 115 109 bytes

f=([c,...a],p=)=>c?[...(for(n of+[12,240,1350,26,157,2468,359,48,579,68][c])p+n+a.join),...f(a,p+c)]:[]


Edit: Saved 6 bytes thanks to @edc65 (although suggested 0s now appear after other suggestions). ES6 version, 118 112 bytes:

f=([c,...a],p=)=>c?[...[...+[12,240,1350,26,157,2468,359,48,579,68][c]].map(n=>p+n+a.join),...f(a,p+c)]:[]
<input oninput=o.textContent=f(this.value).join\n><pre id=o>

• I see this [for(...)] in a lot of code golfs, but I don't fully understand it, and I can't seem to find it in any documentation. Could you explain it or post a link to an explanation? – TheOtherOne Jan 21 '18 at 21:02
• save 6 bytes [...[12,240,1350,26,157,2468,359,48,579,78][c]+''] – edc65 Jan 21 '18 at 21:14
• @AntonBallmaier [for(...)] was one of several array comprehension syntax proposals that never made it into ECMAscript. It allowed you to loop over an iterator and succinctly filter and/or map over the results. (I found it particularly useful when doing double iteration.) – Neil Jan 21 '18 at 23:01

# Java, 205 177 bytes

b->{for(int c=0;c<3;c++){char[]d=b.toCharArray();for(char e:"12,024,0135,26,157,2468,359,48,579,68".split(",")[new Byte(""+d[c])].toCharArray()){d[c]=e;System.out.println(d);}}}


I know it's long compared to the other answers. My excuse: it's in Java.
Oracle should rename toCharArray to something like getCrs.

## Credits

-28 characters by Kevin Cruijssen

• Some small things to golf. (String b)-> can be just b->, and you can remove the trailing ;. As for actual things to golf: You only use a once, so you can remove String[]a=...; and use "12,024,0135,26,157,2468,359,48,579,68".split(",")[...] directly. Also, Byte.parseByte can be new Byte. In total: 177 bytes. – Kevin Cruijssen Jan 22 '18 at 8:18
• @KevinCruijssen thanks, those are some tricks I'll have to learn :) – Reinis Mazeiks Jan 22 '18 at 14:40
• Tips for golfing in Java and Tips for golfing in <all languages> might be interesting to read through in case you haven't yet. :) – Kevin Cruijssen Jan 22 '18 at 14:55

# Ruby 97 bytes

->i{c=0;i.map{|j|[12,204,1035,26,157,2468,359,48,579,68][j].digits.map{|k|f=*i;f[c]=k;p f};c+=1}}


Try it online!

### Alternatively, 94 chars but 100 bytes

->i{c=0;i.map{|j|"\fÌЋ\u001A\u009Dতŧ0ɃD".unpack("U*")[j].digits.map{|k|f=*i;f[c]=k;p f};c+=1}}


Try it online!

# C (gcc), 136 or 114 bytes

## ASCII version 136 bytes

m[]={12,240,1350,26,157,2468,359,48,579,68},p,i,X=10;f(n){for(i=100;i;i/=X)for(p=m[n/i%X];p;p/=X)printf("%d ",n/(i*X)*(i*X)+p%X*i+n%i);}


Try it online!

## Unicode 114 108 bytes (TiO seems to count weirdly for this)

Thanks to @ceilingcat for this version.

p,i,X=10;f(n){for(i=1e3;i/=X;)for(p=L"\fðՆ\32\x9dতŧ0ɃD"[n/i%X];p;p/=X)printf("%d ",n/i/X*i*X+p%X*i+n%i);}


Try it online!

• @ceilingcat Hm. TiO says 108 bytes. – gastropner Nov 28 '19 at 5:03
• I don’t think TIO counts UTF-8 bytes correctly in C. Try changing the language to bash or something else and watch the byte count change. – ceilingcat Nov 28 '19 at 6:46
• @ceilingcat Yeah, was wonky locally too. Saved file is 114, true enough. – gastropner Nov 28 '19 at 7:33

# Perl 5, 120 85 + 2 (-F) = 87 bytes

map{@,=@F;$,[$i]=$_,say@,for(12,240,1350,26,157,2468,359,48,579,68)[$_]=~/./g;$i++}@F  Try it online! Saved 35 bytes by borrowing an idea from @AsoneTuhid's ruby answer. # Python 2, 103 bytes thanks to @Lynn for -4 bytes. lambda n:{n[:i]+r+n[i+1:]for i,v in enumerate(n)for r in0x134cd9a07d1e58feab643f7db24102[int(v)::10]}  Try it online! • Save 4 bytes with: in0x134cd9a07d1e58feab643f7db24102[int(v)::10] (I tried int('…',36) too but it’s one byte longer.) – Lynn Jan 19 '18 at 21:03 # Julia 0.6, 93 bytes ~r=[(R=copy(r);R[j]=i;R)for i=0:9,j=1:3 if(big(1)<<(i+10r[j]))&0x502A044228550A21102B05406>0]  Try it online! • Takes a vector of digits and returns a list in the same format. • 0x502A044228550A21102B05406 is a UInt128 in which the 1+10jth bit is set iff i is next to j on the numpad. • big(1) is a BigInt. It is used to prevent overflow and uses less characters than Int128(1) or UInt128(1). # Jelly, 35 bytes ḷþị“-ⱮⱮVḟ|żṣ~ẋ³ɱgẆ’ḃ⁽¦ḳ¤$ṛ¦DŒp$¥"JẎ  Try it online! -1 thanks to Jonathan Allan. Explanation being updated... • I honestly have no clue about how this is parsed, let alone how it works. An explanation would be greatly appreciated. – caird coinheringaahing Jan 19 '18 at 22:32 • @cairdcoinheringaahing sorry, no time now – Erik the Outgolfer Jan 19 '18 at 22:44 • -1 byte: Wẋ3 -> ḷþ – Jonathan Allan Jan 21 '18 at 20:33 ## SQL (SQLite), 533 bytes with m as (select 0 as i, 1 as o union values (0,2),(1,0),(1,2),(1,4),(2,0),(2,1),(2,3),(2,5),(3,2),(3,6),(4,1),(4,5),(4,7),(5,2),(5,4),(5,6),(5,8),(6,3),(6,5),(6,9),(7,4),(7,8),(8,5),(8,7),(8,9),(9,6),(9,8))select o || substr('008', 2, 1) || substr('008', 3, 1)from m where substr('008', 1, 1) = cast(i as text)union select substr('008', 1, 1) || o || substr('008', 3, 1)from m where substr('008', 2, 1) = cast(i as text)union select substr('008', 1, 1) || substr('008', 2, 1) || o from m where substr('008', 3, 1) = cast(i as text)  ### Ungolfed with m as ( select 0 as i, 1 as o union values /*(0,1),*/(0,2), (1,0),(1,2),(1,4), (2,0),(2,1),(2,3),(2,5), (3,2),(3,6), (4,1),(4,5),(4,7), (5,2),(5,4),(5,6),(5,8), (6,3),(6,5),(6,9), (7,4),(7,8), (8,5),(8,7),(8,9), (9,6),(9,8) ) select o || substr(s, 2, 1) || substr(s, 3, 1) from m, t where substr(s, 1, 1) = cast(i as text) union select substr(s, 1, 1) || o || substr(s, 3, 1) from m, t where substr(s, 2, 1) = cast(i as text) union select substr(s, 1, 1) || substr(s, 2, 1) || o from m, t where substr(s, 3, 1) = cast(i as text)  ### Explanation The input is a single text row on table t with column s. My understanding is that according to this meta answer this is an acceptable form of input. The input can be created as below. drop table if exists t; create table t (s text); insert into t values('555'); -- Your input here  ### Annotated solution with m as ( -- Using this in the "with" allows us to only type is once select 0 as i, 1 as o -- The first pair is here and it names the columns union values /*(0,1),*/(0,2), (1,0),(1,2),(1,4), (2,0),(2,1),(2,3),(2,5), (3,2),(3,6), (4,1),(4,5),(4,7), (5,2),(5,4),(5,6),(5,8), (6,3),(6,5),(6,9), (7,4),(7,8), (8,5),(8,7),(8,9), (9,6),(9,8) ) select o || substr(s, 2, 1) || substr(s, 3, 1) -- concat the first wrong char with two correct chars from m, t where substr(s, 1, 1) = cast(i as text) -- when the first char is in the i (input) column from above union select substr(s, 1, 1) || o || substr(s, 3, 1) from m, t where substr(s, 2, 1) = cast(i as text) union select substr(s, 1, 1) || substr(s, 2, 1) || o from m, t where substr(s, 3, 1) = cast(i as text)  # Kotlin, 117 bytes mapIndexed{i,c->"12,024,0135,26,157,2468,359,48,579,68".split(",")[c-'0'].map{replaceRange(i,i+1,it+"")}}.flatMap{it}  ## Beautified mapIndexed { i, c -> "12,024,0135,26,157,2468,359,48,579,68" .split(",")[c - '0'] .map { replaceRange(i, i + 1, it + "") } }.flatMap { it }  ## Test fun String.f(): List<String> = mapIndexed{i,c->"12,024,0135,26,157,2468,359,48,579,68".split(",")[c-'0'].map{replaceRange(i,i+1,it+"")}}.flatMap{it} data class Test(val input:Int, val answers: List<Int>) val tests = listOf( Test(8, listOf(108, 208, 18, 28, 5, 7, 9)), Test(123, listOf(23, 223, 423, 103, 113, 133, 153, 122, 126)), Test(585, listOf(285, 485, 685, 885, 555, 575, 595, 582, 584, 586, 588)), Test(777, listOf(477, 877, 747, 787, 774, 778)), Test(963, listOf(663, 863, 933, 953, 993, 962, 966)), Test(555, listOf(255, 455, 655, 855, 525, 545, 565, 585, 552, 554, 556, 558)) ) fun main(args: Array<String>) { for (r in tests) { val input = r.input.toString().padStart(3, '0') val expected = r.answers.map { it.toString().padStart(3, '0') }.sorted() val actual = input.f().sorted() if (expected != actual) { throw AssertionError("$input -> $actual |$expected")
}
}
}


TryItOnline

# T-SQL, 322 bytes

WITH m AS(SELECT LEFT(value,1)i,RIGHT(value,1)o FROM STRING_SPLIT('01,02,10,12,14,20,21,23,25,32,36,41,45,47,52,54,56,58,63,65,69,74,78,85,87,89,96,98',','))SELECT o+RIGHT(s,2)FROM t,m WHERE i=LEFT(s,1)UNION SELECT LEFT(s,1)+o+RIGHT(s,1)FROM t,m WHERE i=SUBSTRING(s,2,1)UNION SELECT LEFT(s,2)+o FROM t,m WHERE i=RIGHT(s,1)


The input is taken from the column s of a single-row table named t:

DROP TABLE IF EXISTS t
CREATE TABLE t (s CHAR(3))
INSERT INTO t VALUES('008')


Ungolfed:

WITH m AS (
SELECT LEFT(value,1) i, RIGHT(value,1) o
FROM STRING_SPLIT('01,02,10,12,14,20,21,23,25,32,36,41,45,47,52,54,56,58,63,65,69,74,78,85,87,89,96,98',',')
)
SELECT o+RIGHT(s,2) FROM t,m WHERE i=LEFT(s,1)
UNION
SELECT LEFT(s,1)+o+RIGHT(s,1) FROM t,m WHERE i=SUBSTRING(s,2,1)
UNION
SELECT LEFT(s,2)+o FROM t,m WHERE i=RIGHT(s,1)


SQLFiddle