Is there an elegant way to chain join() after a foreach()? - javascript

I am trying to write something like this:
DOMElement
.innerHTML
.toLowerCase()
.split(' ')
.forEach(function(word) {
return word.charAt(0).toUpperCase() + word.slice(1);
})
.join(' ')
Since join needs to receive an array, is there an elegant way to provide it one?

You could replace Array#forEach with Array#map.
The map() method creates a new array with the results of calling a provided function on every element in this array.
.map(function(word) {
return word.charAt(0).toUpperCase() + word.slice(1);
})

Related

Array manipulation error with regex - Kata 6 Codewar

In theory it should transform a given array to camel case. I don't understand what is wrong
function toCamelCase(str){
if(str.length === 0) return ""
let array = str.split(/([_-])/);
array.forEach(word =>{
word == "-" ? word.replace("") : word.charAt(0).toUpperCase()
})
return array
}
The .replace() method doesn't modify the word variable, it instead returns a new modified string. So your code is producing new values within the loop but doesn't do anything with those values. Moreover, word here is a value and not a reference to your array values, so you can't modify them directly from within your forEach() loop and expect it to modify the string values from your array. You instead need to create a new array, with each element transformed, which can be done by using .map() and returning the new value:
function toCamelCase(str) {
const array = str.split(/[_-]/);
return array.map((word, i) => {
return i === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1)
}).join("");
}
console.log(toCamelCase("this-is-some-text"));
Note that you can remove the capturing group from your .split() to remove the _ and - chars from your array so that you don't need to remove them when you map.
Note that for something like this, if you're already using regular expressions in your .split(), you might consider using .replace() with a replacement function, for example, something like:
function toCamelCase(str) {
return str.replace(/-\w/g, ([,m]) => m.toUpperCase());
}
console.log(toCamelCase("this-is-some-text"));
word == "-" ? word.replace("") : word.charAt(0).toUpperCase() is just a ternary statement floating in space. This code isn't altering any variables. It's equivalent to:
if(word == "-"){
word.replace("")
}
else{
word.charAt(0).toUpperCase()
}
Really, you don't need to mess with arrays if you make use of .replace()'s callback function.
function toCamelCase(str) {
// Match underscore or dash followed by a letter, case-insensitively
// Store the letter in a capture group; $1 in this case
return str.replace( /[_-]([a-z])/gi, function( matches ) {
// Uppercase the letter
return matches[ 1 ].toUpperCase()
} );
}
console.log( toCamelCase( 'to-be_Camel_cased' ) );

Javascript - map(function) return this.value - comma on wrong side

I use this code for listing all checked values in the array:
var healthSafety = $('input:checkbox[name=health_safety]:checked').map(function() {
return this.value + (' ');
}).get();
but the problem is, that comma, which separates them falls in the wrong place.
I get something like:
Tripple ladders ,Skylight ,Access to roof
but I want something like this:
Tripple ladders, Skylight, Access to roof.
I tried to put:
return this.value + (' ');
but it didn't helped
Is any way to fix it?
You're relying on implicit join, which puts a comma after each item, but your items (once you update them) end with a space.
Do an explicit join instead with the separator you want:
var healthSafety = $('input:checkbox[name=health_safety]:checked')
.map(function() { return this.value; })
.get()
.join(", ");
or perhaps
const healthSafety = $('input:checkbox[name=health_safety]:checked')
.get()
.map(({value}) => value)
.join(", ");
In your question, you are viewing the default serialization of your array, which only uses a single-character , as a separator. You're trying to add spaces to the separation by adding them to the values.
But you don't want the values to include spaces: that will result in an extra space either at the beginning or ending of the string. Instead, you want to serialize the array with the two-character separator , (comma + space). You can perform this serialization with join:
$('...').map(function() {
return this.value;
}).get().join(", ");
You didn't post the offending code, but somewhere you're stringifying an array, which automatically concatenates all of the elements with a comma:
console.log(['a', 'b', 'c']); //-> "a,b,c"
In your case, you're adding a space to the end of each value, so you're ending up with something like this:
console.log(['value1 ', 'value2 ', 'value3 ']); //-> "value1 ,value2 ,value3 "
What you want to do is remove the extra space as you currently have it and then call .join(', ') on the array - notice the extra space after the comma:
console.log(['value1', 'value2', 'value3'].join(', ')); //-> "value1, value2, value3"
You can join the array with any string:
console.log(['value1', 'value2', 'value3'].join('.:::.')); //-> "value1.:::.value2.:::.value3"

Javascript: Map function weird behaviour

