dev.to staff

Posted on

# Daily Challenge #50 - Number Neighbor

Given a phone number of integer length `N (1 ≤ N ≤ 10)` as a number string (e.g. `555-555-5555` would be passed as `"5555555555"`), return an array of all phone numbers as number strings that would be considered neighbors of that phone number (which is any numbers ±1).

For example: someone with the phone number `555-555-5555` has neighbors `555-555-5554` and `555-555-5556`.

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

This challenge comes from SaladFork on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Chris Achard

Huh, this one was harder than I expected it to be... that probably means I missed something 🤣

Also, TIL that setting a character of a string in JS with square brackets doesn't throw an error - but also doesn't do anything! example:

``````let myStr = "abc123"
myStr[0] = 'X'
// myStr is unchanged here!
``````

Solution in javascript:

``````
const numberNeighbor = (number) => {
return [...number].reduce((arr, num, i) => {
num = Number(num)

if(num - 1 >= 0) {
arr.push([
number.slice(0, i),
num - 1,
number.slice(i+1)
].join(""))
}

if(num + 1 <= 9) {
arr.push([
number.slice(0, i),
num + 1,
number.slice(i+1)
].join(""))
}

return arr
}, [])
}
``````

Alfredo Salzillo

Strings in javascript are immutable, so even if you change a character using the `[]` notation, it always returns a new string.

This instead work as you expected

``````// The string is transformed into an Array
const arrString = [..."abc123"]
arrString[0] = "X"
// arrString is now ["X", "b", "c", "1", "2", "3"]
``````

Chris Achard

Makes sense - thanks! (forgot (or never really knew?) that strings in javascript were immutable)

Jarod Smith
``````// areaCode :: String -> String
const areaCode = str => str.substring(0,3);
// threeCode :: String -> String
const threeCode = str => str.substring(3,6);
// fourCode :: String -> String
const fourCode = str => str.substring(6);
// formatPhone :: String -> String
const formatPhone = str => `\${areaCode(str)}-\${threeCode(str)}-\${fourCode(str)}`;

// getNeighbors :: String -> [Number] -> [String]
const getNeighbors = number => [parseInt(number) + 1, parseInt(number) - 1]
.map(neighbor => formatPhone(`\${neighbor}`));
``````

Attempting to learn and understand more functional paradigms. Is this right way to solve this problem functionally?

Amin

Way to go buddy!

From my perspective, to format the phone number in 3 segments, I would have used this instead of the 3 calls you made.

``````"use strict"

const phoneNumber = "123456789"

const formatPhoneNumber = (string, glue) => string.match(/.{1,3}/g).join(glue)

console.log(formatPhoneNumber(phoneNumber, "-")) // 123-456-789
console.log(formatPhoneNumber(phoneNumber, " ")) // 123 456 789
console.log(formatPhoneNumber(phoneNumber, ".")) // 123.456.789
``````

This leverage the regular expressions to match three characters at most. I know regular expressions is not the easiest thing to use but in this case it can be quite handy. As it returns an array, joining the items will produce a string back so the signature of the function can be written as follow in a purely functional programming language like PureScript.

``````formatPhoneNumber :: String -> String -> String
``````

Unfortunately, currying (partially applying parameters) is not built-in in JavaScript. You could implement your own curry function and use it on all your future function definitions. But this would be overkill for this challenge. But if you would ask me how I would implement a currying function in JavaScript, here is my take.

``````"use strict"

function curry(callable, ...initialParameters) {
if (typeof callable !== "function") {
throw new TypeError("Function expected as first argument")
}

if (parameters.length >= callable.length) {
return callable(...parameters)
}

return curry(callable, ...parameters)
}
}

// format :: String -> Number -> String -> String
const format = curry(function(glue, segments, phone) {
return phone.match(new RegExp(`.{1,\${segments}}`, "g")).join(glue)
})

// usFormat :: String -> String
const usFormat = format("-", 3)

// frenchFormat :: String -> String
const frenchFormat = format(".", 2)

console.log(usFormat("123456789")) // 123-456-789
console.log(frenchFormat("1234567890")) // 12.34.56.78.90
``````

