JavaScript: Cannot manipulate array produced with string.match - javascript

I have produced an array using:
var arr = [];
arr = string.match(/(?:^| )([a-z]+)(?= [A-Z])/g);
This works as expected and the array is full and can be seen using
console.log or alert().
The array consists of words which I need to filter, so I am trying to
use .splice to remove unwanted instances of the same word using:
for (var i = arr.length - 1; i >= 0; i--) {
if (arr[i] === 'jim') {
arr.splice(i, 1);
}
}
The for loop doesn't recognize any instances of, for instance, 'jim' in
the array although there are several.
I tried the loop using an array I made myself and it worked fine, ie:
arr = ['jim', 'bob', 'arthur', 'jim', 'fred']
I have also tried the following which reports that 'jim' !== 'jim' as well as the other names not equalling 'jim'. Again this loop works fine with the self assigned array.
var i = arr.length;
while ( i-- )
if (arr[i] === 'jim')
arr.splice(i, 1);
else
alert( "'" + arr[i].toString() + "' !== 'jim'" );
What is it about the array produced by the string.match that I am not
understanding? Any help would be much appreciated.

You can save a lot of time by using Array.filter():
arr = arr.filter(function(x){
return x.trim() !== 'jim';
});

James is right: whitespace charcters cause the problem here.
When I try your examples above the test alerts:
' jim' !== 'jim'.
The first part/bracket of your regEx includes a whitespace character in the matched strings.
The generated array will most likely be something like: arr = [' jim', ' bob', ' arthur', ' jim', ' fred'].

Related

Write a recursive function called capitalizeFirst. Given an array of strings, capitalize the first letter of each string in the array

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;
}

getting index of string in array

I'm bashing my head over this one. I have tried using indexOf() and made my own function to iterate through the array and compare each term but I am always getting -1!
function checkindex(array,temp) {
for(var i = 0; i < array.length; i++) {
console.log(array[i] + " " + temp);
if (array[i] == temp) return i;
}
return -1;
}
array is an Object which is generated this way:
var array = (req.body.somestring).split(',');
When I output array and string this way:
console.log(array[i] + " " + temp);
I get something like this:
["My variable","Something else"] My variable
The spelling matches but its still -1. array.indexOf(temp) gives me the same results. Any thoughts?
Thanks in advance.
This seems to work for me:
var array = ["My variable","Something else"];
var lookup = "My variable";
var index = array.indexOf(lookup);
alert(index);
There is a nice polyfill for older browsers that can be found here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
So the problem was actually more upstream... I stringified the data (JSON.stringify) prior to sending it so the string from var array = (req.body.somestring).split(','); included brackets and all. That why the output of console.log(array) looked like this:
["My variable","Something else"]
The elements in this situation are:
array[0] === ["My variable"
array[1] === "Something else"]
The solution was instead of split was use JSON.parse. The output of console.log(array) after that was:
My variable,Something else
Thank you for your help.

Removing empty strings from array in javascript

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.

Remove array element when subarray has certain values