I am using the below code to identify if a character is duplicated, if it is then I replace with a specific char, else another char.
This code works
function dup(str) {
return str
.toLowerCase()
.split("")
.map((index, nonsense, s) => {
console.log(s);
return s.indexOf(index) == s.lastIndexOf(index) ? "(" : ")";
})
.join("");
}
But I do not understand why, the variable 'nonsense' makes it work. If you remove that unused var I get errors.
How can an unused var affect how map works?
The problem is with the order of the arguments and the value at specific locations
function dup(str) {
return str
.toLowerCase()
.split("")
.map((char, index, self) => { // the order is char, index and the current array in the 3rd argument
console.log(self);
return self.indexOf(char) == s.lastIndexOf(char) ? "(" : ")";
})
.join("");
}
If you wish to remove the issue with unused variable (for example, say with eslint) use the _ or prefix with _
function dup(str) {
return str
.toLowerCase()
.split("")
.map((char, _, self) => { // the order is char, index and the current array in the 3rd argument
console.log(self);
return self.indexOf(char) == self.lastIndexOf(char) ? "(" : ")";
})
.join("");
}
EDIT
Though unrelated,a more optimal way to do it would be to use Set to deduplicate.
function dup(str) {
return Array.from(new Set(str
.toLowerCase()
.split(""))
.join("");
}
This has less to do with .map() and more to do with receiving arguments.
The function passed to .map is automatically passed 3 arguments (which you are calling index, nonsense, and s here). In JavaScript, you are not required to specifically capture any of them with argument names, but if you want to use the second or third one, you will need to provide some argument name(s) for the ones you are going to skip over to indicate that you are interested in the third argument.
Having said that, the arguments passed to .map() (in order) are: element, index, array and your names suggest that you believe it's: index, element, array. So a better naming convention would be as shown below:
function dup(str) {
return str
.toLowerCase()
.split("")
.map((char, index, ary) => {
console.log(ary);
return ary.indexOf(index) == ary.lastIndexOf(index) ? "(" : ")";
})
.join("");
}
dup("The quick quick brown fox.");
in your code index represent the current char, nonsense represents the indexOf that char at s array. if you remove nonsense index will be represent the char and s will be represent the indexOf that char. that cause the error because you are trying to do s.indexOf(index) while s is of type number
try to do it like this
function dup(str) {
return str
.toLowerCase()
.split('')
.map(s => {
console.log(s)
return str.indexOf(s) == str.lastIndexOf(s) ? '(' : ')'
})
.join('')
}
From the docs:
var new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array
}[, thisArg])
As you can see, the map function takes up to three params, where the second and third are optional. Your variables index, nonsense and s are filling in for the current value, the actual index and the array. Remove nonsense and your s variable becomes the current index of map, instead of your array.
That is the expected behavior.

Why can't I assign the updated value to variable 'word' here in the for-of loop?

Why can't I update the array 'words' using this for-of loop?
function capitalizeLetters(str) {
let words = str.split(' ');
for(var word of words)
word = word.charAt(0).toUpperCase() + word.slice(1);
return words.join(' ');
}
console.log(capitalizeLetters('i love javascript'));
You can assign to word, but since it's a primitive, and not a pointer to a place in the area, the value is replaced, but it doesn't update the array. You can use Array.map() instead:
function capitalizeLetters(str) {
return str.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}
console.log(capitalizeLetters('i love javascript'));
Another option is to use String.replace() with capture groups for the 1st letter (head) and the rest of the letters (tail) in the word. Then change the case, and combine them again with a string literal:
function capitalizeLetters(str) {
return str.replace(/(\w)(\w+)/g,
(_, head, tail) => `${head.toUpperCase()}${tail.toLowerCase()}`
);
}
console.log(capitalizeLetters('i love javascript'));
Below snippet can update your 'words' array. Hope that is what you were looking for.
function capitalizeLetters(str) {
let words = str.split(' ');
i=0;
for(var word of words){
words[i] = word.charAt(0).toUpperCase() + word.slice(1);
i++;
}
return words.join(' ');
}
console.log(capitalizeLetters('i love javascript'));

begin .map(callback()) from specific index

Is it possible to run a callback function with specific start and stop indexes? I am practicing my JS and am writing a function to convert strings to camel case (from being '-' or '_' seperated) without altering the capitalization of the first word in the string. Basically, after I split the string into an array of words, I want to call .map() and start my callback on the second word in the array.
currently I have:
function toCamelCase(str){
return str.split(/\-|_/).map(word => word.charAt(0).toUpperCase() + word.slice(1)).join('');
}
How can I get .map() to begin at str.split(/\-|_/)[1] ?
In simple words, you can't. .map will iterate over an entire array.
You can chain .map to .slice though
function toCamelCase(str, start, stop){
return str.split(/\-|_/).slice(start, stop).map(word => word.charAt(0).toUpperCase() + word.slice(1)).join('');
}
Array.map() always iterates the entire array. You can use the index (2nd param in callback) to return the word without changes if the index is 0:
function toCamelCase(str){
return str.split(/\-|_/).map((word, i) => i ? word.charAt(0).toUpperCase() + word.slice(1) : word).join('');
}
console.log(toCamelCase('the_camels_toes'));
BTW - you can use a regular expression with String.replace() to to create the camel case:
function toCamelCase(str){
return str.replace(/_(\w)/g, (_, c) => c.toUpperCase());
}
console.log(toCamelCase('the_camels_toes'));

Categories

Resources