Reverse string in alternating chunks of size k - javascript

Given a string and an integer k, you need to reverse the first k characters for every segment of length 2k characters counting from the start of the string. If there are less than k characters left, reverse all of them. If there are less than 2k but greater than or equal to k characters, then reverse the first k characters and left the other as original.
Example
Input: s = "abcdefg", k = 2
Output: "bacdfeg"
In the above example, the first chunk of two "ab" was reversed to "ba" and the third chunk of two "ef" was reversed to "fe".
This is my approach:
var reverseStr = function(s, k) {
for (let i = 0; i < k; i++) {
let temp = s[i];
s[i] = s[k - i - 1];
s[k - i - 1] = temp
}
return s
};
console.log(reverseStr("abcdefg", 2))
How do I produce the desired output?

One option is to use a regular expression to match up to k characters, followed by up to 2k characters - then use a replacer function to reverse only the initial k characters:
var reverseStr = function(s, k) {
const pattern = new RegExp(`(.{1,${k}})(.{0,${k}})`, 'g');
return s.replace(pattern, (_, g1, g2) => [...g1].reverse().join('') + g2);
};
console.log(reverseStr("abcdefg", 2))

Strings in JavaScript are immutable, you can't assign to their indexes to modify the string in place. You need to build a new string and return it.
You need a loop for each 2k group. Extract that substring, reverse the first k characters, then concatenate them to the result.
function reverseStr(s, k) {
let result = "";
for (let i = 0; i < s.length; i += 2*k) {
let chunk1 = s.substr(i, k);
// reverse first half
chunk1 = chunk1.split("").reverse().join("");
let chunk2 = s.substr(i+k, k);
result += chunk1 + chunk2;
}
return result;
}
console.log(reverseStr("12345678", 2));

You can realize it in three steps. First separate the string into k group. Then reverse the string of each group at an even index. Last join all the group together.
function reverseStr(s, k) {
return s
.replace(new RegExp('\\w{' + k + '}', 'g'), $2 => $2 + '|')
.split('|')
.map((item, i) =>
i % 2 !== 0
? item
: item
.split('')
.reverse()
.join('')
)
.join('');
}
console.log(reverseStr('abcdefg', 2));

Reverse string in chunks of size k
//make a variable string with some letters to test.
let str = "abcdefg";
//k represents is the number of letters to reverse starting at 0 to position k
let k = 4;
//select the first k letters and split on blankstring, converting to a list
//then reversing that list, collapsing it to a string with join, then
//re-append the original string at position k to the end.
result = (str.substring(0,k).split('')).reverse().join('') + str.substring(k);
console.log({result});
Prints:
{ result: 'dcbaefg' }

Related

Implement the .split method

I need to implement the .split method in my own way without using prebuilt functions. The method should receive a string divided into 2 sentences by a dot and divide them through a separator.
For example, there is this string:
'word wooord wooooooooord wooooooord. wooooooooord woooooord woooord wooooooooord', separator in this case: '. '
The result should be:
['word wooord wooooooooord wooooooord", "wooooooooord woooooord woooord wooooooooord']
I tried to implement it myself, the first problem I encountered is that the words from the string are added character by character to the new array. The second problem is that the output is still a string even though I declared an array earlier.
function split(str, splitter){
let arrSent = []
for (let i = 0; i < str.length; i++){
if (str[i] != splitter){
arrSent += str[i]
}
}
return arrSent
}
console.log(split('word wooord wooooooooord wooooooord. wooooooooord woooooord woooord wooooooooord', '. '))
Since the delimiter can have more than one character, you need a system to upfront collect a sample of characters (of the same length as the delimiter) to be then compared with the delimiter:
const split = (str, delimiter) => {
// If delimiter is empty string just return an array of characters
if (delimiter === "") return [...str];
const len = delimiter.length;
const iter = str.length - len + 1; // max needed iterations
const arr = [""]; // Prefill it with empty string
let idx = 0; // arr insertion pointer
for (let i = 0; i < iter; i++) {
// Collect len chars from str as a sample
// to compare with the delimiter:
let sample = "";
for (let x = i; x < i + len; x++) {
sample += str[x];
}
const isSplit = sample === delimiter;
const isEnded = i === iter - 1;
if (isSplit) {
i += len - 1; // Consume splitted characters
idx += 1; // Increment arr pointer
arr[idx] = ""; // Prepare the new array key as empty string
} else {
// If loop ended append the entire sample.
// Otherwise, append a single character:
arr[idx] += isEnded ? sample : str[i];
}
}
return arr
}
console.log(split("word. etc", ". "));
console.log(split("word. etc. ", ". "));
console.log(split(". word yep. . etc. ", ". "));
console.log(split("word", ". "));
console.log(split("word", "word"));
console.log(split("word", ""));
console.log(split("", ""));
above, idx (starting at 0) is used as the output's arr insertion pointer. The idx is incremented if the sample matches the delimiter. Also, if there's a match, we need to skip iterations i += len, to not include the delimiter in the output array.
To test, create many examples and right before return arr; use console.log(JSON.stringify(arr) === JSON.stringify(str.split(delimiter))); - it should return true for all the submitted tests.