Last tip, when creating a function, try to put the data structure at the end, that way you can construct partial application of your function without pain, even in JavaScript. In this case, this means puting the phone number to format at the very end of the parameter's list in your function definition. Hope that helps you in your functional programing journey pal.

Jarod Smith

Hey thanks for the response I will look at regexes next time for this kind of problem. Also I thought currying was supported in ES6?
via

``````const f = a => b=> a + b
``````

Amin

What you did is indeed currying but it is not transparent in the sense that if you want to pass all three arguments at once you will have to make three calls. The curry implementation allows you to either pass all arguments at once like a regular function call or partially calling the function. I kinda dislike calling my function multiple times and the only two reasons are aesthetic and the fact that it is less natural to define functions like that but arrow functions syntax makes it a little bit cleaner.

La blatte

Here goes some ugly code!

``````f=a=>{p=parseInt,l=(''+a).length,i=0,r=[a];while(i<l){r.push(...[('0'+(p(a)-(10**i))).slice(-l),''+(p(a)+(10**i))]);++i}return r}
``````

It works like that:

• Take the input's length
• Add and subtract 10n (where n goes from 0 to the input's length - 1) to the input number
• Add a trailing 0 if necessary
• Return the built array!
``````f('5555555555').join('\n');
"5555555555
5555555554
5555555556
5555555545
5555555565
5555555455
5555555655
5555554555
5555556555
5555545555
5555565555
5555455555
5555655555
5554555555
5556555555
5545555555
5565555555
5455555555
5655555555
4555555555
6555555555"
``````

Alfredo Salzillo

One line JS generate all possible neighbor number for any position.

``````const allNumberNeighbor = number => [...number]
.map(Number)
.flatMap((n, i) => Number(n) && [
n + 1 <= 9 && Object.values({ ...number, [i]: n + 1 }),
n - 1 >= 0 && Object.values({ ...number, [i]: n - 1 }),
])
.filter(Boolean)
.map(n => n.join(''));

allNumberNeighbor('555-555-555');
// return
/*
[
'655-555-555',
'455-555-555',
'565-555-555',
'545-555-555',
'556-555-555',
'554-555-555',
'555-655-555',
'555-455-555',
'555-565-555',
'555-545-555',
'555-556-555',
'555-554-555',
'555-555-655',
'555-555-455',
'555-555-565',
'555-555-545',
'555-555-556',
'555-555-554',
];
*/
``````

My take on this challenge with JS

``````const neighbors = number => {
const format = n => {
return `\${`\${n}`.substr(0,3)}-\${`\${n}`.substr(3, 3)}-\${`\${n}`.substr(6, 4)}`;
};

const nArray = [...number].reduce((t, d, i) => {
const n1 = Number(number) + Math.pow(10, i);
const n2 = number - Math.pow(10, i);

t.push(format(n1));
t.push(format(n2));

return t;
}, [])

return nArray;
}

/**

neighbors("5555555555");

[
"555-555-5556",
"555-555-5554",
"555-555-5565",
"555-555-5545",
"555-555-5655",
"555-555-5455",
"555-555-6555",
"555-555-4555",
"555-556-5555",
"555-554-5555",
"555-565-5555",
"555-545-5555",
"555-655-5555",
"555-455-5555",
"556-555-5555",
"554-555-5555",
"565-555-5555",
"545-555-5555",
"655-555-5555",
"455-555-5555"
]

**/
``````

K.V.Harish

My solution in js

``````const numberNeighbour = (num) => {
const neighbours = [];
num.split('').forEach((value, index) => {
if(parseInt(value) - 1 > 0) {
let prevNum = num.split('');
prevNum[index] = parseInt(value) - 1;
prevNum = prevNum.join('');
neighbours.push(prevNum.slice(0, 3) + "-" + prevNum.slice(3, 6) + "-" + prevNum.slice(6));
}
if(parseInt(value) + 1 < 10) {
let nextNum = num.split('');
nextNum[index] = parseInt(value) + 1;
nextNum = nextNum.join('');
neighbours.push(nextNum.slice(0, 3) + "-" + nextNum.slice(3, 6) + "-" + nextNum.slice(6));
}
});
return neighbours;
};

numberNeighbour('5555555555');
/*[
"455-555-5555",
"655-555-5555",
"545-555-5555",
"565-555-5555",
"554-555-5555",
"556-555-5555",
"555-455-5555",
"555-655-5555",
"555-545-5555",
"555-565-5555",
"555-554-5555",
"555-556-5555",
"555-555-4555",
"555-555-6555",
"555-555-5455",
"555-555-5655",
"555-555-5545",
"555-555-5565",
"555-555-5554",
"555-555-5556"
]*/
numberNeighbour('1111111111');
/*[
"211-111-1111",
"121-111-1111",
"112-111-1111",
"111-211-1111",
"111-121-1111",
"111-112-1111",
"111-111-2111",
"111-111-1211",
"111-111-1121",
"111-111-1112"
]*/
numberNeighbour('9999999999');
/*[
"899-999-9999",
"989-999-9999",
"998-999-9999",
"999-899-9999",
"999-989-9999",
"999-998-9999",
"999-999-8999",
"999-999-9899",
"999-999-9989",
"999-999-9998"
]*/
``````

Yaroslav Barkovskiy

Simple ruby solution(with edge cases):

``````def neighbors str
num = str.to_i
res = case num
when 0
[num + 1]
when 9999999999
[num - 1]
when -Float::INFINITY..0, 9999999999..Float::INFINITY
raise 'Not a phone number'
else
[num - 1, num + 1]
end

res.map(&:to_s)
end
``````

Oleksii Filonenko

I liked the "any number can differ by 1" approach more - it's a but more work :)

Rust:

``````pub fn number_neighbors(number: u32) -> Vec<u32> {
let len = number.to_string().len() as u32;
(0..len)
.map(|delta| 10u32.pow(delta))
.flat_map(|delta| vec![number - delta, number + delta])
.collect()
}
``````

And a test (I usually omit them from my submissions, but I think they have a place here):

``````#[test]
fn test_number_neighbors() {
assert_eq!(vec![221, 223, 212, 232, 122, 322], number_neighbors(222));
}
``````

Amin

My take at the challenge written in Elm.

``````computeNeighbors : String -> Maybe (List Int)
computeNeighbors phoneNumber =
case String.toInt phoneNumber of
Just integerPhoneNumber ->
Just [integerPhoneNumber + 1, integerPhoneNumber - 1]
Nothing ->
Nothing
``````

Python one-liner to the rescue again. (Horribly formatted this time tho)

`f = lambda x: print(*[x[:len(x)-1]+str(int(x[len(x)-1])+1), x[:len(x)-1]+str(int(x[len(x)-1])-1)])`

``````>>> f('555-555-5555')
555-555-5556 555-555-5554
``````

any numbers ±1

🤔
Most people interpreted that as only a single number could vary at a time, others took the example too literally as the full number +- 1...

So I thought...

What if any numberS (plural) means any number of digits can vary...?

``````const neighbourSingle = (num) => [...new Set([ clamp(num-1), num, clamp(num+1) ])];
const clamp = (num) => Math.min(Math.max(num, 0), 9);
/*
neighbourSingle(5) === [4, 5, 6]
neighbourSingle(0) === [0, 1]
*/

const neighbours = (phoneString) => {
return phoneString.split("").reduce((a,b) => a.flatMap(c=> neighbourSingle(parseInt(b)).map(d=> c+d)) ,[""])
}
/*
neighbours("55")
(9) ["44", "45", "46", "54", "55", "56", "64", "65", "66"]

neighbours("555")
(27) ["444", "445", "446", "454", "455", "456", "464", "465", "466", "544", "545", "546", "554", "555", "556", "564", "565", "566", "644", "645", "646", "654", "655", "656", "664", "665", "666"]

neighbours("5555555555")
(59049) ["4444444444", "4444444445", "4444444446", "4444444454", "4444444455", "4444444456", "4444444464", "4444444465", "4444444466", "4444444544", "4444444545", "4444444546", "4444444554", "4444444555", "4444444556", "4444444564", "4444444565", "4444444566", "4444444644", "4444444645", "4444444646", "4444444654", "4444444655", "4444444656", "4444444664", "4444444665", "4444444666", "4444445444", "4444445445", "4444445446", "4444445454", "4444445455", "4444445456", "4444445464", "4444445465", "4444445466", "4444445544", "4444445545", "4444445546", "4444445554", "4444445555", "4444445556", "4444445564", "4444445565", "4444445566", "4444445644", "4444445645", "4444445646", "4444445654", "4444445655", "4444445656", "4444445664", "4444445665", "4444445666", "4444446444", "4444446445", "4444446446", "4444446454", "4444446455", "4444446456", "4444446464", "4444446465", "4444446466", "4444446544", "4444446545", "4444446546", "4444446554", "4444446555", "4444446556", "4444446564", "4444446565", "4444446566", "4444446644", "4444446645", "4444446646", "4444446654", "4444446655", "4444446656", "4444446664", "4444446665", "4444446666", "4444454444", "4444454445", "4444454446", "4444454454", "4444454455", "4444454456", "4444454464", "4444454465", "4444454466", "4444454544", "4444454545", "4444454546", "4444454554", "4444454555", "4444454556", "4444454564", "4444454565", "4444454566", "4444454644", …]
*/
``````

Donald Feury

This was a little harder, at least to me it was.

phone_number.go

``````package phonenumbers

// PhoneNeighbors returns all the phone numbers that are adjacent to the given phone number
func PhoneNeighbors(phone string) []string {
numbers := []string{}

if phone == "" {
return numbers
}

runes := []rune(phone)

for i, r := range runes {
// Copy the phone number so we can replace the current digit with its neighbors during the loop
instance := append(runes[:0:0], runes...)

// Get the neighbors of this digit
n := neighbors(r)

// Loop over the neighbors
for _, num := range n {
// Replace the digit at this index with the neighbor and append the result to the slice
instance[i] = num
numbers = append(numbers, string(instance))
}
}

return numbers
}

func neighbors(number rune) []rune {
switch number {
case '0':
return []rune{'8'}
case '1':
return []rune{'2', '4'}
case '2':
return []rune{'1', '3', '5'}
case '3':
return []rune{'2', '6'}
case '4':
return []rune{'1', '5', '7'}
case '5':
return []rune{'2', '4', '6', '8'}
case '6':
return []rune{'3', '5', '9'}
case '7':
return []rune{'4', '8'}
case '8':
return []rune{'0', '5', '7', '9'}
case '9':
return []rune{'6', '8'}
default:
return []rune{}
}
}

``````

phone_number_test.go

``````package phonenumbers

import "testing"

var testCases = []struct {
description string
input       string
expected    []string
}{
{
"empty string",
"",
[]string{},
},
{
"single digit",
"5",
[]string{"2", "4", "6", "8"},
},
{
"many digits",
"4531",
[]string{
"1531",
"5531",
"7531",
"4231",
"4431",
"4631",
"4831",
"4521",
"4561",
"4532",
"4534",
},
},
}

func equal(result []string, expected []string) bool {
if len(result) != len(expected) {
return false
}

for i := range result {
if result[i] != expected[i] {
return false
}
}

return true
}

func TestPhoneNeighbors(t *testing.T) {
for _, test := range testCases {
if result := PhoneNeighbors(test.input); !equal(result, test.expected) {
t.Fatalf("FAIL: %s - PhoneNeighbors(%s): %v - expected: %v", test.description, test.input, result, test.expected)
}
t.Logf("PASS: %s", test.description)
}
}

``````

Mat-R-Such

python

``````def number(n):
if len(n.replace('-','')) >= 1 and len(n.replace('-','')) <= 10:
print(*[n[:len(n)-1]+str(int(n[-1])+1), n[:len(n)-1]+str(int(n[-1])-1)])
else:
print('Error')
``````

SavagePixie • Edited

So... something like this?

``````const neighbours = str => [ (parseInt(str) + 1).toString(), parseInt(str) - 1).toString() ]
``````

``````const neighborNumbers = number => [`\${Number(number)-1}`, `\${Number(number)+1}`];

neighborsNumbers("5555555555") // ["5555555554", "5555555556"]
``````