Removing words from array using regex - javascript

I am creating a program that involves removing text from an array. If a letter is included in a word in the array, then that word will be removed. My first test was successful, a simple regular expression with a small array:
var regex = /z/;
var words = ["eggs", "zebras", "lampshade"];
for (i = 0; i < words.length; i++) {
let testResult = regex.test(words[i]);
if (!testResult) {
words.splice(i, 1);
}
}
console.log(words);
As expected, it returned [ 'zebras' ]. Because this was successful, I quickly scaled up using an npm package called "an-array-of-english-words". I ran my new script:
const masterWords = require("an-array-of-english-words");
var regex = /z/;
var words = masterWords;
for (i = 0; i < words.length; i++) {
let testResult = regex.test(words[i]);
if (!testResult) {
words.splice(i, 1);
}
}
console.log(words);
Every time I run it it ends up returning values that do not abide by the regular expression. For example, the code above returned ['aa', 'aahed', 'aahs', ...] as its first few values. Do I have to change my code to deal with a bigger array? Am I making a stupid mistake I didn't notice?

I think it may be due to the fact that you are splicing the array and looping it at the same time.
For ex. if the length of array is 5 and the current index is 2, after splicing the array, the item at the index 3 will be moved to the index 2, and will be skipped in the next iteration, since the next index will be 3.
So, you can create a clone of the words and change it, while iterating the original array.

#Joey , in official documentation if you see , try using it this way as a workaround . Also #Igor Moraru's answer explains why the issue might be happening.
console.log(words.filter(d => /z/.test(d)))

Related

How to search for a string INSIDE of entries in an array

So I get how to use the .includes option in JavaScript, but I need it to be able to find if a string is even just PART of an entry in an array. How would this be done? For example, to see if an array includes the value "Chocolate" in one of its entries, or as PART of one of its entries. Thanks,
Greg
Here's my code so far:
function filterByWord(arr, flavor){
for (let i = 0; i < arr.length; i++) {
if (arr[i].includes(flavor)) {
return arr[i];
}
}
}
console.log(filterByWord(originalFlavors, "Chocolate"))
You're returning the first match. The return statement ends the function, which stops the loop.
Use filter() to find all matches.
function filterByWord(arr, flavor) {
return arr.filter(x => x.includes(flavor));
}
const originalFlavors = ["Dark Chocolate", "Vanilla", "Chocolate Swirl", "Chocolate", "Strawberry"];
console.log(filterByWord(originalFlavors, "Chocolate"))
As Barmar says, you are returning the first match, he already gives an optimized solution using filter, if you are not comfortable with this tool and want to keep the most of your original code I suggest you modify it like this:
function filterByWord(arr, flavor){
let filteredWords = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i].includes(flavor)) {
filteredWords.push(arr[i]);
}
}
return filteredWords;
}
console.log(filterByWord(originalFlavors, "Chocolate"));
Notice that my suggestion is creating an empty array that is going to be filled with all the matching words when looping. As I said, Barmar's solution is more optimized but this can work out too and I believe this was what you were trying to attempt.

Removing array elements that contain a number