How to find characters of a string that did not changed position after resverse

I want to know how can I print the number of characters of a string that does not change its position after reversal of that string in JavaScript.
Is there any way?
data stream - alphxxdida . After reverse - adidaxxhpla. so here x and a doesn't changed it position. How can I do this ?
Input - alphxxdida
Output-4
A character doesn't change upon reverse if the character in the "mirror image" position is the same character. The "mirror image" position of a character n positions from the start of the string is the character n positions from the end of the string.
let mirroredChars = str => {
let result = [];
let halfLen = Math.ceil(str.length / 2);
let lastIndex = str.length - 1;
for (let i = 0; i < halfLen; i++) {
if (str[i] === str[lastIndex - i]) result.push(str[i]);
}
return result;
};
console.log(mirroredChars('alphxxdida'));
The count is actually slightly unintuitive. We can't simply take 2 * mirroredChars(...).length, since that would imply the number of mirrored characters is always even (and odd counts can occur, in any odd-length input string, since the middle character can always be considered mirrored).
The count will be:
let countMirroredChars = str => {
let numMirrored = mirroredChars(str).length;
return (str.length % 2) // "Is the input string of odd length?"
? (numMirrored * 2 - 1)
: (numMirrored * 2);
};
We can use a bitwise trick to shorten this code. Either of the following work (and the second should deliver better performance, but looks a bit mystical):
let countMirroredChars = str => mirroredChars(str).length * 2 - str.length % 2;
let countMirroredChars = str => mirroredChars(str).length * 2 - str.length & 1;
You can filter over the characters of the string and compare with the character at the corresponding index in the reversed string. The length of the filtered array will be the number of characters that remained the same.
var str = "alphxxdida";
var reversed = [...str].reverse().join('');
const same = [...str].filter((char,i)=>char===reversed[i]);
console.log(same.length);
Of course, you don't actually need the reversed string to perform the filter, as you can calculate the index of the mirrored character.
var str = "alphxxdida";
var same = [...str].filter((char,i)=>char===str[str.length - i - 1]);
console.log(same.length);
Here is with reduce. Traverse till half length of string and compare the chars from both ends of string and count.
Update: As #Gershom pointed out, Fixed to work for odd length of string.
const getCount = (str) =>
[...str.slice(0, str.length / 2)].reduce(
(acc, char, i) => acc + (char === str[str.length - 1 - i] ? 2 : 0),
str.length % 2
);
var str = "alphxxdida";
var str2 = "alphxdida";
console.log(str, getCount(str));
console.log(str2, getCount(str2));

Write a function which takes a sentence as an input and output a sorted sentence. (The answer should be valid for any given input.)

