Running into error creating a Javascript function - javascript

I am trying to write a function to change a string written in Snake Case to Camel Case, but running into an error.
function snakeToCamel(string) {
arr = [...string];
for (i of arr) {
if (i === "_") {
let upperCaseLetter = arr[i+1].toUpperCase();
arr.splice(i+1,1,upperCaseLetter);
arr.splice(i,1)
}
};
return arr;
}
The error is here. I can't find what is wrong in the line stated in the error. What is going on?
snakeToCamel("you_dont_know")
snake-to-camel.js:5 Uncaught TypeError: Cannot read property 'toUpperCase' of undefined
at snakeToCamel (snake-to-camel.js:5)
at <anonymous>:1:1

In a for-of loop, the control variable is the array element, not an index. So i in your example is a string. So arr[i+1].toUpperCase(); will do string concatenation and try to look up a property with a name like s1, not 1.
If you want to use the index, you want a for loop or a forEach call (or even map, since that's kind of what you're doing), not a for-of loop.
A couple of other notes:
You need to be sure to declare your variables; right now, your code is falling prey to what I call The Horror of Implicit Globals, creating a global called arr. Add const or let before arr.
You don't put ; after blocks attached to control-flow statements. (You can have them there, because empty statements are allowed, but they're not supposed to be there.)
For example, using a for loop:
function snakeToCamel(string) {
// `const` because we never reassign `arr`
const arr = [...string];
// Traditional `for` so we have the index
for (let i = 0, len = arr.length; i < len; ++i) {
const ch = arr[i];
if (ch === "_") {
if (i === len - 1) {
// Trailing _, just remove it
arr.splice(i, 1);
--i;
--len;
} else {
let upperCaseLetter = arr[i + 1].toUpperCase();
// You can remove the _ and the lowr case letter
// in a single `splice` rather than multiple ones
arr.splice(i, 2, upperCaseLetter);
--i; // Need to do this to allow for multiple `_` in a row
--len;
}
}
};
return arr;
}
console.log(snakeToCamel("one_two__three_"));
Or using map:
function snakeToCamel(string) {
let lastWasUnderscore = false;
const result = [...string]
.map(ch => {
const thisIsUnderscore = ch === "_";
if (lastWasUnderscore && !thisIsUnderscore) {
lastWasUnderscore = false;
return ch.toUpperCase(); // or `.toLocaleUpperCase()`
}
lastWasUnderscore = thisIsUnderscore;
return thisIsUnderscore ? null : ch;
})
.filter(ch => ch !== null);
return result;
}
console.log(snakeToCamel("one_two__three_"));

You were actually fairly close to the solution!
Instead of using a for-of loop I would suggest you use a normal for loop.
This way you can access the index i
function snakeToCamel(string) {
const arr = [...string];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === "_") {
let upperCaseLetter = arr[i+1].toUpperCase();
arr.splice(i+1,1,upperCaseLetter);
arr.splice(i,1)
}
}
return arr.join("");
}

Here's the code I wrote. (needs to be refined for DRY principle)
function STC(string) {
const arr = [...string];
let output = '';
for (const letter of arr) {
if (letter == '_') {
output += arr[arr.indexOf(letter)+1].toUpperCase()
arr.splice(arr.indexOf(letter), 1)
} else {
output += letter;
}
}; return output;
}
I just add to the string instead of an array. I also delete the underscore. Tell me if it works.

function snakeToCamel(string) {
const arr = [...string];
arr.forEach((v, i) => {
if (v === "_") {
arr.splice(i+1,1, arr[i+1].toUpperCase());
arr.splice(i,1)
}
});
return arr.join("");
}
const result = snakeToCamel('a_b_c');
console.log(result);

Related

How to remove only one of repeated chars in string using JavaScript

