Array elements not mutating inside for of loop - javascript

function rot13(str) {
let alphArr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
let n = 13;
let arr = str.split("");
let len = alphArr.length;
for (let i of arr) {
if (alphArr.includes(i)) {
if (alphArr.indexOf(i) + n <= len - 1) {
i = (alphArr[alphArr.indexOf(i) + n])
console.log(i) // This is as expected
}
}
}
console.log(arr) // Array itself did not mutate and is showing the initial array.
return str;
}
rot13("SERR PBQR PNZC");
The value of i inside the second if statement is proper as can be seen in the console.log statement but the array in itself did not mutate. Why was that?
P.S. I've solved it by using map function and it works properly because map function does not mutate the original array.

It's worth mentioning that your code can be simplified:
let rot = (str, n, asciiStart='A'.charCodeAt(0), asciiEnd='Z'.charCodeAt(0), asciiRange=asciiEnd-asciiStart+1) =>
str.split('')
.map(c => {
let code = c.charCodeAt(0) - asciiStart;
if (code >= 0 && code <= asciiRange) code = (code + n) % asciiRange;
return String.fromCharCode(asciiStart + code);
})
.join('');
let inp = document.getElementsByTagName('input')[0];
let p = document.getElementsByTagName('p')[0];
inp.addEventListener('input', () => p.innerHTML = rot(inp.value, 13));
<input type="text" placeholder="test here (use capital letters)"/>
<p></p>
Your code wasn't working because replacing the value of i does not effect the array index that i was initially based off of. Once you define i, it doesn't remember how it was defined (e.g. it doesn't think to itself, "I originated from a value within an array')

You can't directly set value to element in for of loop try the following...
function rot13(str) {
let alphArr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
let n = 13;
let arr = str.split("");
let len = alphArr.length;
let j=0
for (let i of arr) {
if (alphArr.includes(i)) {
if (alphArr.indexOf(i) + n <= len - 1) {
arr[j]= (alphArr[alphArr.indexOf(i) + n])
console.log(i) // This is as expected
}
}
j++
}
console.log(arr) // Array itself did not mutate and is showing the initial array.
return str;
}
rot13("SERR PBQR PNZC");

Instead of using a for of loop you should use map to create a new array and used the newly mapped array. Fixed fulling working example:
function rot13(str) {
let alphArr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
let n = 13;
let arr = str.split("");
let len = alphArr.length;
arr = arr.map((i) => {
if (alphArr.includes(i)) {
let index = (alphArr.indexOf(i) + n) % len;
return alphArr[index];
}
return i;
});
return arr.join("");
}
console.log(rot13("SERR PBQR PNZC")); // logs "FREE CODE CAMP"

Related

JavaScript How to Create a Function that returns a string with number of times a characters shows up in a string