I want to write a function which takes a sentence as an input and output a sorted sentence, and there are two criterias:
Each character of the word should be arranged in alphabetical order.
Words should be arranged in ascending order depending on its character count.
Note: - Word only can have lowercase letters
Example :
Inputs str = "she lives with him in a small apartment"
Output = "a in ehs him hitw eilsv allms aaemnprtt"
Here is my code.
function makeAlphabetSentenceSort(str) {
if (!str || str.length === 0) return 0;
var word = str.split(' ');
for (var j = 0; j < word.length; j++) {
word[j] = word[j].split('').sort().join('');
}
for (var h = 0; h < word.length - 1; h++) {
for (var i = 0; i < word.length - h - 1; i++) {
if (String(word[i]).length > String(word[i + 1]).length) {
var temp = word[i];
word[i] = word[i + 1];
word[i + 1] = temp;
}
}
}
return word.join(' ');
}
makeAlphabetSentenceSort("she lives with him in a small apartment");
Based on the assumption that the output should contain only lowercase letters.
Well if you want to use the built-in functions you could also write that as:
function makeAlphabetSentenceSort(str) {
if (!str) return str;
const nonCharacters = /[^a-z]/g; // to replace any thing other than letters
// We split the sentence to words by any whitespace first
return str.toLowerCase().split(/\s+/).map(word => {
// Here we remove all non-characters from the word
// And sort the remaining characters alphabetically
return word.replace(nonCharacters, '').split('').sort().join('');
// It might be that the sentence looks like:
// "Hey! ???"
// In that case the "word" ??? would become just an empty string
// since all the non-characters have been removed.
// But then you would end up with a result:
// " ehy"
// because the empty word would still get added to the beginning of the sentence
// Because of that we need to filter the empty words out
// And to do that I use this lil trick of mine, using "Boolean"
// as a filter function since Boolean('') is false
// and Boolean('any word') is true
}).filter(Boolean).sort((a, b) => {
// Here we sort all the words by their length
return a.length - b.length;
}).join(' ');
}
console.log(makeAlphabetSentenceSort("Isn't it?"));
console.log(makeAlphabetSentenceSort("she lives with him in a small apartment"));

Having trouble re-indexing my array after looping from the end of the array while adding characters in place

I am trying to solve this particular algorithm question:
You are given a license key represented as a string S which consists only alphanumeric character and dashes. The string is separated into N+1 groups by N dashes.
Given a number K, we would want to reformat the strings such that each group contains exactly K characters, except for the first group which could be shorter than K, but still must contain at least one character. Furthermore, there must be a dash inserted between two groups and all lowercase letters should be converted to uppercase.
Given a non-empty string S and a number K, format the string according to the rules described above.
Example 1:
Input: S = "5F3Z-2e-9-w", K = 4
Output: "5F3Z-2E9W"
Explanation: The string S has been split into two parts, each part has 4 characters.
Note that the two extra dashes are not needed and can be removed.
Example 2:
Input: S = "2-5g-3-J", K = 2
Output: "2-5G-3J"
Explanation: The string S has been split into three parts, each part has 2 characters except the first part as it could be shorter as mentioned above.
Note:
The length of string S will not exceed 12,000, and K is a positive integer.
String S consists only of alphanumerical characters (a-z and/or A-Z and/or 0-9) and dashes(-).
String S is non-empty.
I have written the following code:
const licenseKeyFormatting = (S, K) => {
//convert to array, remove special characters, and capitalize
let s = [...S.replace(/\W/g, '').toUpperCase()]
let pos = 1
//from end of array add '-' for every K
for (let i = s.length - 1; i > 0; i--) {
if (pos === K) {
s.splice(i, 0, '-')
pos = 1
i-- //re-index bc adding to the array
}
pos++
}
return s
}
console.log(licenseKeyFormatting("5F3Z-2e-9-w", 4)) //5F3Z-2E9W
console.log(licenseKeyFormatting("2-5g-3-J", 2)) //2-5G-3J
console.log(licenseKeyFormatting("a-a-a-a-", 1)) // this test case fails should be A-A-A-A, I am getting AAA-A
I am pretty sure the flaw in my logic is due to the re-index, but I can't figure out how to address it.
You should not alter the index. Using splice to insert an element pushes the other elements back, however since you iterate from back to front that doesn't matter. You've already handled the shifted elements.
Another issue is setting pos = 1 in the loop. This is directly followed by pos++. So when pos reaches K the value of pos will be reset to 2 at the end of the loop. Either set pos = 0 (in the loop) so it ends on 1 or move pos++ into the else section.
const licenseKeyFormatting = (S, K) => {
//convert to array, remove special characters, and capitalize
let s = [...S.replace(/\W/g, '').toUpperCase()]
let pos = 1
//from end of array add '-' for every K
for (let i = s.length - 1; i > 0; i--) {
if (pos === K) {
s.splice(i, 0, '-')
pos = 0
}
pos++
}
return s.join("") // <- added join for cleaner console output
}
console.log(licenseKeyFormatting("5F3Z-2e-9-w", 4)) //5F3Z-2E9W
console.log(licenseKeyFormatting("2-5g-3-J", 2)) //2-5G-3J
console.log(licenseKeyFormatting("a-a-a-a-", 1)) // this test case fails should be A-A-A-A, I am getting AAA-A
my way....
function licenseKeyFormatting( S, K )
{
let arr = [...S.replace(/\W/g, '').toUpperCase()]
, p = 0
;
for (let i=arr.length;i--;)
{
p = ++p % K // p = (p+1) % K
if (!p&&i) arr.splice(i,0,'-') // if p===0 and i>0
}
return arr.join('')
}
console.log(licenseKeyFormatting("5F3Z-2e-9-w", 4)) // 5F3Z-2E9W
console.log(licenseKeyFormatting("2-5g-3-J", 2)) // 2-5G-3J
console.log(licenseKeyFormatting("a-a-a-a-", 1)) // A-A-A-A
OR: (more simple)
function licenseKeyFormatting( S, K )
{
let arr = [...S.replace(/\W/g, '').toUpperCase()];
for (let p=arr.length-K;p>0;p-=K) arr.splice(p,0,'-');
return arr.join('');
}
console.log( licenseKeyFormatting("5F3Z-2e-9-w", 4)) // 5F3Z-2E9W
console.log( licenseKeyFormatting("2-5g-3-J", 2)) // 2-5G-3J
console.log( licenseKeyFormatting("a-a-a-a-", 1)) // A-A-A-A