I have an array that contains any number of subarrays, each containing exactly two values.
i.e: interestArray[[1, 5], [3, 8] ... ]
How do I remove say the subarray containing the values [3, 8]?
My code is:
$('td', container).click(function(){
if(!$(this).hasClass('purchased') && !$(this).hasClass('manu'))
{
var manuId = $(this).parent().children('td:first-child').data('manu-id');
var typeId = $(this).data('type-id');
if($(this).hasClass('interest'))
{
$(this).removeClass('interest');
$(this).parent().children('td.manu').removeClass('interest');
var index = interestArray.indexOf([manuId, typeId]);
interestArray.splice(index, 1);
} else {
$(this).addClass('interest');
$(this).parent().children('td.manu').addClass('interest');
interestArray.push([manuId, typeId]);
}
//updateSurvey(interestsArray);
console.log(interestArray)
}
})
The below section does not work, and simply removes the first subarray.
var index = interestArray.indexOf([manuId, typeId]);
interestArray.splice(index, 1);
Here's a generic approach with your requirements:
var arr = [[1,2],[3,4],[5,6]];
var remove = [3,4];
for (var i=0; i<arr.length; i++) {
if (arr[i][0] == remove[0] && arr[i][1] == remove[1]) {
arr.splice(i, 1);
break;
}
}
console.log(arr); //=> [[1,2],[5,6]]
For a general approach, you can filter the array:
var reducedArray = interestArray.filter(function (item) {
return item[0] != manuId || item[1] != typeId;
});
You cannot use indexOf because that looks for the identical object (not merely an equivalent one).
If you're running an earlier version of JS that doesn't have Array.filter, there's a nice shim on the filter doc page linked to above.
Here is my personal solution more complete to avoid multiple entry issue and the break; thing seen above, it also avoids an issue if the array is after entry removal (it is jquery based but you can make a regular loop if you feel more comfy with it):
$.each( answers, function( index, value ){
if (typeof answers[index] != "undefined")
{
if(answers[index]["question_id"]==answer_to_del)
{
delete answers[index];
}
}
});
//Clean answer array from empty values created above
answers = answers.filter(function(n){ return n != undefined });

Only return values found in all arrays

Simple question, but i dont know how to solve it
I have several arrays, but i only want the values that all arrays have in common
Im using javascript.
Try looking for the value in each of the arrays using indexOF.
I never knew IE didn't support indexOf, but here's a quick fix from this post.
Something like this should work:
function getCommonElements() {
var common = [],
i, j;
if (arguments.length === 0)
return common;
outerLoop:
for (i = 0; i < arguments[0].length; i++) {
for (j = 1; j < arguments.length; j++)
if (-1 === arguments[j].indexOf(arguments[0][i]))
continue outerLoop;
common.push(arguments[0][i]);
}
return common;
}
Call it with any number of arrays as arguments:
var commonEls = getCommonElements(arr1, arr2, arr3, etc);
In case it's not obvious, the idea is to loop through the array from the first argument and test each of its elements against the other arrays. As soon as a particular element is found to not be in any of the other arrays from the other arguments continue on with the next element. Otherwise add the current element to the output array, common.
If you need to support browsers (IE < 9) that don't support the Array.indexOf() method you can either include the shim shown at the MDN page or replace the .indexOf() test from my code with another loop.
I think this should work.
var arr1 = [1,2,3,4]
, arr2 = [2,3,4,5]
, arr3 = [3,4,5,6]
, arrs = [arr1, arr2, arr3];
var all = arr1.concat(arr2.concat(arr3)).sort()
, red1 = all.filter(
function(val, i, arr) {
return i === arr.lastIndexOf(val)-1;
})
, red2 = red1.filter(
function(val, i, arr) {
var shared = true;
arrs.forEach(
function(arr, i, src) {
if (arr.indexOf(val) === -1)
shared = false;
})
return shared;
})
If you are only concerned with modern browsers that support reduce(), then use this solution:
Finding matches between multiple JavaScript Arrays
If you must support IE6, then use my solution below. Here's how I got this to work in IE6 using jQuery:
// Find common values across all arrays in 'a',
// where 'a' is an array of arrays [[arr1], [arr2], ...]
Object.common = function(a) {
var aCommon = [];
for (var i=0,imax=a[0].length,nMatch,sVal; i<imax; i++) {
nMatch = 0;
sVal = a[0][i];
for (var j=1,jmax=a.length; j<jmax; j++) {
nMatch += ($.inArray(sVal, a[j])>-1) ? 1 : 0;
}
if (nMatch===a.length-1) aCommon.push(sVal);
}
return aCommon;
}
Basically, you just loop through each value of the first array in 'a' to see if it exists in the other arrays. If it exists, you increment nMatch, and after scanning the other arrays you add the value to the aCommon array if nMatch equals the total number of the other arrays.
Using the sample data provided by Florian Salihovic, Object.common(arrs) would return [3, 4].
If you cannot use jQuery, then replace $.inArray() with the code provided by Mozilla:
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/IndexOf

Categories