I am trying to figure out how to make a function that takes a string. Then it needs to return a string with each letter that appears in the function along with the number of times it appears in the string. For instance "eggs" should return e1g2s1.
function charRepString(word) {
var array = [];
var strCount = '';
var countArr = [];
// Need an Array with all the characters that appear in the String
for (var i = 0; i < word.length; i++) {
if (array.indexOf(word[i]) === false) {
array.push(word[i]);
}
}
// Need to iterate through the word and compare it with each char in the Array with characters and save the count of each char.
for (var j = 0; j < word.length; i++) {
for (var k = 0; k < array.length; k++){
var count = 0;
if (word[i] === array[k]){
count++;
}
countArr.push(count);
}
// Then I need to put the arrays into a string with each character before the number of times its repeated.
return strCount;
}
console.log(charRepString("taco")); //t1a1co1
console.log(charRepString("egg")); //e1g2
let str = prompt('type a string ') || 'taco'
function getcount(str) {
str = str.split('')
let obj = {}
for (i in str) {
let char = str[i]
let keys = Object.getOwnPropertyNames(obj)
if (keys.includes(char)) {
obj[char] += 1
} else {
obj[char] = 1
}
}
let result = ''
Object.getOwnPropertyNames(obj).forEach((prop) => {
result += prop + obj[prop]
})
return result
}
console.log(getcount(str))
If the order of the alphanumeric symbols matters
const str = "10zza";
const counted = [...[...str].reduce((m, s) => (
m.set(s, (m.get(s) || 0) + 1), m
), new Map())].flat().join("");
console.log(counted); // "1101z2a1"
Or also like (as suggested by Bravo):
const str = "10zza";
const counted = [...new Set([...str])].map((s) =>
`${s}${str.split(s).length-1}`
).join("");
console.log(counted); // "1101z2a1"
A more clear and verbose solution-
Let m be max number of symbols in charset
Time complexity- O(n log(m))
Space complexity- O(m)
function countFrequencies(str) {
const freqs = new Map()
for (const char of str) {
const prevFreq = freqs.get(char) || 0
freqs.set(char, prevFreq + 1)
}
return freqs
}
function getCountStr(str) {
const freqs = countFrequencies(str)
const isListed = new Set()
const resultArray = []
for (const char of str) {
if (isListed.has(char)) continue
resultArray.push(char)
resultArray.push(freqs.get(char))
isListed.add(char)
}
return resultArray.join("")
}
console.log(getCountStr("egg"))
console.log(getCountStr("taco"))
console.log(getCountStr("10za"))
Using Set constructor, first we will get the unique data.
function myfun(str){
let createSet = new Set(str);
let newArr = [...createSet].map(function(elem){
return `${elem}${str.split(elem).length-1}`
});
let newStr = newArr.join('');
console.log(newStr);
}
myfun('array');

Function that randomizes a string (permutation) in javascript

Is there a way to code a function that gets a string for example "Overflow!" and returns a random Permutation with the first, last and penultimate char staying the same?
Examples of the randomized string could be "Orfevolw!" or "Oervolfw!".
Thank you.
You need to pass the string and the constants number array that you don't want to change.
const generateString = (str, constants = []) => {
const strArray = str.split("");
const result = Array(str.length)
.fill("")
.map((s, i) => {
if (constants.includes(i))
return strArray.splice(i - (str.length - strArray.length), 1);
return "";
});
for (let i = 0; i < result.length; ++i) {
if (!result[i]) {
const random = Math.floor(Math.random() * strArray.length);
result[i] = strArray.splice(random, 1);
}
}
return result.join("");
};
console.log(generateString("Overflow!", [0, 7, 8]));
console.log(generateString("Overflow!"));
Use string.randomize function ( I created this function returns randomly arranged string) on string variable
String.prototype.randomize = function() {
let str = "";
let array = [];
for (var i = 0; i < this.length; i++) {
array.push(this[i])
}
array.sort(() => 0.5 - Math.random());
array.forEach(word => {
str += word
})
return str;
}
let str = "Overflow";
// Calling .randomzie function
str = str.randomize()
// Logging it
console.log(str)

Stuck on a simple recursion exercise, getting "max stack error"

Sum all numbers in an array containing nested arrays
arraySum([1,[2,3],[[4]],5]); // 15
I have written this so far ..
var newArray = array.slice();
newArray = newArray.flat(Infinity);
var sum = 0;
if (newArray.length === 1) {
return array[0];
}
if (newArray.length === 0) {
return 0;
}
for (var i = 0; i < newArray.length; i++) {
if (typeof newArray[i] === "number") {
sum += newArray[i];
}
}
return arraySum(newArray);
};
If I put return sum it works perfect, but the exercise is calling for recursion.
If you want to use recursion, then replace the for loop with returning the value of the first element of the array, plus the sum of the rest of the elements in the array:
const arraySum = (array) => {
var newArray = array.slice();
newArray = newArray.flat(Infinity);
var sum = 0;
if (newArray.length === 1) {
return array[0];
}
if (newArray.length === 0) {
return 0;
}
return array[0] + arraySum(newArray.slice(1));
};
console.log(arraySum([1,[2,3],[[4]],5]));
But the .flat seems weird to be doing on every iteration. Consider doing it only once, in a second default argument that gets passed along:
const arraySum = (arr, arrFlat = arr.flat(Infinity)) => {
return arrFlat.length
? arrFlat[0] + arraySum(arr, arrFlat.slice(1))
: 0;
};
console.log(arraySum([1,[2,3],[[4]],5]));
Or check to see if the current element being iterated over is an array instead of using .flat, to see whether you need to iterate over the subarray with another recursive call to arraySum or just add the number:
const arraySum = (arr) => {
if (!arr.length) return 0;
const rest = arraySum(arr.slice(1));
return typeof arr[0] === 'number'
? arr[0] + rest
: arraySum(arr[0]) + rest;
};
console.log(arraySum([1,[2,3],[[4]],5]));

