I am working on the following codewars problem. The question looks like this:
Instructions
Write a function capitals that takes a single string (word) as
argument. The functions must return an ordered list containing the
indexes of all capital letters in the string.
Example
Test.assertSimilar( capitals('CodEWaRs'), [0,3,4,6] );
And my solution is:
function capitals(word){
var ara = []
for(var i = 0; i < word.length; i++){
if(word[i] == word[i].toUpperCase()){
ara.push(word.indexOf(word[i]))
}
}
return ara
}
The code works fine whenever I pass a string to it. The only problem I have is I am getting the same index for a repeated spelling. for instance, capitals("HeLLo") returns [0, 2, 2] instead of [0, 3, 4].
Is there any way to fix this?
word.indexOf(word[i]) returns the first index, you should simply push i onto the array.
Say this
ara.push(i)
instead of
ara.push(word.indexOf(word[i]))
When you will say word.indexOf(word[i])) it will return the very first index where it get the word[i]. So for both L it's giving 2.
You may find the solutions above satisfactory but I thought I would provide a more functional approach in ES2015.
const capitals = word => word.split('') // split word into an array
.map((letter, idx) => letter === letter.toUpperCase() ? idx : false)
// return a new array that gives the index of the capital letter
.filter(num => Number.isInteger(num));
// filter through each item in the new array and return
// a newer array of just the index values
Related
I see this similar algorithm was posted on stackoverflow, nevertheless I cannot understand, so I decided to post once more.
function capitalizeFirst(arr) {
if (arr.length === 1) {
return [arr[0].toUpperCase()]
}
let res = capitalizeFirst(arr.slice(0, -1))
res.push(arr.slice(arr.length - 1)[0].toUpperCase())
return res
}
console.log(capitalizeFirst(['dog', 'car', 'horse']))
Things I do not understand...
Why it is inside square brackets return [arr[0].toUpperCase()]
why not just return arr[0].toUpperCase()
Why "arr" is getting sliced twice:
here
let res = capitalizeWords(arr.slice(0,-1)
and here
res.push(arr.slice(arr.length-1)[0].toUpperCase())
Overall, I am lost, please help
I see that the OP wants to explain some found code. First, it's not very good code. The function can be restated in a couple easy to read lines.
Here's the not-so-good code annotated (comments *in stars* answer the specific OP questions)
function capitalizeWords(arr) {
// this is the degenerate case: a single item array
if (arr.length === 1) {
return [arr[0].toUpperCase()] // return a *single item array* with the one element capitalized
// incidentally, toUpperCase capitalizes all letters, not only the first, as stated in the OP title
}
// here, length must be zero or > 1. If zero, the remaining code will fail, indexing past 0
// otherwise, if length > 1, this code will run the function on the array minus
// the last element it will return an array (see above) for that last element
let res = capitalizeWords(arr.slice(0, -1))
// this says capitalize the last element.
// it's super clumsy, grabbing the last element by *slicing the array again* just before the end,
// getting that one element from the slice, and using with toUpperCase
// then pushing that uppercase result onto the result array
res.push(arr.slice(arr.length - 1)[0].toUpperCase())
return res
}
Here's a cleanup. First, isolate the capitalization logic and get that tested and correct. It will look like this:
const capitalizeWord = word => word[0].toUpperCase() + word.slice(1);
Next, realize that the most degenerate (elemental) case is capitalizing an empty array. The result of capitalizing an empty array is an empty array.
// something like
return !arr.length ? [] : // ... recursion will go here
When recursing with arrays, we generally say: "do something with the first element, and do the function with the rest of the elements". In JS, it's much more elegant to refer to the "first and rest" than to "all but the last and the last".
// first element (after we've established > 0 length)
arr[0]
// the rest of the elements
arr.slice(1)
Putting this all together...
const capitalizeWord = word => word[0].toUpperCase() + word.slice(1);
function capitalizeWords(arr) {
return arr.length ? [ capitalizeWord(arr[0]), ...capitalizeWords(arr.slice(1))] : [];
}
console.log(capitalizeWords(['dog', 'car', 'horse']))
I would forget about what that code does and concentrate on the steps you need to take to make your function work.
Recursive - so the function needs to call itself but you need to find a way to identify which element you're working on.
You need a way to break out of the recursion when you reach the end of the array.
You need a way to separate out the first letter of an element from all the rest, and update the element with a transformed string.
Here's how I might approach it.
// Pass in the array, and initialise an index
// variable
function capitalizeFirst(arr, index = 0) {
if (!arr.length) return 'Empty array';
// If we're at the end of the array
// return the array
if (index === arr.length) return arr;
// If the element is not empty
if (arr[index].length) {
// Get the first letter, and place all
// the other letters in an array called `rest`
// You can use destructuring here because strings
// are iterable
const [first, ...rest] = arr[index];
// Update the element at the current index
// with the new string making sure you join up `rest`
arr[index] = `${first.toUpperCase()}${rest.join('')}`;
}
// Call the function again increasing the index
return capitalizeFirst(arr, ++index);
}
console.log(capitalizeFirst(['dog', 'car', 'horse']));
console.log(capitalizeFirst([]));
console.log(capitalizeFirst(['dog', '', 'horse']));
console.log(capitalizeFirst(['dog', 'o', 'horse']));
Additional documentation
Destructuring assignment
Rest parameters
Template/string literals
your confusion code
1.let res = capitalizeWords(arr.slice(0,-1)
2.res.push(arr.slice(arr.length-1)[0].toUpperCase())
1.res is an variable array . when this line of code will run let res = capitalizeWords(arr.slice(0,-1)) that means first thing will be done is from your array ['dog', 'car', 'horse'] it will take out the first item that is "dog" and after capitalizeWords function will run and inside capitalizeWords function the argument passed from res is "dog" . and when the function will run if block will run because now arr has one element that is ["dog"] and that will be converted to ["DOG"] . and as like this ['car', 'horse'] this 2 elements will be converted to capital .
but it is a bit complex code to understand as a beginner.
so,you can use my simplify code . i hope you can understand this easily !!
function capitalizeWords(arr) {
if(arr.length === 1) {
return [arr[0].toUpperCase()]
}
let res = []
for (let i of arr){
res.push(i.toUpperCase())
}
return res
}
console.log(capitalizeWords(['dog', 'car', 'horse']))
your another confusion is
return [arr[0].toUpperCase()]
if you write return arr[0].toUpperCase() that means arr[0]="dog" (its a string not an array) . if you just want to print it as a string then you can write arr[0].toUpperCase() :"dog" but if you want to console it as an array then you have to write this : [arr[0].toUpperCase()] :["dog"]
Update
Added some input checking:
if (array.length < 1) return `ERROR Empty Array`;
// Return error message if input is an empty array
if (typeof str === "string" && /[a-z]/.test(str.charAt(0))) {...
/**
* Ignore all non-string data and any string that doesn't start with
* a lower case letter
*/
The code in OP doesn't capitalize each word in an array, it capitalizes every letter of each word. I honestly didn't really try to figure out what's exactly wrong because there's no recursion in the OP anyhow.
Recursion
A function that calls itself within the function (which is itself).
A base condition must be met in order for the function to call itself.
The parameters should change upon each recurse.
The function will cease calling itself once the base condition is no longer true.
In the OP, there's no base condition (see Recursion 2).
In the following example is a recursive function that capitalizes each word of an array.
Pass in the array and index (if index is undefined it defaults to 0)
function capWords(array, index = 0) {...
// array = ["dog", "cat', 'bird'], index = 0
Find the word from the array at the index
let str = array[index];
// str = 'dog'
Get the first letter of that word and capitalize it
let cap = str.charAt(0).toUpperCase();
// cap = "D"
Then concatenate cap to the rest of that word and then reassign the new word to the array at index
array[index] = cap + str.slice(1);
// array = ['Dog', 'cat', 'bird']
If index is less than the length of the array -1...
if (index < array.length - 1) {...
/**
* See Recursion 2
* index = 0, array.length -1 = 2
*/
...return and call capWords(array, index + 1)...
return capWords(array, index + 1)
/**
* See Recursion 1
* array = ['Dog', 'cat', 'bird'], index = 1
* See Recursion 3
*/
...otherwise return array
return array
/**
* See Recursion 4
* After calling capWords() recursively 2 more times, the array is
* returned one more time
* array = ["Dog", "Cat", "Bird"]
*/
function capWords(array, index = 0) {
if (array.length < 1) return `ERROR Empty Array`;
let str = array[index];
if (typeof str === "string" && /[a-z]/.test(str.charAt(0))) {
let cap = str.charAt(0).toUpperCase();
array[index] = cap + str.slice(1);
}
if (index < array.length - 1) {
return capWords(array, index + 1);
}
return array;
}
console.log(capWords(['dog', 'cat', 'bird'], 0));
console.log(capWords(['dog', '', 'bird']));
console.log(capWords([2, 'cat', 'bird'], 0));
console.log(capWords(['dog', 'cat', {}], 0));
console.log(capWords([]));
You just forgot to select the First letter charAt(0) and to add more logic to connect the first letter with the other part of the word array[0].charAt(0).toUpperCase() + array[0].slice(1).toLowerCase();
The same situation when you are recursively pushing every word into an array.
function capitalizeFirst (array){
if (array.length === 1) {
return [array[0].charAt(0).toUpperCase() + array[0].slice(1).toLowerCase()];
}
var word = capitalizeFirst(array.slice(0, -1));
word.push(array.slice(array.length-1)[0].charAt(0).toUpperCase() +
array.slice(array.length-1)[0].slice(1).toLowerCase());
return word;
}
javascript function which returns an array of string in such a way that it contains all possible upper-case letters of the input string one at a time sequentially.
uppercase("hello") ➞ ["Hello", "hEllo", "heLlo", "helLo", "hellO"]
what i have tried is
const helloCapital = (str) => {
let a = [];
for (let i in str) {
a.push(str[i].toUpperCase() + str.slice(1));
}
return a;
};
but it gives weird results
[ 'Hello', 'Eello', 'Lello', 'Lello', 'Oello' ]
This looks like a challenge for a course or challenges website.
If that is the case, it is really, really not cool to come here and ask for that answer.
But, since I'm already here, here it goes a working solution.
const capitals = s => Array.from(s,(_,i)=>s.slice(0,i)+_.toUpperCase()+s.slice(i+1))
UPDATE: Explaining the code
Array.from works on iterable objects, such as strings, Arrays and, ArrayLike objects for example.
It calls the function you pass as the first argument on each element of the iterable, in this case, the string.
The function receives 1 element of the iterable (the _) and the position of that element (the i)
So the function is returning a concatenation of 3 things:
* the substring of the original string from 0 to i
* the current element of the iterable, or current character, toUpperCase()
* the substring of the original string from i+1 to the end of the string.
Your logic is wrong, to work you need to concat the slice before letter with capitalized letter and with the slice after letter.
function capitalizeEachLetter (text) {
return Array.from(text, (letter, index) =>
text.slice(0, index) + letter.toUpperCase() + text.slice(index + 1)
);
}
use array array map,
var str = "hello";
var capitals = Array.from(str).map((e, i, ar) => {
let r = [...ar];
r[i] = ar[i].toUpperCase();
return r.join('');
});
console.log(capitals)
I have an sorted array e.g
var arr = [ "aasd","march","mazz" ,"xav" ];
And i want to find the first occurance of letter that starts with "m" here it would be 1 . Is thee any way how to do it without looping trought whole array?
You could use a binary search to find any word starting with that letter, then loop backwards until you get the first one.
Is there any way how to do it without looping trought whole array?
Yes, loop until you've found the match.
If you want to avoid a for or while construct, you can use Array's find() method.
For example, arr.find(word => word.startsWith("m")) should return the result you expect (or undefined if there's no such word).
You could use the find() function to search for the first match that meets your constraint.
The startsWith() function could easily handle this :
// Your array
var arr = [ "aasd","march","mazz" ,"xav" ];
// This will find the first match that starts with "m"
arr.find(function(word){ return word.startsWith('m');}); // yields "march"
Or if you needed a bit more extensive pattern matching, you could use a regular expression via the test() function, which can be seen in the following example and handles the same scenario (matching a string that begins with "m") :
// Your array
var arr = [ "aasd","march","mazz" ,"xav" ];
// First match that starts with "m"
var match = arr.find(function(word){ return /^m/i.test(word);}); // yields "march"
Example
var arr = ["aasd", "march", "mazz", "xav"];
var match = arr.find(function(word) { return /^m/i.test(word); });
alert(match);
You dont need to loop through the whole array - only until such time as you find what you're interested in
function findFirstIndex(arr, char){
for(var i=0;i<arr.length;i++){
if(arr[i].substring(0,1) === char)
return i;
}
return -1; // not found
}
You could use Array#some()
The some() method tests whether some element in the array passes the test implemented by the provided function.
function find(letter, array) {
var index;
array.some(function (a, i) {
if (a[0] === letter) {
index = i;
return true;
}
});
return index;
}
var arr = ["aasd", "march", "mazz", "xav"];
document.write(find('m', arr));
When looking at set of characters I am trying to put each letter into a specifc order in an array. For Example: Given the Strings "cat" and "dog" I would want an array that contains [d,o,g,c,a,t], cat at the end of the array because it was read first.
Currently I have tried this:
However, when I try the code below assuming the strings are "cat" and "dog".
I get an array containing: [c,a,t,d,o,g]. Instead of push I have also tried .unshift but the array now reads: [g,o,d,t,a,c].
var chars = /^[a-z]$/;
var string = [];
function makeword(){
if(currentChar.match(chars)){
string.push(currentChar);
currentChar = getNextChar(); //Gets next Character in the String
makeword();
}
}
Is something like this possible in Javascript?
If I understood you correctly, you want to provide a list of strings, then have them show up in an array in reverse order, with each letter as an element of the array. The following function will do just that:
function makeWords() {
var arr = [];
for(var i = arguments.length - 1; i >=0; i--) {
arr.push(arguments[i]);
}
return arr.join('').split('');
}
so running makeWords('cat', 'dog') will result in ['d','o','g','c','a','t'].
It's a relatively simple code when a functional approach is used. The rest and spread operators are very handy both to collect the function arguments and to spread the characters of a word into an array.
var characterify = (...c) => c.reduceRight((a,b) => a.concat([...b]) ,[]);
document.write("<pre>" + JSON.stringify(characterify("cat","dog")) + "</pre>");
Not sure how to explain this in words, but is there any function in javascript that, when given a string , will return the number of times it occurs in an array?
For example:
var arr = ["a","b","c","d","c","c","b"];
var repeats = arr.count("c");
With repeats then being equal to 3 as "c" occurs 3 times.
I tried to look this up but I wasn't sure on how to word it so I didn't get anything useful.
You can create your own function or add it to the prototype of Array:
Array.prototype.count = function (val){
var result = 0;
for(var i = 0; i < this.length; i++){
if(this[i] === val) result++;
}
return result;
}
Then you can do ['a','b', 'a'].count('a') // returns 2
You can use array.filter()
var arr = ["a","b","c","d","c","c","b"];
var repeats = arr.filter(function(value) { return value==="c"; } ).length;
console.log(repeats)
arr.filter(function(v){return v=='c';}).length
Exact Word Match Example
var search_word = 'me';
var arr = ['help','me','please'];
arr.filter(function(el){return el === search_word}).length;
The filter function will return the element if the result of the function is true. The function is called on each element of the array. In this case, we are comparing the array element to our search word. If they are equal (true) the element is returned. We end up with an array of all the matches. Using .length simply gives us the number of items in the resulting array; effectively, a count.
Partial Match Example
If you were to want something a little more robust, for instance count the number of words that contain the letter l, then you could tokenize the string and scan the index, or use some regex which is a little more costly, but also more robust:
var search_word = 'l';
var arr = ['help','me','please'];
arr.filter( function(el){ return ( el.match(search_word) || [] ).length }).length;
Note that match also returns an array of matching elements, but an unsuccessful match returns undefined and not an empty array, which would be preferred. We need an array to use .length (the inside one), otherwise it would result in an error, so we add in the || [] to satisfy the case when no matches are found.