Check if array value is included in string - javascript

I'm working on some client side validation for a contact form of sorts, the website currently isn't online so server side isn't relevant.
I am trying to create a 'word filter' to catch on any abusive of obscene language before the form is 'submitted'.
Heres the code, without the obscenities...
function filterInput(str) {
var inputFilter = ['word1', 'word2', 'word3'];
var arrayLength = inputFilter.length;
if (inputFilter.indexOf(str) > - 1) {
// Word caught...
} else {
// Clear...
}
If the user were to enter 'word1', it will catch the word. If the user enters 'word1word2' or 'John is a word3', it doesn't catch it.
I originally had a for loop which worked better, but still wouldn't work without whitespace between words('word1word2').
Any input would be greatly appreciated, I've been searching but nothing quite matches my needs.
EDIT: So I too have come up with a solution, but seeing the varying ways this can be achieved I am curious as to how it works and also why a particular way is better?
Heres what I came up with...
function filterInput(str) {
var inputFilter = ['word1', 'word2', 'word3'];
var arrayLength = inputFilter.length;
for (var i = 0; i < arrayLength; i++) {
if (str.includes(inputFilter[i])) {
window.alert('Message...');
return;
}
}
}

You're looking for some rather than indexOf, since you have to do custom matching:
if (inputFilter.some(function(word) { return str.indexOf(word) != -1; })) {
// Word caught...
} else {
// Clear...
}
Or with an ES2015+ arrow function and String.prototype.includes:
if (inputFilter.some(word => str.includes(word))) {
// Word caught...
} else {
// Clear...
}
some calls the callback repeatedly until the first time it returns a truthy value. If the callback ever returns a truthy value, some returns true; otherwise, some returns false. E.g., it's asking if "some" of the entries match the predicate function. (any may have been a better term, but when adding to the built-ins, the TC39 committee have to do a lot of work to avoid conflicts with libraries and such.)
If you ever need to get back the actual entry, use find which returns the entry or undefined if not found. If you need its index, use findIndex.
Side note: Just beware that it's notoriously complicated to do this well. Beware of the Scunthorpe problem, and of course people will routinely just confuse the sequence of letters or substitute asterisks or similar to defeat filters of this sort...

you can try something like this:-
function filterInput(str) {
var badWords = ['bad', 'worst'];
var isTrue = false;
if(str) {
for (var i = 0; i < badWords.length; i++) {
isTrue = !!(str.replace(/\W|\s/g, '').toLowerCase().indexOf(badWords[i]) + 1);
if(isTrue) break;
}
}
return isTrue;
}

Related

How to find whole substring in string?

I have a string and I have to check if that string contains defined substring I need to do some work and otherwise, I should return some error.
I have the following code:
function isContains(myString) {
let subString = 'test1234';
if(myString.includes(subString)) {
// to do some work
} else {
// return some error.
}
}
but the problem is if myString = 'my-string-test1-rrr' its condition return true.
How can I get true only in case when the whole subString was included in myString?
Use indexOf() instead.
function isContains(myString) {
let subString = 'test1234';
if(myString.indexOf(subString) > -1) {
// to do some work
} else {
// return some error.
}
}
you can use regex to check if that value is present are not;
example 1
without containing the specific string
var test = 'my-string-test1-rrr';
console.log(' test --- ', test.match(/test1234/g))
example 2
contains the specific string
var test = 'my-string-test1234-rrr';
console.log(' test --- ', test.match(/test1234/g))
It is highly recommended to use includes() over indexOf() and further indexOf returns the index of the occurrence where you would prefer an immediate answer - false / true if that substring is found inside the searched string.
Your function does exactly what you are asking. I would suggest to isolate the retrieval of this function and make it purer like so, then when you have the return boolean value you could utilize it after to run whatever logic you wish. This way you keep this function pure and separate your concerns better.
I also believe it would be easier for you to debug your issue if you isolate this functions like In the example I provided.
function isContains(myString) {
let subString = 'test1234';
let isContains = false;
if(myString.includes(subString)) {
isContains = true;
} else {
isContains = false;
}
return isContains;
}
You could use it like so in a later phase in your code:
const myString = 'my-string-test1-rrr';
let shouldRunOtherLogic = isContains(myString);
if (shouldRunOtherLogic) {
// to do some work
} else {
// return some error.
}
Hope I could help, if there's anything further you may need feel free to let me know.

How to find the missing next character in the array?

I have an array of characters like this:
['a','b','c','d','f']
['O','Q','R','S']
If we see that, there is one letter is missing from each of the arrays. First one has e missing and the second one has P missing. Care to be taken for the case of the character as well. So, if I have a huge Object which has all the letters in order, and check them for the next ones, and compare?
I am totally confused on what approach to follow! This is what I have got till now:
var chars = ("abcdefghijklmnopqrstuvwxyz"+"abcdefghijklmnopqrstuvwxyz".toUpperCase()).split("");
So this gives me with:
["a","b","c","d","e","f","g","h","i","j","k","l","m",
"n","o","p","q","r","s","t","u","v","w","x","y","z",
"A","B","C","D","E","F","G","H","I","J","K","L","M",
"N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
Which is awesome. Now my question is, how do I like check for the missing character in the range? Some kind of forward lookup?
I tried something like this:
Find the indexOf starting value in the source array.
Compare it with each of them.
If the comparison failed, return the one from the original array?
I think that a much better way is to check for each element in your array if the next element is the next char:
function checkMissingChar(ar) {
for (var i = 1; i < ar.length; i++) {
if (ar[i].charCodeAt(0) == ar[i-1].charCodeAt(0)+1) {
// console.log('all good');
} else {
return String.fromCharCode(ar[i-1].charCodeAt(0)+1);
}
}
return true;
}
var a = ['a','b','c','d','f']
var b = ['O','Q','R','S']
console.log(checkMissingChar(a));
console.log(checkMissingChar(b));
Not that I start to check the array with the second item because I compare it to the item before (the first in the Array).
Forward Look-Ahead or Negative Look-Ahead: Well, my solution would be some kind of that. So, if you see this, what I would do is, I'll keep track of them using the Character's Code using charCodeAt, instead of the array.
function findMissingLetter(array) {
var ords = array.map(function (v) {
return v.charCodeAt(0);
});
var prevOrd = "p";
for (var i = 0; i < ords.length; i++) {
if (prevOrd == "p") {
prevOrd = ords[i];
continue;
}
if (prevOrd + 1 != ords[i]) {
return String.fromCharCode(ords[i] - 1);
}
prevOrd = ords[i];
}
}
console.log(findMissingLetter(['a','b','c','d','f']));
console.log(findMissingLetter(['O','Q','R','S']));
Since I come from a PHP background, I use some PHP related terms like ordinal, etc. In PHP, you can get the charCode using the ord().
As Dekel's answer is better than mine, I'll try to propose somewhat more better answer:
function findMissingLetter (ar) {
for (var i = 1; i < ar.length; i++) {
if (ar[i].charCodeAt(0) != ar[i-1].charCodeAt(0)+1) {
return String.fromCharCode(ar[i-1].charCodeAt(0)+1);
}
}
return true;
}
var a = ['a','b','c','d','f']
var b = ['O','Q','R','S']
console.log(findMissingLetter(a));
console.log(findMissingLetter(b));
Shorter and Sweet.

Javascript: action when a string is not found in an array of objects (if statement)

var checkStore=function (book) {
for (var i=0; i<BookStore.length;i++) {
if (book==BookStore[i].title) {
var reply= prompt ('Want to add to your cart?'+ BookStore[i].title);
if (reply==='yes') {
Susan.cart.push(BookStore[i]);
}
}
}
}
How to add to my existing code "if the string is not found, then alert the user 'not found!'". Do I used [else] statement or begin a new one? It's not working correctly when I used else-if statement.
You might want to find the book first, then do your logic:
var checkStore = function(book) {
var found;
for (var i = 0; i < BookStore.length; i++) {
if (book == BookStore[i].title) {
found = BookStore[i];
break;
}
}
if (found) {
var reply = prompt ('Want to add to your cart?' + found.title);
if (reply === 'yes') {
Susan.cart.push(found);
}
} else {
alert('not found!');
}
}
var checkStore=function (book) {
if(BookStore.indexOf(book) < 0){
alert('hello, no book');
} else {
for (var i=0; i<BookStore.length;i++) {
if (book==BookStore[i].title) {
var reply= prompt ('Want to add to your cart?'+ BookStore[i].title);
if (reply==='yes') {
Susan.cart.push(BookStore[i]);
}
}
}
}
}
}
The other answers are totally valid. You can also just make return points which stop the execution of the function at logical endpoints.
var checkStore=function (book) {
for (var i=0; i<BookStore.length;i++) {
if (book==BookStore[i].title) {
var reply= prompt ('Want to add to your cart?'+ BookStore[i].title);
if (reply==='yes') {
Susan.cart.push(BookStore[i]);
return "book added to cart."
}else{
return "book found, but not added to cart."
}
}
}
return "bummer, book not found.";
}
alert(checkStore("book title"));
Fastest solution
This is the shortest way to take your exact existing code and add a few lines and solve this problem: How to add to my existing code "if the string is not found, then alert the user 'not found!'".
var checkStore=function (book) {
var bookWasFound=false; //Line added here
for (var i=0; i<BookStore.length;i++) {
if (book==BookStore[i].title) {
bookWasFound=true; //Line added here
var reply= prompt ('Want to add to your cart?'+ BookStore[i].title);
if (reply==='yes') {
Susan.cart.push(BookStore[i]);
}
}
}
if (!bookWasFound) { //Line added here
alert('Not found!'); //Line added here
} //Line added here
}
Explanation and alternatives
Why don't we use an else statement?
You ask, "Do I used [else] statement[...]?"
You do not. You can not! The reason is because the else part of an if-else statement is executed when the if part is not executed. So if we put that in our loop, every time we compare a book title to the string we're looking for, our else would be executed if the book title didn't match. We only want that to happen once.
It is somewhat difficult to think about this scenario without a concrete example, so I will show what happens if we use an if-else statement.
The code would look like this:
if (book==BookStore[i].title) {
...
} else {
alert('Not found!');
}
Imagine that our BookStore is populated with 3 books: "The C Programming language", "JavaScript: Pocket Reference", and "Everybody Poops".
If the checkStore function is passed in "JavaScript: Pocket Reference". Our loop will go like this:
As soon as we enter the loop, book would be equal to "JavaScript: Pocket Reference", and i = 0 so BookStore[i].title is the same as BookStore[0].title, and the 0th book's title is "The C Programming language".
So the line if(book==BookStore[i].title) { would compare "JavaScript: Poc..." to "The C Prog..." and evaluate to false.
Since the if evaulated to false, the else part gets executed. The else part is alert('Not found!').
Now, we have finished one iteration of our loop, and we compared the string passed in to the first book's title which didn't match, so we gave the alert. But we shouldn't have! If we just kept going, we would see that the second book's title matched, so we shouldn't have told the user that the book wasn't found. It is correct that the book wasn't found, yet, but we should have waited before we announced this to the user.
I may have went into great detail to explain my point, but I believe it is very important. This is one example of when it takes thinking like a programmer or thinking algorithmically to solve the problem.
You have to think about the problem and ask yourself, "How do we know if a book is in the list?". Well, a book is in the list if its title is the same as the title of any book in the list. Because we have to use the word any when we say "...any book in the list", we know that our algorithm must test every book in the list.
We can't tell the user that a book was found, or not found, until we have tested every book in the list, or in our BookStore. We can't tell the user that a book was found, or not found, until our loop has finished.
So what do we do?
You will see this situation over and over again during your programming experience. You need to iterate over a list, and determine if something is in the list.
You can't ever know until you've iterated over every single object in the list, but once you have, how do you know the answer?
The solution is to use what is commonly referred to in programming, a flag. A flag is a variable that has two possible states: true, or false, on or off, yes or no, 1 or 0. It doesn't matter what you call the states. At any time, the flag can only be in one of the two possible states.
We can use a flag by setting it to one state, usually false or 0, then looping through some list and if (...) we set it to true. Each iteration of the loop, if some condition is met, we set our flag to true, and at the end of the loop, we know that the condition was met at some point in the loop, if the flag is true.
The most common way to implement a flag, is to use a boolean variable. That is, a variable which can only be set to true or false. In Javascript, there isn't a boolean variable since any variable can be set to any value. So we just use a regular variable, but still only set it to true or false.
We do that like this:
var myFlag = false;
// OR
var myFlag = true;
So a simple usage of a flag could be to determine if there exists an even number in a given array.
Just like this:
var myArray = [1, 3, 5, 123, 125, 4, 89, 8, 10];
var hasEven = false;
for(var i = 0; i < myArray.length; i++) {
if( myArray[i] % 2 == 0 ) {
hasEven = true;
}
}
if(hasEven) {
alert('Found an even number!');
} else {
alert('Did not find an even number!');
}
Note that you can test the value of a boolean (true or false) variable with if(variableName), you don't have to include the == true like if(variableName == true).
Final implementation
Now we can use our flag to solve our original problem
var checkStore = function(book) {
var bookWasFound = false;
for (var i = 0; i < BookStore.length; i++) {
if (book == BookStore[i].title) {
// If we get in this loop, we must have found a match,
// so we should set our flag to true now
bookWasFound = true
var reply = prompt ('Want to add to your cart? ' + BookStore[i].title);
if (reply === 'yes') {
Susan.cart.push( BookStore[i] );
}
} // End of if statement for prompt
} // End of loop looking for book
if( !bookWasFound ) { // Same as if( bookWasFound == false )
alert('Not found!');
}
} // End of function
Using this flag allows us to keep the inside of the for loop very clean and organized, and handle the results outside of it.

How do I recurse DOM nodes to an arbitrary depth in Javascript?

I am really having trouble getting my head around crossbrowser recursion in the DOM. I want to get only the text content of a node, but not any HTML tags or other information. Through trial and error, I found that the textContent and innerText attributes don't hold across all browsers, so I have to use the data attribute.
Now the function I have so far is this:
getTextContentXBrowser: function(nodeIn) {
// Currently goes down two levels. Need to abstract further to handle arbitrary number of levels
var tempString = '';
for (i=0, len=nodeIn.childNodes.length; i < len; i++) {
if (nodeIn.childNodes[i].firstChild !== null) {
tempString += nodeIn.childNodes[i].firstChild.data;
} else {
if (nodeIn.childNodes[i].data && nodeIn.childNodes[i].data !== '\n') {
tempString += nodeIn.childNodes[i].data;
}
}
}
return tempString;
},
It's written in object notation, but otherwise it's a pretty standard unremarkable function. It goes down two levels, which is almost good enough for what I want to do, but I want to "set it and forget it" if possible.
I've been at it for four hours and I haven't been able to abstract this to an arbitrary number of levels. Is recursion even my best choice here? Am I missing a better option? How would I convert the above function to recurse?
Thanks for any help!
Update: I rewrote it per dsfq's model, but for some reason, it goes one level down and is unable to go back up afterwards. I realized that my problem previously was that I wasn't concatenating in the second if clause, but this seems to have stopped me short of the goal. Here is my updated function:
getTextContentXBrowser: function(nodeIn) {
var tempString = '';
for (i=0, len=nodeIn.childNodes.length; i < len; i++) {
if (nodeIn.childNodes[i].data) {
tempString += nodeIn.childNodes[i].data;
} else if (nodeIn.childNodes[i].firstChild) {
tempString += this.getTextContentXBrowser(nodeIn.childNodes[i]);
}
}
return tempString.replace(/ /g,'').replace(/\n/g,'');
},
Anyone see what I'm missing?
Have you considered doing this with jQuery?
getTextContentXBrowser: function(nodeIn) {
return $(nodeIn).text();
}
As simple as that!
It can be really simple function calling itself to to replace nodes with its contents. For example:
function flatten(node) {
for (var c = node.childNodes, i = c.length; i--;) {
if (c[i].nodeType == 1) {
c[i].parentNode.replaceChild(document.createTextNode(flatten(c[i]).innerHTML), c[i]);
}
}
}
Looks like in your case you getTextContentXBrowser is a method of some object, so you will need to call it from inside itself properly (in my example I just use function).
Demo: http://jsfiddle.net/7tyYA/
Note that this function replaces nodes with a text in place. If you want a function that just returns a text without modifying actual node right away consider this example with another version of the script:
Demo 2: http://jsfiddle.net/7tyYA/1/

Better way to see if an array contains an object?

I have an array of items (terms), which will be put as <option> tags in a <select>. If any of these items are in another array (termsAlreadyTaking), they should be removed first. Here is how I have done it:
// If the user has a term like "Fall 2010" already selected, we don't need that in the list of terms to add.
for (var i = 0; i < terms.length; i++)
{
for (var iAlreadyTaking = 0; iAlreadyTaking < termsAlreadyTaking.length; iAlreadyTaking++)
{
if (terms[i]['pk'] == termsAlreadyTaking[iAlreadyTaking]['pk'])
{
terms.splice(i, 1); // remove terms[i] without leaving a hole in the array
continue;
}
}
}
Is there a better way to do this? It feels a bit clumsy.
I'm using jQuery, if it makes a difference.
UPDATE Based on #Matthew Flaschen's answer:
// If the user has a term like "Fall 2010" already selected, we don't need that in the list of terms to add.
var options_for_selector = $.grep(all_possible_choices, function(elem)
{
var already_chosen = false;
$.each(response_chosen_items, function(index, chosen_elem)
{
if (chosen_elem['pk'] == elem['pk'])
{
already_chosen = true;
return;
}
});
return ! already_chosen;
});
The reason it gets a bit more verbose in the middle is that $.inArray() is returning false, because the duplicates I'm looking for don't strictly equal one another in the == sense. However, all their values are the same. Can I make this more concise?
var terms = $.grep(terms, function(el)
{
return $.inArray(el, termsAlreadyTaking) == -1;
});
This still has m * n performance (m and n are the lengths of the arrays), but it shouldn't be a big deal as long as they're relatively small. To get m + n, you could use a hashtable
Note that ECMAScript provides the similar Array.filter and Array.indexOf. However, they're not implemented in all browsers yet, so you would have to use the MDC implementations as a fallback. Since you're using jQuery, grep and inArray (which uses native indexOf when available) are easier.
EDIT:
You could do:
var response_chosen_pk = $.map(response_chosen_items, function(elem)
{
return elem.pk;
});
var options_for_selector = $.grep(all_possible_choices, function(elem)
{
return $.inArray(elem.pk, response_chosen_pk) == -1;
});
http://github.com/danstocker/jorder
Create a jOrder table on termsAlreadyTaking, and index it with pk.
var table = jOrder(termsAlreadyTaking)
.index('pk', ['pk']);
Then you can search a lot faster:
...
if ([] == table.where([{ pk: terms[i].pk }]))
{
...
}
...

Categories

Resources