|
/* |
|
Write a function spell() that takes a keyboard layout as a 2D array and an input string. The function will output a list of instructions that will traverse the keyboard to print the input the string. |
|
|
|
The instructions are up, down, left, right and print. i.e. U, D, L, R and P |
|
|
|
Assume the starting key is the first element in the first row ('q' in the example below) e.g. |
|
|
|
spell( |
|
[ |
|
['q','w','e','r','t','y','u','i','o','p'], |
|
['a','s','d','f','g','h','j','k','l'], |
|
['z','x','c','v','b','n','m'] |
|
], |
|
"hello" |
|
); |
|
|
|
Should output: "DRRRRRPULLLPDRRRRRRPPUP" |
|
*/ |
|
|
|
/** |
|
* Generates instructions to spell a given string on a provided keyboard layout. |
|
* @param {Array} layout - The keyboard layout as a 2D array. |
|
* @param {string} s - The string to spell on the keyboard. |
|
* @returns {string} - The instructions to spell the string on the keyboard. |
|
*/ |
|
function spell(layout, s) { |
|
/* |
|
Remap keyboard layout to unique character codes |
|
[ |
|
['q','w','e','r','t','y','u','i','o','p'], |
|
['a','s','d','f','g','h','j','k','l'], |
|
['z','x','c','v','b','n','m'] |
|
] |
|
=> |
|
[ |
|
[0,1,2,...,9], |
|
[10,11,...,18], |
|
[19,20,...,25] |
|
] |
|
*/ |
|
let charI = 0; |
|
const charMap = {}; |
|
const rowSets = []; |
|
|
|
const charLayout = layout.map((row) => { |
|
const mappedRow = row.map((char) => { |
|
charMap[char] = charI; |
|
return charI++; |
|
}); |
|
rowSets.push(new Set(mappedRow)); |
|
return mappedRow; |
|
}); |
|
|
|
let curRowIndex = 0; |
|
let curColIndex = 0; |
|
let seq = ''; |
|
|
|
// for each given character |
|
for (let sIndex = 0; sIndex < s.length; sIndex++) { |
|
const charCode = charMap[s.charAt(sIndex)]; |
|
|
|
// find row |
|
const targetRowIndex = rowSets.findIndex((rowSet) => rowSet.has(charCode)); |
|
if (targetRowIndex === -1) { |
|
throw new Error(`Character "${s.charAt(sIndex)}" not found in layout`); |
|
} |
|
seq += new Array(Math.abs(targetRowIndex - curRowIndex)) |
|
.fill(targetRowIndex - curRowIndex > 0 ? 'D' : 'U') |
|
.join(''); |
|
curRowIndex = targetRowIndex; |
|
|
|
// find col |
|
const curCode = charLayout[curRowIndex]?.[curColIndex]; |
|
if (curCode == undefined) { |
|
throw new Error(`Invalid current position at row ${curRowIndex}, col ${curColIndex}`); |
|
} |
|
const colDistance = Math.abs(charCode - curCode); |
|
seq += new Array(colDistance).fill(charCode - curCode > 0 ? 'R' : 'L').join(''); |
|
curColIndex += colDistance * (charCode - curCode > 0 ? 1 : -1); |
|
|
|
seq += 'P'; |
|
// request char print |
|
} |
|
|
|
// console.log('seq = ', seq); |
|
return seq; |
|
// solution: 'DRRRRRPULLLPDRRRRRRPPUP'; |
|
} |
|
|
|
// UNIT TESTS |
|
console.assert( |
|
spell( |
|
[ |
|
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'], |
|
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'], |
|
['z', 'x', 'c', 'v', 'b', 'n', 'm'], |
|
], |
|
'hello' |
|
) === 'DRRRRRPULLLPDRRRRRRPPUP', |
|
'Failed unit test' |
|
); |