I have a string with repeated chars like : 'CANADA'.
And I am trying to get the string which removed only one of repeated chars :
'CNADA', 'CANDA', 'CANAD'.
I've tried it with subString, but it returned the part of string removed.
Also I've tried it with reduce, but it ended up removing all the repeated chars ('CND').
What is the way of removing only one char at time?
The results can be stored in array. (results = ['CNADA', 'CANDA', 'CANAD'])
Thank you.
You can achieve this by utilizing the second parameter of String#indexOf() which specifies the position from which to start the search. Here in a while loop, and using a Set to remove dulplicates before returning.
function getReplaceOptions(str, char) {
let res = [], i = str.indexOf(char, 0);
while (i !== -1) {
res.push(str.substring(0, i) + str.substring(++i));
i = str.indexOf(char, i)
}
return Array.from(new Set(res))
}
console.log(getReplaceOptions('CANADA', 'A'));
console.log(getReplaceOptions('Mississippi', 's'));
You can first count all the occurrences in the string. Later you can iterate over the script and if the count is greater than 1 you can remove that character.
const theString = 'CANADA'
const letterCount = {}
const resultArr = []
for (var i = 0; i < theString.length; i++) {
const theLetter = theString.charAt(i)
if(letterCount[theLetter]){
letterCount[theLetter] = letterCount[theLetter] + 1
}
else{
letterCount[theLetter] = 1
}
}
console.log(letterCount)
for (var i = 0; i < theString.length; i++) {
const theLetter = theString.charAt(i)
if(letterCount[theLetter] && letterCount[theLetter] > 1){
resultArr.push(theString.substr(0, i) + theString.substr(i + 1))
}
}
console.log(resultArr)
If you want to remove only the first repeating character then you can use matchAll here as:
Just see the browser compatibility before using this
const str = 'CANADA';
const firstRepeatedChar = 'A';
const result = [];
for (let { index } of str.matchAll(firstRepeatedChar)) {
result.push(str.slice(0, index) + str.slice(index + 1));
}
console.log(result);
NOTE: If you want to search for the first repeating character then remove it, then you can do as:
const str = 'CANADA';
let firstRepeatedChar = '';
const set = new Set();
for (let i = 0; i < str.length; ++i) {
if (!set.has(str[i])) {
set.add(str[i]);
} else {
firstRepeatedChar = str[i];
break;
}
}
const result = [];
for (let { index } of str.matchAll(firstRepeatedChar)) {
result.push(str.slice(0, index) + str.slice(index + 1));
}
console.log(result);
You could use some Array magic to remove duplicate characters:
function removeDuplicateCharacters(value) {
// convert string to array and loop through each character
return String(value).split('').filter(function(char, index, all) {
// return false if char found at a different index
return (index === all.indexOf(char));
})
.join(''); // convert back to a string
}
// returns `CAND`
removeDuplicateCharacters('CANADA');
// returns `helo wrd`
removeDuplicateCharacters('hello world');

Finding both characters and putting them in a new array

I've created a function that passes in a string and a character. The string is saturday and the character is a.
I want the result to be an array that contains all the index numbers of where the letter 'a' sits in saturday.
Then array ends up with only [1]. So it finds the first a sitting at the second index. I tested this by changing saturday to soturday and the console prints the array with [6].
I want the result to be [1, 6]
I've tried putting the return result outside the next set of {} braces but no joy.
const subLength = (str, cha) => {
let result = [];
for (let i = 0; i < str.length; i++) {
if (str.charAt(i) === cha) {
result.push(str.indexOf(cha));
return result;
}
}
};
console.log(subLength('Saturday', 'a'));
2 small problems with your code
The return statement is in the for loop. The first time the loop hits that your loop will stop and the function will return. This is why you are only getting 1 result. Move the return outside the loop.
Once the above is fixed you will realize that your array will now return [1, 1]. This is because str.indexOf(cha) will always return 1 since it's returning the index of the first a. To fix this, you should be appending the index i to your array instead since it represents the index of the current char.
const subLength = (str, cha) => {
let result = [];
for (let i = 0; i < str.length; i++) {
if (str.charAt(i) === cha) {
result.push(i);
}
}
return result;
};
console.log(subLength('Saturday', 'a'));
You are pretty close.
In your code the return is being executed as soon as a match is found. You want to return after you've checked every char - so I've moved it after the foreach.
indexof has a second parm, which defines the char to start looking from. If you omit it, you will get the index of the first match every time - which is not what you want.
const subLength = (str, cha) => {
let result = [];
for(let i = 0; i < str.length; i++){
if(str[i] === cha) {
result.push(str.indexOf(cha, i));
}
}
return result;
};
console.log(subLength('Saturday', 'a'));
Room for improvement
Since you are iterating over every char anyways, you can simply store every i where str[i] matches cha.
So optimized:
const subLength = (str, cha) => {
let result = [];
for(let i = 0; i < str.length; i++){
if(str[i] === cha) {
result.push(i);
}
}
return result;
};
console.log(subLength('Saturday', 'a'));
An even simpler version using regex:
const subLength = (str, cha) => {
return [...str.matchAll(new RegExp(cha, 'g'))].map(e => e.index);
};
console.log(subLength('Saturday', 'a'));
How about putting the return result; outside of the for loop?
Something like this should work, if it is case-sensitive
const subLength = (str, cha) => {
const chaArr = str.split('');
const result = [];
chaArr.forEach((v, i) => {
if (v === cha) result.push(i);
})
return result
};
console.log(subLength('Saturday', 'a'));

