I'm trying to make a simple 'bad words' filter with javascript. It's meant to listen to any submit events on the page, then iterate through all input fields of the text type, check them for bad stuff by comparing the entered text with the word list, and finally return an according console.log/alert (for now).
I have two files: word-list.js with the critical words (loads first) and filter.js which pulls an array with all words from word-list.js.
My problems is, swear_words_arr[1] is 'undefined' and I don't understand why. I've been looking around for solutions, but still I can't seem to determine the reason for this. Help is much appreciated.
// get all inputs type = text and turn html collection into array
var getInputs = document.querySelectorAll("input[type=text]")
var inputs = Array.from(getInputs);
//var swear_alert_arr -> from in word-list.js
var swear_alert_arr = new Array();
var swear_alert_count = 0;
function reset_alert_count() {
swear_alert_count = 0;
}
function validate_text() {
reset_alert_count();
inputs.forEach(function(input) {
var compare_text = input.value;
console.log(compare_text);
for (var i = 0; i < swear_words_arr.length; i++) {
for (var j = 0; j < compare_text.length; i++) {
if (
swear_words_arr[i] ==
compare_text.substring(j, j + swear_words_arr[i].length).toLowerCase()
) {
swear_alert_arr[swear_alert_count] =
compare_text.substring(
j,
j + swear_words_arr[i].length
);
swear_alert_count++;
}
}
}
var alert_text = "";
for (var k = 1; k <= swear_alert_count; k++) {
alert_text += "\n" + "(" + k + ") " + swear_alert_arr[k - 1];
if (swear_alert_count > 0) {
alert("No!");
console.log('omg no bad stuff! D:');
} else {
console.log('no bad stuff found :)');
}
}
});
}
window.onload = reset_alert_count;
window.addEventListener('submit', function() {
validate_text();
});
It doesn't look like you've declared the array you're trying to access.
But, instead of loops with nested loops and keeping track of loop counters, just get a new array that contains any bad words in the submitted array. You can do this a number of ways, but the Array.prototype.filter() method works nicely:
let badWords = ["worse", "terrible", "horrible", "bad"];
let submittedWords = ["Good", "Terrible", "Great", "Fabulous", "Bad", "OK"];
// Loop over the submitted words and return an array of all the bad words found within it
let bad = submittedWords.filter(function(word){
// Do a case-insensitive match test. Return the word from the submitted words
// if it's on the bad word list.
return badWords.indexOf(word.toLowerCase()) > -1 ? word: null;
});
console.log("Bad words found in submitted data: " + bad.join(", "));
Related
I'm working on a Racker Rank problem whose function in JavaScript receives a single parameter (input).
Input Format:
The first line contains an integer, (the number of test cases).
Each line of the subsequent lines contain a String.
I need to print the even-indexed and odd-indexed characters of each string (S) separated by a space on a single line (see the Sample below for more detail).
2
Hacker
Rank
Hce akr
Rn ak
Is there a way to read the input line-by-line and save each string in a specific variable? If I achieve that I know how to solve the problem by iterating through the string. Otherwise, I'm lost. If not, how else could I handle the input? Thanks!
Readline doesn't seem to be the way to go.
function processData(input) {
//Enter your code here
}
process.stdin.resume();
process.stdin.setEncoding("ascii");
_input = "";
process.stdin.on("data", function (input) {
_input += input;
});
process.stdin.on("end", function () {
processData(_input);
});
What I have tried without success:
function processData(input) {
let myArray = input.split("\n");
let even_indexed = "";
let odd_indexed = "";
for (let i = 1; i <= myArray.length; i++) {
let str = myArray[i];
let len = str.length;
for (let j = 0; j < len; j++){
if (j % 2 == 0) { //check if the index is even;
even_indexed.concat(str[j]);
}
else {
odd_indexed.concat(str[j]);
}
}
}
console.log("%s %s", even_indexed, odd_indexed);
}
Can't you just use split() method with a newline operator?
<script>
let x= `Hey
I'm
a
multiline
string`
console.log(x.split("\n"))
</script>
The result will be an array on which every element represents a line of your input.
I made this pretty quickly so I apologize for it being kinda messy and I know there are probably more efficient ways of doing this, but it does what you are asking for.
let input = `This
is
a
multiline
string`
let splitWords = [];
input.split(/\r?\n/).forEach(function(e){ // Split the array by \n (newline) and/or \r (carriage return)
currentLine = {evenIndex: [], oddIndex: []}
for(let i = 0; i < e.length; i++){
if((i + 1)%2 === 0){
currentLine.evenIndex.push(e[i]);
}
else{
currentLine.oddIndex.push(e[i]);
}
}
splitWords.push(currentLine);
});
splitWords.forEach(function(e){
console.log(e.oddIndex.join('') + " " + e.evenIndex.join(''))
});
I'm working on exercism question and am stuck on one of the jasmine-node based tests, which says that I should be able to generate 10000 random names without any clashes (e.g. 2 randomly generated names match). This is the test:
it('there can be lots of robots with different names each', function() {
var i,
numRobots = 10000,
usedNames = {};
for (i = 0; i < numRobots; i++) {
var newRobot = new Robot();
usedNames[newRobot.name] = true;
}
expect(Object.keys(usedNames).length).toEqual(numRobots);
});
What I think I need to do is:
Create an array to hold all the names (robotNames),
Each time a name is generated, check if it exists in the array,
If it does, generate another name,
If it doesn't, add it to the array.
And here is my code so far...
"use strict";
var robotNames = [];
var name;
var Robot = function() {
this.name = this.generateName();
};
Robot.prototype.generateName = function() {
var letters = "";
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var numbers = "";
var digits = "0123456789";
// generate random characters for robot name...
for( var i=0; i < 2; i++ ) {
letters += alphabet.charAt(Math.floor(Math.random() * alphabet.length));
};
for( var i=0; i < 3; i++ ) {
numbers += digits.charAt(Math.floor(Math.random() * digits.length));
};
name = letters+numbers;
// Loop through array to check for duplicates
for(var i = 0; i < robotNames.length; i++) {
if (name == robotNames[i]) {
this.generateName();
return;
} else {
robotNames.push(name);
}
}
return name;
};
Robot.prototype.reset = function() {
this.name = this.generateName();
};
module.exports = Robot;
The test fails with an error message: "Expected 9924 to equal 10000."
The '9924' number is slightly different each time I run the test. I'm thinking this means the generateName function is eventually generating 2 matching random names. It seems as though my loop for checking duplicates is not being run and I'm not sure why.
I have tried a couple of different versions of the loop but with no success. So my questions is a) is my approach correct and there is something wrong with the syntax of my loop? or b) have I got the wrong idea about how to check for duplicates here?
Any pointers appreciated, thanks.
The problem is in this bit:
for(var i = 0; i < robotNames.length; i++) {
if (name == robotNames[i]) {
this.generateName();
return;
} else {
robotNames.push(name);
}
}
...you probably only want to push your name if NONE of the names fail to match. Here you're adding it to the list as soon as you find ONE that doesn't match. You want something more like:
for(var i = 0; i < robotNames.length; i++) {
if (name == robotNames[i]) {
return this.generateName();
}
}
robotNames.push(name);
(actually, combined with the fact that you weren't even returning the recursive call to this.generateName(), I'm not sure how your program could work...)
Find a library with an implementation for Sets. Collections.js is a good example.
One property of a set is that it doesn't have duplicates. So when you add a value to a set it will look for a duplicate and then add the value if no duplicate exists.
I'm attempting to teach myself javascript. I chose something I assumed was simple, but ran into problems relatively quickly.
I'm attempting to search a string for another string given by the user.
My code so far is:
var source = "XREs2qqAQfjr6NZs6H5wkZdOES5mikexRkOPsj6grQiYNZfFoqXI4Nnc1iONKVrA";
var searchString = []; //the users input
searchString = prompt("Enter search string");
var hits = [];
var one = 0;
var two = 0;
var k = 0;
var sourceSearch = function(text) {
for(i = 0; i < source.length; i++) { //for each character in the source
if(source[i] === searchString[0]) { //if a character in source matches the first element in the users input
one = source.indexOf(i); //confused from here on
for(p = searchString.length; p > 0; p--) {
}
}
}
};
sourceSearch(searchString);
My idea was:
check to see if the first loop finds a character that matches the first character in the user input
if it matches, check to see if the next X characters after the first match the next X characters in the source string
if they all match, push them to the hits array
My problem: I have no idea how to iterate along the arrays without nesting quite a few if statements, and even then, that wouldn't be sufficient, considering I want the program to work with any input.
Any ideas would be helpful. Thanks very much in advance.
Note: There are a few un-used variables from ideas I was testing, but I couldn't make them work.
You can try:
if (source.indexOf(searchString) !== -1) {
// Match!
}
else
{
//No Match!
}
As the other answers so far point out, JavaScript strings have an indexOf function that does what you want. If you want to see how it's done "by hand", you can modify your function like this:
var sourceSearch = function(text) {
var i, j, ok; // always declare your local variables. globals are evil!
// for each start position
for(i = 0; i < source.length; i++) {
ok = true;
// check for a match
for (j = searchString.length - 1; ok && j >= 0; --j) {
ok = source[i + j] === searchString[j];
}
if (ok) {
// searchString found starting at index i in source
}
}
};
This function will find all positions in source at which searchString was found. (Of course, you could break out of the loop on the first success.) The logic is to use the outer loop to advance to each candidate start position in source and use the inner loop to test whether that position actually is the position of a match to searchString.
This is not the best algorithm for searching strings. The built-in algorithm is much faster (both because it is a better algorithm and because it is native code).
to follow your approach, you can just play with 2 indexes:
var sourceSearch = function(text) {
j = 0;
for(i = 0; i < source.length; i++) {
if(source[i] === text[j]) {
j++;
} else {
j = 0;
}
if (j == text.length) {
console.log(i - j); //this prints the starting index of the matching substring
}
}
};
These answers are all pretty good, but I'd probably opt for something like this:
var source = "XREs2qqAQfjr6NZs6H5wkZdOES5mikexRkOPsj6grQiYNZfFoqXI4Nnc1iONKVrA";
var searchString = []; //the users input
searchString = prompt("Enter search string");
var hits = source.split(searchString);
var hitsCount = hits.length - 1;
This way you have all of the data you need to figure out where each hit occurred in he source, if that's important to you.
What's the best way to extract keyphrases from a block of text? I'm writing a tool to do keyword extraction: something like this. I've found a few libraries for Python and Perl to extract n-grams, but I'm writing this in Node so I need a JavaScript solution. If there aren't any existing JavaScript libraries, could someone explain how to do this so I can just write it myself?
I like the idea, so I've implemented it: See below (descriptive comments are included).
Preview at: https://jsfiddle.net/WsKMx
/*#author Rob W, created on 16-17 September 2011, on request for Stackoverflow (http://stackoverflow.com/q/7085454/938089)
* Modified on 17 juli 2012, fixed IE bug by replacing [,] with [null]
* This script will calculate words. For the simplicity and efficiency,
* there's only one loop through a block of text.
* A 100% accuracy requires much more computing power, which is usually unnecessary
**/
var text = "A quick brown fox jumps over the lazy old bartender who said 'Hi!' as a response to the visitor who presumably assaulted the maid's brother, because he didn't pay his debts in time. In time in time does really mean in time. Too late is too early? Nonsense! 'Too late is too early' does not make any sense.";
var atLeast = 2; // Show results with at least .. occurrences
var numWords = 5; // Show statistics for one to .. words
var ignoreCase = true; // Case-sensitivity
var REallowedChars = /[^a-zA-Z'\-]+/g;
// RE pattern to select valid characters. Invalid characters are replaced with a whitespace
var i, j, k, textlen, len, s;
// Prepare key hash
var keys = [null]; //"keys[0] = null", a word boundary with length zero is empty
var results = [];
numWords++; //for human logic, we start counting at 1 instead of 0
for (i=1; i<=numWords; i++) {
keys.push({});
}
// Remove all irrelevant characters
text = text.replace(REallowedChars, " ").replace(/^\s+/,"").replace(/\s+$/,"");
// Create a hash
if (ignoreCase) text = text.toLowerCase();
text = text.split(/\s+/);
for (i=0, textlen=text.length; i<textlen; i++) {
s = text[i];
keys[1][s] = (keys[1][s] || 0) + 1;
for (j=2; j<=numWords; j++) {
if(i+j <= textlen) {
s += " " + text[i+j-1];
keys[j][s] = (keys[j][s] || 0) + 1;
} else break;
}
}
// Prepares results for advanced analysis
for (var k=1; k<=numWords; k++) {
results[k] = [];
var key = keys[k];
for (var i in key) {
if(key[i] >= atLeast) results[k].push({"word":i, "count":key[i]});
}
}
// Result parsing
var outputHTML = []; // Buffer data. This data is used to create a table using `.innerHTML`
var f_sortAscending = function(x,y) {return y.count - x.count;};
for (k=1; k<numWords; k++) {
results[k].sort(f_sortAscending);//sorts results
// Customize your output. For example:
var words = results[k];
if (words.length) outputHTML.push('<td colSpan="3" class="num-words-header">'+k+' word'+(k==1?"":"s")+'</td>');
for (i=0,len=words.length; i<len; i++) {
//Characters have been validated. No fear for XSS
outputHTML.push("<td>" + words[i].word + "</td><td>" +
words[i].count + "</td><td>" +
Math.round(words[i].count/textlen*10000)/100 + "%</td>");
// textlen defined at the top
// The relative occurence has a precision of 2 digits.
}
}
outputHTML = '<table id="wordAnalysis"><thead><tr>' +
'<td>Phrase</td><td>Count</td><td>Relativity</td></tr>' +
'</thead><tbody><tr>' +outputHTML.join("</tr><tr>")+
"</tr></tbody></table>";
document.getElementById("RobW-sample").innerHTML = outputHTML;
/*
CSS:
#wordAnalysis td{padding:1px 3px 1px 5px}
.num-words-header{font-weight:bold;border-top:1px solid #000}
HTML:
<div id="#RobW-sample"></div>
*/
I do not know such a library in JavaScript but the logic is
split text into array
then sort and count
alternatively
split into array
create a secondary array
traversing each item of the 1st array
check whether current item exists in secondary array
if not exists
push it as a item's key
else
increase value having a key = to item sought.
HTH
Ivo Stoykov
function ngrams(seq, n) {
to_return = []
for (let i=0; i<seq.length-(n-1); i++) {
let cur = []
for (let j=i; j<seq.length && j<=i+(n-1); j++) {
cur.push(seq[j])
}
to_return.push(cur.join(''))
}
return to_return
}
> ngrams(['a', 'b', 'c'], 2)
['ab', 'bc']
Is there a way to make the following code faster? It's becoming too slow, when length of array is more than 1000 records, especially in IE6.
dbusers = data.split(";");
$("#users").html("");
for (i = 0; i < dbusers.length; i++) {
if ($("#username").val() != "") {
if (dbusers[i].indexOf($("#username").val()) != -1) {
$("#users").append(dbusers[i] + "<br>");
}
} else {
$("#users").append(dbusers[i] + "<br>");
}
}
Minimize the amount of work you do in the loop. Don't add stuff to the DOM in the loop, create a string.
var dbusers = data.split(";");
var username = $("#username").val();
var userlist = "";
if (username == "") {
for (i = 0; i < dbusers.length; i++) {
userlist += dbusers[i] + "<br>";
}
} else {
for (i = 0; i < dbusers.length; i++) {
if (dbusers[i].indexOf(username) != -1) {
userlist += dbusers[i] + "<br>";
}
}
}
$("#users").html(userlist);
Faster than those by far (especially in IE!) is to build your string as an array (yes, really) and then concatenate it at the end:
var dbusers = data.split(";"), username = $('#username').val();
$("#users").html($.map(dbusers, function(_, dbuser) {
if (username == '' || dbuser.indexOf(username) > 0)
return dbuser + '<br>';
return '';
}).get().join(''));
The $.map() routine will build an array from the return values of the function you pass. Here, my function is returning the user string followed by the <br>. The resulting array is then turned into a string by calling the native join() routine. Especially when you've got like 1000 things to work with, this will be much faster than building a string with repeated calls to +=! Try the two versions and compare!
Use a document fragment.
You can perform more optimizations, too, like removing that nasty if and creating the nodes yourself.
var frag = document.createDocumentFragment(),
dbUsers = data.split(';'),
dbUsersLength = dbUsers.length,
curDbUser,
usernameVal = $('#username').val();
for(i = 0; i < dbUsersLength; ++i) {
curDbUser = dbUsers[i];
if(curDbUser.indexOf(usernameVal) !== -1) {
frag.appendChild(document.createTextNode(curDbUser));
frag.appendChild(document.createElement('br'));
}
}
$('#users').empty().append(frag);
I made a tool to benchmark all the current answers: http://dev.liranuna.com/strager/stee1rat.html
ghoppe's and mine seem to be the fastest.
IE6 doesn't support querySelector, so lookups can be particularly slow. Keep HTML manipulation within loops to a minimum by reducing the number of appends you do, each one has a regular expression run on it to extract the HTML and convert it to a DOM object. Also work in some micro optimisations where you can, might improve performance a little especially over thousands of iterations.
var usersEl = $("#users"); // reduce lookups to the #users element
var result = ""; // create a variable for the HTML string
var unameVal = $("#username").val(); // do the username value lookup only once
dbusers = data.split(";");
usersEl.html("");
// Store the length of the array in a var in your loop to prevent multiple lookups
for (var i = 0, max = dbusers.length; i < max; i++) {
if (unameVal !== "") {
if (dbusers[i].indexOf(unameVal) != -1) {
result += dbusers[i] + "<br>";
}
} else {
result += dbusers[i] + "<br>";
}
}
usersEl.html(result); // Set the HTML only once, saves multiple regexes