# Simple string scrambling

Task: Take every 2nd char from the string, then the other chars, that are not every 2nd char, and concat them as new String. Do this n times!

Examples:

"This is a test!", 1 -> "hsi  etTi sats!"
"This is a test!", 2 -> "hsi  etTi sats!" -> "s eT ashi tist!"

Code:

const split = (text) => {
const firstPart = text.split('').filter((element, index) => index % 2 != 0).join('')
const secondPart = text.split('').filter((element, index) => index % 2 == 0).join('')

return firstPart + secondPart
}

const encrypt = (text, n) => {
if (text == null) {
return null
}

let result = text

for (let i = 0; i < n; i++) {
result = split(result)
}

return result
}

const decrypt = (text, n) => {
if (text == null) {
return null
}

for (let i = 0; i < n; i++) {
let firstPartLength = text.length % 2 == 0 ? text.length / 2 : (text.length - 1) / 2 + 1
let secondPartLenght = text.length % 2 == 0 ? text.length / 2 : (text.length - 1) / 2
let firstPart = text.substring(0, firstPartLength)
let secondPart = text.substring(secondPartLenght, text.length)

secondPart = secondPart.split('').map(element => element + ' ').join('')
let position = -1
secondPart = secondPart.split('').map((element, index) => index % 2 != 0 ? firstPart[++position] : element).join('').substring(0, text.length)

text = secondPart
}

return text
}

console.log(decrypt(encrypt('This is a test!', 3), 3))

console.log(encrypt('Some text!', 4))

Here's a declarative take on it:

const is2ndChar = (c, i) => i % 2;
const isNot2ndChar = (c, i) => !(i % 2)

const scramble = s =>
s.split('').filter(is2ndChar)
.concat(s.split('').filter(isNot2ndChar))
.join('');

const test1 = scramble('This is a test!');
const test2 = scramble(scramble('This is a test!'));

console.log(test1, test1 === 'hsi  etTi sats!');
console.log(test2, test2 === 's eT ashi tist!');

The filter-functions are only used once each, so they could just go directly into filter, but it's a little more legible this way.

• -1, declaring functions with fat arrow syntax is stack trace hell, fat arrow are only okay for inline declarations. We should not show this as an alternative. – konijn Aug 22 '17 at 18:05
const split = (text) => (text.replace(/.(.)?/g, '$1') + ("d"+text).replace(/.(.)?/g, '$1')

The regex matches every other char. Do it twice, add any arbitrary char to the start of the string the second time to offset it.

• This could additionally take advantage of arrow functions' syntax for implied return -- just remove the curly brackets and return. – shabs Aug 21 '17 at 20:38
• @shabs - idk, i feel like the other way was more readable, but as you wish.. – I wrestled a bear once. Aug 21 '17 at 20:53
• :P i don't have wishes, just options – shabs Aug 21 '17 at 20:54
1. Modification on the split function should be a little more efficient since you only go through the variable once.
2. Added join function using similar logic as split. Again, should be more efficient.
3. Removed let result = text in the encrypt function and used text. No need to create an extra variable here.

const split = (text) => {
let n = Math.floor(text.length / 2)
return text.split('').reduce((a, v, i) => {
a[i % 2 ? (i - 1) / 2 : n + (i / 2)] = v
return a
}, []).join('')
}

const join = (text) => {
let n = Math.floor(text.length / 2)
return text.split('').reduce((a, v, i) => {
a[i < n ? (i + 1) * 2 - 1 : (i - n) * 2] = v
return a
}, []).join('')
}

const encrypt = (text, n) => {
if (text == null) {
return null
}

for (let i = 0; i < n; i++) {
text = split(text)
}

return text
}

const decrypt = (text, n) => {
if (text == null) {
return null
}

for (let i = 0; i < n; i++) {
text = join(text)
}

return text
}

console.log(decrypt(encrypt('This is a test!', 3), 3))

console.log(encrypt('Some text!', 4))