Solving a Permutations problem with Heap's Algorithm in Javascript

I'm working through some "Kata's" on CodeWars.com, and am stuck on the Permutations problem.
Here is the problem: n this kata you have to create all permutations
of an input string and remove duplicates, if present. This means, you
have to shuffle all letters from the input in all possible orders.
Examples:
permutations('a'); // ['a']
permutations('ab'); // ['ab', 'ba']
permutations('aabb'); // ['aabb', 'abab', 'abba', 'baab', 'baba', 'bbaa']
The order of the permutations doesn't matter.
Here is my solution:
function permutations(string) {
const swap = (string, x, y) => {
const stringArray = string.split('')
const swapVar = stringArray[x]
stringArray[x] = stringArray[y]
stringArray[y] = swapVar
return stringArray.join('')
}
const permutate = (k, arr) => {
if (k === 1) {
return arr
} else {
for (let i = 0; i < k - 1; i++) {
if (k % 2 === 0) {
arr.push(swap(string, i, k-1))
} else {
arr.push(swap(string, 0, k-1))
}
}
permutate(k - 1, arr)
}
}
return permutate(string.length, [string])
}
When you pass in a single letter, it works fine. Two letters and it returns undefined. I've console logged the if statement block with the return and it should be returning the correct answer, but the result is still undefined. Considering it's getting the correct answer in the if statement and isn't progressing into the else block, I'm at a loss for why this isn't working.
Thank you in advance!
I figured it out - I was missing the return statement before the recursive function call.
Here is a basic solution
String.prototype.replaceAt = function(index, replacement) {
return this.substr(0, index) + replacement + this.substr(index +
replacement.length);}
var words = [];
var string = "lyes";
for(var i = 0;i< string.length;i++){
for(var j = 0;j<string.length;j++){
var tempChar;
if(i!==j){
tempChar = string[j]
var str = string.replaceAt(j,string[i])
str = str.replaceAt(i,tempChar)
if(!words.includes(str)){
words.push(str)
console.log(str)
}
}
}
}
console.log(words.length +" words found")

A function that adds a string ending to each member in an array