how to fix the function to check the same random array value

I am learning javascript, I have been able to create a function that has a parameter, the function has the task of forming an array containing a 2 character (0/1) random string of 1 parameter and the return value must be an array.
example:
console.log (generateString(2));
sample results:
['01', '11']
The problem I face is even though it's a random string, but it still has the possibility to have the same value. Suppose I run the program code
console.log (generateString(4));
and one of the results is like this:
['00', '00', '01', '10']
my question is how can I ensure that the return value of the array has no duplicate value? This is my code so far..
function generateString(num){
let newArray = [];
for(let i = 0; i < num; i++){
let randomChar = generateCharacters();
if(i >= 1 && (newArray[i - 1] === randomChar)){
randomChar = generateCharacters();
newArray.push(randomChar);
} else {
newArray.push(randomChar);
}
}
return newArray;
}
function generateCharacters(){
const chars = '01';
let result = '';
for (let j = 2; j > 0; --j){
result += chars[Math.floor(Math.random() * chars.length)];
}
return result;
}
console.log(generateString(4));
Just check for the duplicate before adding the new string.
function generateString(num){
let newArray = [];
let i =0;
while(i<num){
console.log(newArray)
let randomChar = generateCharacters();
if(newArray.indexOf(randomChar)<=-1){
newArray.push(randomChar);
i+=1;
}
}
return newArray;
}
You can use a do-while inside the for-loop and keep making new random strings until the new strings generated is not included in the previous array.
function generateString(num){
let newArray = [];
let randomChar;
for(let i = 0; i < num; i++){
do{
randomChar = generateCharacters();
}
while(newArray.includes(randomChar));
newArray.push(randomChar)
}
return newArray;
}
function generateCharacters(){
const chars = '01';
let result = '';
for (let j = 2; j > 0; --j){
result += chars[Math.floor(Math.random() * chars.length)];
}
return result;
}
console.log(generateString(4));
You can shuffle the array of all 4 possible pairs of digits:
function shuffle(a) {
for (let i = a.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
let temp = a[i];
a[i] = a[j];
a[j] = temp;
}
return a;
}
function generateString(num){
let all = ["00", "01", "10", "11"];
shuffle(all);
return all.slice(0, num); // Only take the number of elements requested
}
console.log(generateString(4));
Made changes in your generateString function. You can use set for not updating duplicates in the result. I think you need to update generateCharacters function to generate all possible strings properly.
function generateString(num){
let newArraySet = new Set();
for(let i = 0; i < num; i++){
let randomChar = generateCharacters();
while(newArraySet.has(randomChar)) {
randomChar = generateCharacters();
}
newArraySet.add(randomChar);
}
return Array.from(newArraySet);
}
function generateCharacters(){
const chars = '01';
let result = '';
for (let j = 2; j > 0; --j){
result += chars[Math.floor(Math.random() * chars.length)];
}
return result;
}
console.log(generateString(4));
When building the array you need to check to see if the random number is not already in the array before adding it to the array. This function will return true if you feed it the array in question and your "random" item you need to check.
function isInArray(myArray, arrayItemToCheck)
{
var found = myArray.find(function(arrayItem) {
return arrayItem == arrayItemToCheck;
});
return !!found
}
in your function, you change the line let randomChar = generateCharacters(); to:
let randomChar;
// loops forever until condition is met
while (true) {
randomChar = generateCharacters();
if (!isInArray(newArray, randomChar)) {
break;
}
}

Common Character Count in Strings JavaScript