Insert Spaces into string at an index

I'm trying to do this Codewars problem.
Task
In this simple Kata your task is to create a function that turns a string into a Mexican Wave. You will be passed a string and you must return that string in an array where an uppercase letter is a person standing up.
Rules
The input string will always be lower case but maybe empty.
If the character in the string is whitespace then pass over it as if it was an empty seat.
Example
wave("hello") => ["Hello", "hEllo", "heLlo", "helLo", "hellO"]
My code so far is hosted on this repl.it
My thought process is as follows:
Turn argument into array
manipulate each index of the array at index and then readjust previous index to make a wave pattern
turn array into string
reinsert spaces before logging it to console and restarting the loop
I'm pretty stuck and my mind is stuck on how to use
for(var j = 0; j < indexSpaceNumber.length; j++){
//join and add in the spaces at their former index before returning string
strToArray[indexSpaceNumber[j]].slice(0, " ");
}
to insert the spaces into the string.
If there's any guidance or tips it would be much appreciated. I feel like I'm close, but so frustratingly far.
The main idea would be:
Iterate the characters
Replace the character in the original string with an uppercase version
You can use Array.from() to convert the string to an array, and map each item to a new string. If the character is a space return something falsy (en empty string in the example). After the creating the array, filter all falsy values:
const wave = str =>
Array.from(str, (c,i) => // convert the string to an array
// replace the character with an uppercase version in the original string
c === ' ' ?
''
:
`${str.substring(0, i)}${c.toUpperCase()}${str.substring(i + 1)}`
).filter(c => c)
const result = wave("hello")
console.log(result)
For string with spaces
function wave(str) {
let res = []
str.toLowerCase().split('').forEach((v, i) => {
if(v == ' ') return;
res.push( str.substr(0, i) + v.toUpperCase() + str.substr(i + 1) )
});
return res
}
console.log(wave("hello hello"))
I'd go recursive ;)
You know that for a string of length n you need an array of the same length. That's your exit condition.
You can use the length of the array at each iteration to work out the shape of the next string:
hello [] [Hello] 0: uppercase 1st char and append
hello [Hello] [Hello hEllo] 1: uppercase 2nd char and append
hello [Hello hEllo] [Hello hEllo heLlo] 2: uppercase 3rd char and append
...
const wave =
(str, arr = []) =>
str.length === arr.length
? arr
: wave
( str
, [ ...arr
, str.slice(0, arr.length)
+ str[arr.length].toUpperCase()
+ str.slice(arr.length + 1)
]
);
console.log(wave('hello'));
Go over each char in string and build
Slice str from start till current char + current char to upper case + Slice str from current char to end
const wave = str => {
const res = [];
for (let i = 0; i < str.length; i++) {
res.push(`${str.slice(0, i)}${str[i].toUpperCase()}${str.slice(i + 1)}}`);
}
return res;
};
console.log(wave("hi my name is rylan"));
// Alternate way to do with Array.splice
const wave2 = str => {
const res = [];
for (let i in str) {
const temp = Array.from(str);
temp.splice(i, 1, temp[i].toUpperCase());
res.push(temp)
}
return res.map(x => x.join(''));
};
console.log(wave2("hi my name is rylan"));

Categories

Resources