I want to create a JS function that adds a string ending to each member in an array. The expected function when called should return something like this:
addEnding(["clever", "meek", "hurried", "nice"], "ly")
Expected output:
["cleverly", "meekly", "hurriedly", "nicely"]
Thanks for all answers suggesting to use map method, but I want to use a for loop, here is what my code looks like:
const addEnding = (arr, str) => {
let result = '';
for (i = 0; i < arr.length; i++) {
result = arr[i]
result += str
}
return result
}
console.log(addEnding(["new", "pander", "scoop"], "er"))
The issue is that it only returns the first element of an array, when I expect it to return all elements from array + str ending
You declared the result as a string and replace it over the iteration. Just change the data type to array as you expected.
const addEnding = (arr, str) => {
let result = '' // You initiated a string as a result not an array
for(i=0; i<arr.length; i++) {
result = arr[i] // The string got replaced every time it interate
result += str
}
return result // The result is always the last item
}
console.log(addEnding(["new", "pander", "scoop"], "er"))
const arr =["new", "pander", "scoop"]
const str = 'er'
const addEnding = (arr, str) => {
let result = []; // Init an array as format output
for(i=0; i<arr.length; i++) {
result[i] = arr[i] + str // assign like this
}
return result
}
console.log(addEnding(arr, str))
use map function, which will iterate over your array and return a new array with the modified version of each.
Using string interpolation you can append easily the suffix
function addEnding(arr, end) {
return arr.map(word => `${word}${end}`);
}
console.log(addEnding(["clever", "meek", "hurried", "nice"], "ly"))
function addSuffix(arr, suff){
var newArr=[];
for (var i = 0; i < arr.length; i++)
newArr.push(arr[i] + suff);
return newArr;
}
Try to the following code
function addEnding(array, suffix) {
if (Array.isArray(array)) {
for (var i = 0; i < array.length; i++) {
array[i] = array[i] + suffix;
}
}
return array;
}
console.log(addEnding(["clever", "meek", "hurried", "nice"], "ly"));
You can use Array#map along with String#concat
function addEnding(words, suffix) {
return words.map(word => word.concat(suffix))
}
const output = addEnding(["clever", "meek", "hurried", "nice"], "ly");
console.log(output);
If you are looking to use for loop and want to do this in a functional way (the way you choose) then you can try like this.
I have executed the statements on Node REPL.
Note » Using map() method returns a new array, it does not modify the existing one. It is better to use for/forEach loops if you are looking to update the exisiting array.
Your mistake: In your case the little mistake you did is » you used result variable and updated that. Use arr[i] = arr[i] + str.
> const addEnding = (arr, str) => {
... arr.forEach((item, i) => {
..... arr[i] = item + str;
..... })
... return arr
... }
undefined
>
> addEnding(["clever", "meek", "hurried", "nice"], "ly")
[ 'cleverly', 'meekly', 'hurriedly', 'nicely' ]
>
> addEnding(["new", "pander", "scoop"], "er")
[ 'newer', 'panderer', 'scooper' ]
>
const addEnding = (arr, str) => {
let result = []
for(i = 0; i < arr.length; i++){
result[i] = arr[i] + str
}
return result
}
function wordConverter(arr, str) {
return arr.map(x => x.concat(str));
}
console.log(wordConverter(['clean', 'dye', 'dry', 'call'], 'ing'));
Use map which will return a new array of words formed by concatenating words from original array and the suffix
function addEnding(array, suffix) {
// assuring input is array otherwise map will not work
if (Array.isArray(array)) {
return array.map(item => item + suffix)
}
}
console.log(addEnding(["clever", "meek", "hurried", "nice"], "ly"))
Use Array.prototype.map()
const addEnding = (array, suffix) => array.map(word => `${word}${suffix}`);
const result = addEnding(["clever", "meek", "hurried", "nice"], "ly");
console.log(result);
You can do it with the below function if you want to use the traditional for loop.
function addEnding(arr, end) {
for(let i=0; i<= arr.length-1; i++)
arr[i] += end;
return arr;
}

fastest way to detect if duplicate entry exists in javascript array?

