1
\$\begingroup\$

CodeWars 6 Kyu: Reverse or rotate?

I wrote my Javascript "hello world" on Thursday, so I'm definitely a JS beginner. I do have some experience in Python though, so I'm not a complete novice to programming in general.

Question

The input is a string str of digits. Cut the string into chunks (a chunk here is a substring of the initial string) of size sz (ignore the last chunk if its size is less than sz).

If a chunk represents an integer such as the sum of the cubes of its digits is divisible by 2, reverse that chunk; otherwise rotate it to the left by one position. Put together these modified chunks and return the result as a string.

If

  • sz is <= 0 or if str is empty return ""
  • sz is greater (>) than the length of str it is impossible to take a chunk of size sz hence return "".

Examples:

revrot("123456987654", 6) --> "234561876549"
revrot("123456987653", 6) --> "234561356789"
revrot("66443875", 4) --> "44668753"
revrot("66443875", 8) --> "64438756"
revrot("664438769", 8) --> "67834466"
revrot("123456779", 8) --> "23456771"
revrot("", 8) --> ""
revrot("123456779", 0) --> "" 
revrot("563000655734469485", 4) --> "0365065073456944"

Solution

function revrot(str, sz) 
{
   ln = str.length;
   if(sz < 1 || !str || sz > ln) return "";

   const test = s => Array.prototype.reduce.call(s, (acc, val) => acc + Number(val) ** 3, 0) % 2 === 0;
   const reverse = s => s.split("").reverse().join("");
   const rotate = s => s.slice(1) + s.slice(0, 1);

   let arr = [];
   for(let i = 0; i < ln; i += sz) arr.push(i+sz <= ln ? str.slice(i, i+sz) : "")
   return arr.map(x => test(x) ? reverse(x) : rotate(x)).join("");
}
\$\endgroup\$
2
\$\begingroup\$

Towards optimization and restructuring

One of downsides of the initial revrot approach is that all nested functions test, reverse and rotate will be redundantly recreated on each revrot call.
Instead - define them as top-level functions with meaningful names.

Next, the program lacks 2 edge cases:

  • when input string contains one single digit and size of required chunk is also 1 (ex. revrot("3", 1)). It's worth to catch that case immediately and return the unchanged input string

  • when input string contains non-digit character

The intermediate arr array with for loop and slice calls (to get all chunks) is replaced with convenient String.match call on dynamic regex pattern new RegExp(`.{${size}}`, 'g') (matches substrings of specific length/size)


The final optimized approach:

const testCubes = s => Array.prototype.reduce.call(s, (acc, val) => acc + Number(val) ** 3, 0) % 2 === 0;
const strReverse = s => [...s].reverse().join("");
const rotateLeft = s => s.slice(1) + s.slice(0, 1);


function revrot(str, size) {
    let len = str.length;
    if (size < 1 || !str || size > len) return "";
    if (len === 1 && size === 1) return str;
    if (/\D/.test(str)) {
        throw Error("Invalid numeric string");
    }
    let pat = new RegExp(`.{${size}}`, 'g');
    return str.match(pat).map(x => 
        testCubes(x) ? strReverse(x) : rotateLeft(x)
    ).join("");
}


console.log(revrot("123456987654", 6));
console.log(revrot("123456987653", 6));
console.log(revrot("66443875", 8));
console.log(revrot("563000655734469485", 4));
console.log(revrot("1", 1));
console.log(revrot("6644-d3875", 8));

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Your answer seams to contradict its self. You say don't nest functions yet you declare one inside map. Anyways modern JS engines cache functions.. There is no overhead associated with nested functions and it is good practice to encapsulate function within the scope they are used in. -1 \$\endgroup\$ – Blindman67 Dec 23 '19 at 22:27
  • \$\begingroup\$ @Blindman67, I don't agree regarding nested functions and don't want wasting my time for such doubtful debates. \$\endgroup\$ – RomanPerekhrest Dec 24 '19 at 5:51
  • \$\begingroup\$ "...doubtful debate.."? This is not a "debate".A performance test is all that is needed to show that moving the functions out makes no difference (a little slower due to longer scope chain lookups). Additionally I down voted because your function, is a good 25% slower than the OP's, throws a un-required error (feature creep) The error is inconsistent revrot("aa",1) throws while revrot("a",1) does not and for ill formed args should be a RangeError. Also and not part of down vote but your edge cases missed the most obvious optimization if (size === 1) { return str } \$\endgroup\$ – Blindman67 Dec 24 '19 at 13:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.