I need help understanding whats going on in my code. I plan on writing a function that takes in a string and removes all letters. The input will be a combination of letters and numbers. I want this function to return an array of all the numbers found in the string. I have successfully written something(with the help of stackoverflow):
number = "32321FDFDS 44"
arr = number.replace(/[A-Za-z]/g," ").split(" ")
for(var i = arr.length - 1; i >= 0; i--){
if(arr[i] == "") {
arr.splice(i, 1);
}
}
This returns
[ '32321', '44' ]
Which is perfect for now. My question is I don't understand how arr.splice(i,1) is removing empty strings. It doesn't make sense that it is removing the empty strings in arr. Can someone help me understand this?
Test :
if (arr[n] == "") // if item `n` within `arr` array `==` `""` , do stuff
See Array.prototype.splice()
With two items within an array :
var arr = ["123", ""];
if (arr[0] == "") {
arr.splice(0,1);
} else {
console.log(arr); // ["123", ""];
};
if (arr[1] == "") {
arr.splice(1,1);
console.log(arr); // ["123"]
};
Unlike other methods that return a new array, leaving the original variable alone, the .splice method mutates an array, making in-place changes to it.
The statement arr.splice(i, 1); means starting at position i remove one item from the array arr. if(arr[i] == "") means if the item at position i is and empty string, do the stuff inside this block. So when the if statement is true that item is removed from the array.
That said unless you need to support ES3 browsers (which effectively means IE8 or below), instead of looping through the array like that, I would just use the .filter method:
var number = "32321FDFDS 44",
arr = number.replace(/[A-Za-z]/g," ").split(" ").filter(function (item) {
return !!item; // all strings except an empty string will coerce to true
});
console.log(arr);
jsFiddle
If you are just trying to extract an array of numeric strings from a string with no other requirements, an even more succinct way of doing it would be to just split on one or more non-numbers:
var number = "32321FDFDS 44",
arr = number.split(/\D+/);
// arr is now [ "32321", "44" ]
console.log(arr);
This does it all in one step without having to filter out empty strings at all.
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;
}
I use this function:
function search (){
v = [[2.0], [3.0], [4.0], [5.0]]
if(v.indexOf(2.0) != -1){
Logger.log ('if')
}else{
Logger.log('else')
}
}
editing
My real need is in the values of words like this
I was answered about numbers so I did not change and only added
function search (){
v = [["One"], ["two"], ["three"], ["four"]]
if(v.indexOf("One") != -1){
Logger.log ('if')
}else{
Logger.log('else')
}
}
And instead of "if" returning to me, "else" returns to me.
I apologize for the broken English
You input array is a multi/two dimensional array. You are searching for 2.0 which is actually embedded inside another smaller array. Try changing the function to below.
function search() {
v = [
["One"],
["two"],
["three"],
["four"]
]
if (v.findIndex(e => e[0] === 'One') != -1) {
console.log('if')
} else {
console.log('else')
}
}
search();
You have an array of arrays, so trying to use indexOf on the array of arrays will not work for your case. One option is to use flat and some in conjunction to flatten the array of arrays to one array and then check if the value is found inside the flattened array:
function search (){
v = [[2.0], [3.0], [4.0], [5.0]];
if(v.flat().some((a) => a === 2.0)){
console.log('if');
}else{
console.log('else');
}
}
search();
Did you mean to write
v = [2.0, 3.0, 4.0, 5.0];?
Because that will print out 'if'.
Reason is that in your example, each element of v array is an array of it's own.
If you really want it to be an array of arrays, then to make it work you can do this in the if statement:
if ( v.flat().includes(2.0) )
flat converts it from an array of array that contain a number, to an array of numbers.
includes is a nicer method to check if the array contains a value - no need to check for index :)
array.indexOf() is not applicable for nested array. If your array is primitive types then it's working, but if array is non-primitive types then through indexOf you can't match the value.
Use this instated of your function:
v.findIndex(ele => [...new Set(ele)][0] == 2.0) >=0 ? 'if' : 'else'
This function cleans up a string (removes all non-alphanumeric characters including underscores) then splits each letter into an array so that it can be reversed, then checked against the original.
At console.log(cleanStr) , it is returning the reversed array but I do not know why.
function checkIfPalindrome(str) {
var cleanStr = str.toLowerCase().replace(replace, "" ).split("");
var reversedStr = cleanStr.reverse();
console.log(cleanStr); // why is this returning reverseStr, the reversed array?
if (cleanStr == reversedStr){
return true
}
return false
}
checkIfPalindrome("five|\_/|four");
The reverse() method reverses an array in place - it mutates the array it's called on. Try creating a new array instead:
const cleanStr = str.toLowerCase().replace(replace, "" ).split("");
const reversedStr = [...cleanStr].reverse();
At console.log(cleanStr) , it is returning the reversed array but I do not know why.
Because reverse reverses it in place.
Separately, you have a problem here:
if (cleanStr == reversedStr){
If they were different arrays, that would always be false, even if they had the same contents.
If you want to make a copy of the array and then reverse it, throw a .slice() in there:
var reversedStr = cleanStr.slice().reverse();
// -----------------------^
...and then compare after turning them back into strings:
if (cleanStr.join("") === reversedStr.join(""))
(I'd probably change those variable names, too, as they don't refer to strings.)
And finally, any time you find yourself writing:
if (x == b) {
return true;
}
return false;
back up and write
return x == b;
instead. :-)
Is that possible to check if the variable array contains exactly the numbers 1,0,0,1?
For example,
var array = [1,0,0,1];
if (array === 1001) alert("success");
You can just join the array to check
The join() method joins all elements of an array (or an array-like
object) into a string and returns this string.
Note: You need to use == instead of === because join will return a string.
Like:
var array = [1, 0, 0, 1];
if ( array.join("") == 1001 ) alert("success");
As per suggestion below, you can also use === and compare it with a string.
var array = [1, 0, 0, 1];
if ( array.join("") === "1001" ) alert("success");
Please check more info about join: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join
Use the join() method to joins all elements of the array into a string and returns this string.
var elements = [1,0,0,1];
console.log(elements.join('') === "1001");
// expected output: true
Using the method join("") you conver your array into a string with out the commas
Then you use the includes("1001") to check for the expected result
Hope this is what you were looking for. Happy to explain or help in a better solution if needed.
var array = [1,0,0,1];
var string = array.join("");
console.log(string);
if (string.includes('1001')) alert("success");
Well, everyone gave a strict answer to your question; but I figured I would add on to it a little. Assuming you want this to be a little more dynamic. This way we check subsets of the array for the string you are looking for, rather than just the entire array.
Array.prototype.containsSequence = function(str, delimiter){
//Determines what is seperating your nums
delimiter = delimiter || '';
//Check the sub array by splicing it, then joining it
var checkSection = function (arr, start, end, str){
return arr.slice(start, end).join(delimiter) === str;
};
let start = 0; //start of our sub array
let end = str.length; //the length of the sub array
//Loop through each x size of sub arrays
while(end < this.length){
//Check the section, if it is true it contains the string
if(checkSection(this, start, end, str)){
return true;
}
//Incriment both start and end by 1
start++;
end++;
}
//We dont contain the values
return false;
};
///Test stuff
const ARRAY = [1,2,3,4,5,6,7,8,9,10];
if(ARRAY.containsSequence('456')){
console.log('It contains the str');
}else console.log('It does not contain');
I have an array, and I want to remove just one element, but without reordering keys. Is there an easy way without using delete or rebuilding the entire array?
Or alternatively clean up after delete to get rid of the undefined values, fixing the length again.
var array = ["valueone", "valuetwo"];
console.dir(array); // keys 0 and 1
array.splice(0, 1);
console.dir(array); // key 1 is now 0, do not want!
You can delete the elements of an array:
a = ['one', 'two'];
delete a[0];
// a is now [undefined, 'two'];
alternatively, set a[0] explicitly to undefined.
Note that an arrays .length parameter is automatically maintained by the system. If you intentionally set it to a higher number, you'll just get a whole load of undefined values for the missing keys:
a.length = 10;
// a is now [undefined, 'two', undefined x 8]
If these semantics are not acceptable to you, then you should consider using an Object instead. This will preserve your keys, and perhaps be more efficient, but you lose the .length property.
couldn't you just explicitly set the value to undefined or null or an empty string. What are you trying to achieve?
var arr = ['aaaa','bbb','ccc','ddd'];
arr[0]= undefined;
//or
arr[0]= null;
///or
arr[0]= "";
arr.length; <--- 4
Update 2018-09-07
This answer isn't very good, in my opinion. I provided an answer on How do I remove a property from a JavaScript Object that has received much more attention from me over the years and covers this case and goes into much more detail.
The point is, you should be using Array.prototype.splice and Array.prototype.slice.
array.splice(start, n) returns a subset of array from index start with n sequential elements, and removes this subset from the original array, creating a new array in the process.
let array = [1,2,3,4,5,6];
array.splice(2,3); // [3,4,5]
array; // [1,2,6]
array.slice(start, end) returns a subset of array from index start to index end without mutating the original. The behavior is a little different from splice, which is why I prefer to call it as array.slice(start, start + n).
let array = [1,2,3,4,5,6];
array.slice(2, 2 + 3); // [3,4,5]
array; // [1,2,3,4,5,6]
Of course you could set the index to a sentinel value like null or "", but if you are wanting the array to stay in the same order after a deletion, perhaps you should change your approach--why does "valuetwo" have to be at index 1? What useful information is even being held in this data structure if the contents are always the same as the keys needed to access them?
The original answer is below. And if I am going to keep the original text, perhaps I should elaborate on why it's bad advice.
You can use javascript's delete keyword.
delete array[index];
Don't do this. If your array is homogeneous (as it ought to be), then this will corrupt your array by introducing a second type (undefined). You should use array.splice() as discussed above, which will create a new array with the specified range omitted.
Unfortunately, this creates an undefined index inside of the array
var arr = ['pie', 'cake', 'fish'];
delete arr[1];
arr; // ['pie', undefined, 'fish']
Case in point.
You could also do this:
var arr = [9,8,7,6];
arr[1] = null;
arr; // [9,null,7,6]
arr.length; // 4
var i = -1;
while(++i < arr.length){
if(arr[i] && typeof(arr[i] === "number")){
alert(arr[i]);
}
}
You could, but you shouldn't. Not only is this unnecessary, and doesn't do anything useful (because all it's doing is calling alert), but it's actually broken.
if(arr[i] && typeof(arr[i] === "number")){
alert(arr[i]);
}
You might expect this to only print our element if it is a non-zero number, but will in fact also run for values like "foo", [] and document.createElement("p"), because typeof(arr[i] === "number") will always return the value "boolean", which is a non-empty string, which is truthy and will therefore evaluate true. Which means the only requirement for alert to be called is that arr[i] is truthy. There are only six values in the entire language that will cause this if statement to not execute, and those are:
undefined
null
0
"" (pronounced "empty string")
false
NaN
Or, if you don't NEED to use arrays, you could use an object and make everything easier:
var obj = {
0: "first",
1: "second",
2: "third"
};
delete obj[1];
obj; // {0: "first", 2: "third"}
for(var i in obj){
alert(obj[i]);
}
Which would instantaneously erase all of the advantages to using an array. Now you have a data set which may or may not be heterogeneous, which can't be filtered, mapped, reduced or transformed in any sane way, and you have to resort to things like for(i in obj) (which is extremely bug-prone if you dare to use a library like jQuery) to iterate over it. Luckily today we have fancy stuff like Object.keys(obj).map(k => obj[k]).forEach(function(el){ ... }), but that's no excuse to have bad data structures.
To get the length of an object:
getLength = function(obj){
var i = 0, l = 0;
for(i in obj){
l++;
}
return l;
}
getLength(obj); // 3
Again, with arrays, this is unnecessary.
But remember that objects sort their indices by date of creation, not > by name. This shouldn't result in a road block, though.
To sort the indices of an object alphabetically:
sortObject = function (){
var arr = [], i;
for(i in this){
arr.push({index:i,content:this[i]});
delete this[i];
}
arr.sort();
for(i in arr){
var item = arr[i];
this[item.index] = item.content;
}
return this; // make chainable
}
var obj = {
acronym: "OOP",
definition: "Object-Oriented Programming",
article: "http://wikipedia.org/OOP"
};
sortObject.apply(obj); // indices are "acronym", "article", "definition"
array.sort(fn)
The whole point of an object is that its properties are unsorted, anyway. Sorting an unsorted list will hardly do anything useful.
Just to illustrate how much better arrays are at doing array things:
let array = ["pie", "cake", "fish", "brownie", "beef", ...];
/* do some stuff... */
array
.filter(el => exclude.indexOf(el) === -1)
.forEach(function(el){
console.log(el);
});
if exclude is ["cake", "brownie"], then this will log the following to the console:
pie
fish
beef
...
Just try to imagine how many unnecessary lines of code it would take to do the same using the approach from the previous version of this answer.
Hope this helped
Hopefully this update helped.