var arr = ['test0','test2','test0'];
Like the above,there are two identical entries with value "test0",how to check it most efficiently?
If you sort the array, the duplicates are next to each other so that they are easy to find:
arr.sort();
var last = arr[0];
for (var i=1; i<arr.length; i++) {
if (arr[i] == last) alert('Duplicate : '+last);
last = arr[i];
}
This will do the job on any array and is probably about as optimized as possible for handling the general case (finding a duplicate in any possible array). For more specific cases (e.g. arrays containing only strings) you could do better than this.
function hasDuplicate(arr) {
var i = arr.length, j, val;
while (i--) {
val = arr[i];
j = i;
while (j--) {
if (arr[j] === val) {
return true;
}
}
}
return false;
}
There are lots of answers here but not all of them "feel" nice... So I'll throw my hat in.
If you are using lodash:
function containsDuplicates(array) {
return _.uniq(array).length !== array.length;
}
If you can use ES6 Sets, it simply becomes:
function containsDuplicates(array) {
return array.length !== new Set(array).size
}
With vanilla javascript:
function containsDuplicates(array) {
return array
.sort()
.some(function (item, i, items) {
return item === items[i + 1]
})
}
However, sometimes you may want to check if the items are duplicated on a certain field.
This is how I'd handle that:
containsDuplicates([{country: 'AU'}, {country: 'UK'}, {country: 'AU'}], 'country')
function containsDuplicates(array, attribute) {
return array
.map(function (item) { return item[attribute] })
.sort()
.some(function (item, i, items) {
return item === items[i + 1]
})
}
Loop stops when found first duplicate:
function has_duplicates(arr) {
var x = {}, len = arr.length;
for (var i = 0; i < len; i++) {
if (x[arr[i]]) {
return true;
}
x[arr[i]] = true;
}
return false;
}
Edit (fix 'toString' issue):
function has_duplicates(arr) {
var x = {}, len = arr.length;
for (var i = 0; i < len; i++) {
if (x[arr[i]] === true) {
return true;
}
x[arr[i]] = true;
}
return false;
}
this will correct for case has_duplicates(['toString']); etc..
var index = myArray.indexOf(strElement);
if (index < 0) {
myArray.push(strElement);
console.log("Added Into Array" + strElement);
} else {
console.log("Already Exists at " + index);
}
You can convert the array to to a Set instance, then convert to an array and check if the length is same before and after the conversion.
const hasDuplicates = (array) => {
const arr = ['test0','test2','test0'];
const uniqueItems = new Set(array);
return array.length !== uniqueItems.size();
};
console.log(`Has duplicates : ${hasDuplicates(['test0','test2','test0'])}`);
console.log(`Has duplicates : ${hasDuplicates(['test0','test2','test3'])}`);
Sorting is O(n log n) and not O(n). Building a hash map is O(n). It costs more memory than an in-place sort but you asked for the "fastest." (I'm positive this can be optimized but it is optimal up to a constant factor.)
function hasDuplicate(arr) {
var hash = {};
var hasDuplicate = false;
arr.forEach(function(val) {
if (hash[val]) {
hasDuplicate = true;
return;
}
hash[val] = true;
});
return hasDuplicate;
}
It depends on the input array size. I've done some performance tests with Node.js performance hooks and found out that for really small arrays (1,000 to 10,000 entries) Set solution might be faster. But if your array is bigger (like 100,000 elements) plain Object (i. e. hash) solution becomes faster. Here's the code so you can try it out for yourself:
const { performance } = require('perf_hooks');
function objectSolution(nums) {
let testObj = {};
for (var i = 0; i < nums.length; i++) {
let aNum = nums[i];
if (testObj[aNum]) {
return true;
} else {
testObj[aNum] = true;
}
}
return false;
}
function setSolution(nums) {
let testSet = new Set(nums);
return testSet.size !== nums.length;
}
function sortSomeSolution(nums) {
return nums
.sort()
.some(function (item, i, items) {
return item === items[i + 1]
})
}
function runTest(testFunction, testArray) {
console.log(' Running test:', testFunction.name);
let start = performance.now();
let result = testFunction(testArray);
let end = performance.now();
console.log(' Duration:', end - start, 'ms');
}
let arr = [];
let setSize = 100000;
for (var i = 0; i < setSize; i++) {
arr.push(i);
}
console.log('Set size:', setSize);
runTest(objectSolution, arr);
runTest(setSolution, arr);
runTest(sortSomeSolution, arr);
On my Lenovo IdeaPad with i3-8130U Node.js v. 16.6.2 gives me following results for the array of 1,000:
results for the array of 100,000:
Assuming all you want is to detect how many duplicates of 'test0' are in the array. I guess an easy way to do that is to use the join method to transform the array in a string, and then use the match method.
var arr= ['test0','test2','test0'];
var str = arr.join();
console.log(str) //"test0,test2,test0"
var duplicates = str.match(/test0/g);
var duplicateNumber = duplicates.length;
console.log(duplicateNumber); //2

Categories

Resources