I have seen several answers on Stackoverflow but none have helped me. I have a huge array of nearly 100,000 words, of which I am trying to remove all words that contain a number. I am using the following to do that:
for(var i = 0; i < words.length; i++){
if (hasNumbers(words[i]) {
words.splice(i, 1);
}
function hasNumbers(t)
{ return /\d/.test(t); }
It seems to work, but not all the time because I am still getting words that contain numbers. What can I change to make this remove all words that contain any number at all?
(I am using p5.js with my js)
That is because when you delete a word at index i, the next word will have index i, yet you still increase i, thereby skipping a word which you never inspect.
To solve this you can go backwards through your array:
for(var i = words.length - 1; i >= 0; i--){
// etc.
Here is a shorter way to remove words with digits:
words = words.filter(a => !hasNumbers(a));
Finally, you really should call your second function hasDigits instead of hasNumbers. The words "digit" and "number" have a slightly different meaning.
Here is a snippet, using ES6 syntax, that defines the opposite function hasNoDigits and applies it to some sample data:
let words = ['abcd', 'ab0d', '4444', '-)#', '&9ยต*'];
let hasNoDigits = s => /^\D*$/.test(s);
console.log(words.filter(hasNoDigits));
words = words.filter(a => !hasNumbers(a));
I had started writing this and then trincot answered. His answer is correct, though with the popular and widespread usage of ES5 array functions, I feel like you could simplify this down quite a bit.
window.addEventListener('load', function() {
var data = [
'w3.org',
'google.com',
'00011118.com'
]; //This is supposed to be your data, I didn't have it so I made it up.
var no_nums = data.filter(function(item) {
//Tests each string against the regex, inverts the value (false becomes true, true becomes false)
return !/\d/.test(item);
});
var results = document.getElementById('results');
no_nums.forEach(function(item) {
results.innerHTML += item + '<br />';
//Loops through each of our new array to add the item so we can see it.
});
});
<div id="results">
</div>

How to Loop Through ths Array in JavaScript

I'm new to Javascript and I am begining to learn.
I need help understanding; how can I retrieve each carachter of this array.
var possibleRaysPasswords =['mobleyAndTrentonAreDead','tyrellIsElliot','dreadPirateRoberts'];
Like in these example:
e.g: femtocell
f
fe
fem
femt
femto
femtoc
femtoce
femtocel
femtocell
Much appreciated.
If you want to get each character of each element, you may do a simple array transformation to get you an array of all the characters in all the items:
var allOfThem = arr.join('').split('');
That is: you first join all the elements into a single string. and then you split this string into array of characters. Then you can loop over it.
Can you provide an example of what you've tried so far? Doing so will help us to answer any confusion you have.
For a start, let's demonstrate how to loop through each element in the array. We can declare an array the way you demonstrated:
var myArray = ["elements", "are", "pretty", "cool! "];
To loop through this array, we can simply use a for loop.
for (var i = 0; i < myArray.length; ++i) {
console.log(myArray[i]); // this provides the element at the ith index of the array
}
This will log, in order:
elements
are
pretty
cool!
You can access the individual characters of a string in the same exact way that you access individual elements of an array. Try that and see if you can get to where you need to be.
You could use two nested loop, one for the array and one for the atrings for the letters in combination with String#slice
var possibleRaysPasswords =['mobleyAndTrentonAreDead','tyrellIsElliot','dreadPirateRoberts'],
i, j;
for (i = 0; i < possibleRaysPasswords.length; i++) {
for (j = 1; j <= possibleRaysPasswords[i].length; j++) {
console.log(possibleRaysPasswords[i].slice(0, j));
}
}
.as-console-wrapper { max-height: 100% !important; top: 0; }
Let me know if something is unclear. The comments in the code should tell you what's going on though:
// making this a constant, because we only want to read from this data
const passwords = ['mobleyAndTrentonAreDead', 'tyrellIsElliot', 'dreadPirateRoberts'];
// somewhat recently, we can also define functions like this
const printPassword = password => {
// this function prints out the password starting from the first character, all the way to its end
// if the password is 'test', the output should be 't te tes test'
let resultString = ''; // this will be returned later
// let's use the good old for loop
// start at the first character (zero-indexed), and use the counter variable i to mark the end of the substring
for (let i=1; i <= password.length; i++) {
resultString += password.substring(0, i) + ' ';
}
return resultString;
};
// iterating over every password in the passwords array,
// and log the returned string to the console
passwords.forEach(password => console.log(printPassword(password)));
user 'forEach' to iterate elements of array and 'substr' to part of string:
var possibleRaysPasswords =['mobleyAndTrentonAreDead','tyrellIsElliot','dreadPirateRoberts'];
possibleRaysPasswords.forEach(function(element){
for(var i=0 ; i<element.length ; i++){
console.log(element.substr(0,i));
}
});
'for of' can also be used for iteration:
for (element of possibleRaysPasswords){
for(var i=0 ; i<element.length ; i++){
console.log(element.substr(0,i));
}
}
As simple as that:
var letters = [];
for(let i of possibleRaysPasswords ){letters.push.apply(letters,i.split(""))}
console.log(letters);
That will create and array with all letters. Not sure if that was the question though

Removing a value in an object based on if it contains a string

a fairly simple question today.
I have an object that looks like this:
var buttonLogos = {
adcraft: [".","..","1.png","2.png","3.png"],
ferries: [".","..","1.png","2.png"]
}
and I'm looking for a quick way to remove the entries at the beginning with the dots, I would usually just filter out anything with a dot, but I can't because the strings I want contain a .png
it might be a solution to filter out the first two entries, because they will always be "." and ".." but alas I'm not sure how to even do that.
(jQuery is encouraged)
I would love some help! Thanks.
for(i in buttonLogos){
buttonLogos[i] = buttonLogos[i].filter(function(i){
return !i.match(/^\.{1,2}$/);
});
}
You can use js regex as follows,
buttonLogos.adcraft = $(buttonLogos.adcraft).filter(function(i,val){return val.match(/[^\.]/);});
Filters as mentioned in other answers or a combination of indexOf and splice would also work.
var adcraft = [".","..","1.png","2.png","3.png"];
var elems_to_rm = [];
for (var i = 0; i < adcraft.length; i++) {
if (adcraft[i].indexOf('.') === 0) {
elems_to_rm.push(adcraft[i]);
}
}
for (var i = 0; i < elems_to_rm.length; i++) {
var index = adcraft.indexOf(elems_to_rm[i]);
adcraft.splice(index, 1);
}
Try manually. Any number of items can be deleted from the array by specifying just two
arguments: the position of the first item to delete and the number of items to delete. For
example, splice(0, 2) deletes the first two items.

alternatives for excessive for() looping in javascript

Situation
I'm currently writing a javascript widget that displays a random quote into a html element. the quotes are stored in a javascript array as well as how many times they've been displayed into the html element. A quote to be displayed cannot be the same quote as was previously displayed. Furthermore the chance for a quote to be selected is based on it's previous occurences in the html element. ( less occurrences should result in a higher chance compared to the other quotes to be selected for display.
Current solution
I've currently made it work ( with my severely lacking javascript knowledge ) by using a lot of looping through various arrays. while this currently works ( !! ) I find this solution rather expensive for what I want to achieve.
What I'm looking for
Alternative methods of removing an array element from an array, currently looping through the entire array to find the element I want removed and copy all other elements into a new array
Alternative method of calculating and selecting a element from an array based on it's occurence
Anything else you notice I should / could do different while still enforcing the stated business rules under Situation
The Code
var quoteElement = $("div#Quotes > q"),
quotes = [[" AAAAAAAAAAAA ", 1],
[" BBBBBBBBBBBB ", 1],
[" CCCCCCCCCCCC ", 1],
[" DDDDDDDDDDDD ", 1]],
fadeTimer = 600,
displayNewQuote = function () {
var currentQuote = quoteElement.text();
var eligibleQuotes = new Array();
var exclusionFound = false;
for (var i = 0; i < quotes.length; i++) {
var iteratedQuote = quotes[i];
if (exclusionFound === false) {
if (currentQuote == iteratedQuote[0].toString())
exclusionFound = true;
else
eligibleQuotes.push(iteratedQuote);
} else
eligibleQuotes.push(iteratedQuote);
}
eligibleQuotes.sort( function (current, next) {
return current[1] - next[1];
} );
var calculatePoint = eligibleQuotes[0][1];
var occurenceRelation = new Array();
var relationSum = 0;
for (var i = 0; i < eligibleQuotes.length; i++) {
if (i == 0)
occurenceRelation[i] = 1 / ((calculatePoint / calculatePoint) + (calculatePoint / eligibleQuotes[i+1][1]));
else
occurenceRelation[i] = occurenceRelation[0] * (calculatePoint / eligibleQuotes[i][1]);
relationSum = relationSum + (occurenceRelation[i] * 100);
}
var generatedNumber = Math.floor(relationSum * Math.random());
var newQuote;
for (var i = 0; i < occurenceRelation.length; i++) {
if (occurenceRelation[i] <= generatedNumber) {
newQuote = eligibleQuotes[i][0].toString();
i = occurenceRelation.length;
}
}
for (var i = 0; i < quotes.length; i++) {
var iteratedQuote = quotes[i][0].toString();
if (iteratedQuote == newQuote) {
quotes[i][1]++;
i = quotes.length;
}
}
quoteElement.stop(true, true)
.fadeOut(fadeTimer);
setTimeout( function () {
quoteElement.html(newQuote)
.fadeIn(fadeTimer);
}, fadeTimer);
}
if (quotes.length > 1)
setInterval(displayNewQuote, 10000);
Alternatives considered
Always chose the array element with the lowest occurence.
Decided against this as this would / could possibly reveal a too obvious pattern in the animation
combine several for loops to reduce the workload
Decided against this as this would make the code to esoteric, I'd probably wouldn't understand the code anymore next week
jsFiddle reference
http://jsfiddle.net/P5rk3/
Update
Rewrote my function with the techniques mentioned, while I fear that these techniques still loop through the entire array to find it's requirements, at least my code looks cleaner : )
References used after reading the answers here:
http://www.tutorialspoint.com/javascript/array_map.htm
http://www.tutorialspoint.com/javascript/array_filter.htm
http://api.jquery.com/jQuery.each/
I suggest array functions that are mostly supported (and easily added if not):
[].splice(index, howManyToDelete); // you can alternatively add extra parameters to slot into the place of deletion
[].indexOf(elementToSearchFor);
[].filter(function(){});
Other useful functions include forEach and map.
I agree that combining all the work into one giant loop is ugly (and not always possible), and you gain little by doing it, so readability is definitely the winner. Although you shouldn't need too many loops with these array functions.
The answer that you want:
Create an integer array that stores the number of uses of every quote. Also, a global variable Tot with the total number of quotes already used (i.e., the sum of that integer array). Find also Mean, as Tot / number of quotes.
Chose a random number between 0 and Tot - 1.
For each quote, add Mean * 2 - the number of uses(*1). When you get that that value has exceeded the random number generated, select that quote.
In case that quote is the one currently displayed, either select the next or the previous quote or just repeat the process.
The real answer:
Use a random quote, at the very maximum repeat if the quote is duplicated. The data usages are going to be lost when the user reloads/leaves the page. And, no matter how cleverly have you chosen them, most users do not care.
(*1) Check for limits, i.e. that the first or last quota will be eligible with this formula.
Alternative methods of removing an array element from an array
With ES5's Array.filter() method:
Array.prototype.without = function(v) {
return this.filter(function(x) {
return v !== x;
});
};
given an array a, a.without(v) will return a copy of a without the element v in it.
less occurrences should result in a higher chance compared to the other quotes to be selected for display
You shouldn't mess with chance - as my mathematician other-half says, "chance doesn't have a memory".
What you're suggesting is akin to the idea that numbers in the lottery that haven't come up yet must be "overdue" and therefore more likely to appear. It simply isn't true.
You can write functions that explicitly define what you're trying to do with the loop.
Your first loop is a filter.
Your second loop is a map + some side effect.
I don't know about the other loops, they're weird :P
A filter is something like:
function filter(array, condition) {
var i = 0, new_array = [];
for (; i < array.length; i += 1) {
if (condition(array[i], i)) {
new_array.push(array[i]);
}
}
return new_array;
}
var numbers = [1,2,3,4,5,6,7,8,9];
var even_numbers = filter(numbers, function (number, index) {
return number % 2 === 0;
});
alert(even_numbers); // [2,4,6,8]
You can't avoid the loop, but you can add more semantics to the code by making a function that explains what you're doing.
If, for some reason, you are not comfortable with splice or filter methods, there is a nice (outdated, but still working) method by John Resig: http://ejohn.org/blog/javascript-array-remove/

Categories