Here is the problem:
Given two strings, find the number of common characters between them.
For s1 = "aabcc" and s2 = "adcaa", the output should be 3.
I have written this code :
function commonCharacterCount(s1, s2) {
var count = 0;
var str = "";
for (var i = 0; i < s1.length; i++) {
if (s2.indexOf(s1[i]) > -1 && str.indexOf(s1[i]) == -1) {
count++;
str.concat(s1[i])
}
}
return count;
}
console.log(commonCharacterCount("aabcc", "adcaa"));
It doesn't give the right answer, I wanna know where I am wrong?
There are other more efficient answers, but this answer is easier to understand. This loops through the first string, and checks if the second string contains that value. If it does, count increases and that element from s2 is removed to prevent duplicates.
function commonCharacterCount(s1, s2) {
var count = 0;
s1 = Array.from(s1);
s2 = Array.from(s2);
s1.forEach(e => {
if (s2.includes(e)) {
count++;
s2.splice(s2.indexOf(e), 1);
}
});
return count;
}
console.log(commonCharacterCount("aabcc", "adcaa"));
You can do that in following steps:
Create a function that return an object. With keys as letters and count as values
Get that count object of your both strings in the main function
Iterate through any of the object using for..in
Check other object have the key of first object.
If it have add the least one to count using Math.min()
let s1 = "aabcc"
let s2 = "adcaa"
function countChars(arr){
let obj = {};
arr.forEach(i => obj[i] ? obj[i]++ : obj[i] = 1);
return obj;
}
function common([...s1],[...s2]){
s1 = countChars(s1);
s2 = countChars(s2);
let count = 0;
for(let key in s1){
if(s2[key]) count += Math.min(s1[key],s2[key]);
}
return count
}
console.log(common(s1,s2))
After posting the question, i found that i havent looked the example well. i thought it wants unique common characters ..
and i changed it and now its right
function commonCharacterCount(s1, s2) {
var count = 0;
var str="";
for(var i=0; i<s1.length ; i++){
if(s2.indexOf(s1[i])>-1){
count++;
s2=s2.replace(s1[i],'');
}
}
return count;
}
Create 2 objects containing characters and their count for strings s1
and s2
Count the common keys in 2 objects and return count - Sum the common keys with minimum count in two strings
O(n) - time and O(n) - space complexities
function commonCharacterCount(s1, s2) {
let obj1 = {}
let obj2 = {}
for(let char of s1){
if(!obj1[char]) {
obj1[char] = 1
} else
obj1[char]++
}
for(let char of s2){
if(!obj2[char]) {
obj2[char] = 1
} else
obj2[char]++
}
console.log(obj1,obj2)
let count = 0
for(let key in obj1 ){
if(obj2[key])
count += Math.min(obj1[key],obj2[key])
}
return count
}
I think it would be a easier way to understand. :)
function commonCharacterCount(s1: string, s2: string): number {
let vs1 = [];
let vs2 = [];
let counter = 0;
vs1 = Array.from(s1);
vs2 = Array.from(s2);
vs1.sort();
vs2.sort();
let match_char = [];
for(let i = 0; i < vs1.length; i++){
for(let j = 0; j < vs2.length; j++){
if(vs1[i] == vs2[j]){
match_char.push(vs1[i]);
vs2.splice(j, 1);
break;
}
}
}
return match_char.length;
}
JavaScript ES6 clean solution. Use for...of loop and includes method.
var commonCharacterCount = (s1, s2) => {
const result = [];
const reference = [...s1];
let str = s2;
for (const letter of reference) {
if (str.includes(letter)) {
result.push(letter);
str = str.replace(letter, '');
}
}
// ['a', 'a', 'c'];
return result.length;
};
// Test:
console.log(commonCharacterCount('aabcc', 'adcaa'));
console.log(commonCharacterCount('abcd', 'aad'));
console.log(commonCharacterCount('geeksforgeeks', 'platformforgeeks'));
Cause .concat does not mutate the string called on, but it returns a new one, do:
str = str.concat(s1[i]);
or just
str += s1[i];
You can store the frequencies of each of the characters and go over this map (char->frequency) and find the common ones.
function common(a, b) {
const m1 = {};
const m2 = {};
let count = 0;
for (const c of a) m1[c] = m1[c] ? m1[c]+1 : 1;
for (const c of b) m2[c] = m2[c] ? m2[c]+1 : 1;
for (const c of Object.keys(m1)) if (m2[c]) count += Math.min(m1[c], m2[c]);
return count;
}

Categories