Instructions are as follows:
"Write a function that will find all the anagrams of a word from a list. You will be given two inputs a word and an array with words. You should return an array of all the anagrams or an empty array if there are none. For example:
anagrams('abba', ['aabb', 'abcd', 'bbaa', 'dada']) => ['aabb', 'bbaa']"
I snagged a snippet that takes the first parameter and delivers every combination of characters possible. My trouble now is figuring out how to match this array against the second parameter, and returning some results..
function allAnagrams (word,words) {
if (word.length < 2) {
return [word];
} else {
var allAnswers = [];
for (var i = 0; i < word.length; i++) {
var letter = word[i];
var shorterWord = word.substr(0, i) + word.substr(i + 1, word.length - 1);
var shortwordArray = allAnagrams(shorterWord);
for (var j = 0; j < shortwordArray.length; j++) {
allAnswers.push(letter + shortwordArray[j]);
}
}
return allAnswers;
}
}
allAnagrams("abc",["acb","cba","bac","bca"]);
My instinct was to split the word into an array, and then have another nested for loop to match what needs to be matched. However, I seem to have run into some problems with handling the scope and keep breaking the function so I've turned to you bright minded people. If you have a moment, I would appreciate a hint of how to tackle this from here.
You basically need to see if any permutation of your first string appears in the array.
A fast way to compare if two strings are permutations of one another is to sort the two strings and compare:
function stringSort(string) {
return string.split('').sort().join('');
}
function isAnagram(first, second) {
// are the two sorted strings equal, if so then anagram
return stringSort(first) == stringSort(second);
}
Now, we can use these handy functions to help us build the final desired function:
function allAnagrams(word, words) {
return words.filter(function(element) {
return isAnagram(word, element);
});
}
Note the use of Array#filter, a very handy method for boiling an array down to a few values under some condition.
Note that I have not tested this so if there are any problems please ask.
Related
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.
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>
I have searched high and low, not only on StackOverflow, but many other places elsewhere on the web. I've tried what seems like everything, but something is fundamentally flawed with my logic. I apologize for introducing another "Duplicates in Array" question, but I am stuck and nothing seems to be working as expected.
Anyway, I have a multi-dimensional JavaScript array, only 2 levels deep.
var array = [[Part #, Description, Qty:],
[Part #, Description, Qty:],
[Part #, Description, Qty:]]; //etc
What I need to do is create a function that searches array and returns any duplicate "Part #" lines. When they are returned, I would like to have the entire inner array returned, complete with description and qty.
The trick with this is that the Part #'s that would qualify as 'duplicate' would end differently (specifically the last 4 characters), so using String.prototype.substr makes sense (to me).
I know there are duplicates in the array in the way that I am looking for, so I know that if I had the solution, it would return those Part #'s.
Here is what I have tried so far that gets me the closest to a solution:
function findDuplicateResults(arr) {
var result = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i][0].substr(0,5) === arr[++i][0].substr(0,5)) {
result.push(arr[i]);
}
}
return console.log(result);
}
My thinking is that if the element in the array(with substr(0,5) is equal to the next one in line, push that to the result array. I would need the other duplicate in there too. The point of the code is to show only dupes with substr(0,5).
I have tried using Higher Order Functions such as map, forEach, reduce, and filter (filter being the one that boggles my mind as to why it doesn't do what I want), but I have only been able to return [] or the entire array that way. The logic that I use for said Higher Order Functions remains the same (which is probably the problem here).
I am expecting that my if condition is where the most of the problem is. Any pointers or solutions are greatly appreciated.
There is a mistake in your code. When you use ++i, you are changing the value of i, so it is going to skip one item in the next iteration.
Regarding the logic, you are only comparing one item to the next item, when you should really be comparing each item to all items:
function findDuplicateResults(arr) {
var result = [];
for (var i = 0; i <= arr.length - 1; i++) {
for (var k = 0; k <= arr.length - 1; k++) {
if (i !== k && arr[i][0].substr(0,5) === arr[k][0].substr(0,5)) {
result.push(arr[i]);
}
}
}
return result;
}
Although, the 'substr' could be dropped, and 'for' loop could be replaced by a higher order function:
function findDuplicateResults(arr) {
return arr.filter(function(item1){
return arr.filter(function(item2){
return item1[0] === item2[0];
}).length > 1;
});
}
I tried to find out some good examples but SO seems to have mainly examples from 4-5 years ago and I would like to use a solution that would work using modern browser capabilities.
Ihave an array of test objects:
var tests;
Each test object contains a testId.
How can I remove test object with testId = 25 from the array tests. I was thinking of a for loop but is there a cleaner way to do this?
The best answer depends on whether you know in advance whether there's at most one match, or potentially more than one (and in the latter case whether you want to remove all of them or just the first)
Removing all matches
The "simplest" way is to use filter, although strictly that produces a new array without the matching element:
tests = tests.filter(function(e) {
return e.testId !== 25;
});
This is OK, unless other code is holding a reference to the original array.
Modifying the array safely "in-place" still appears to require a combination of a for loop with .splice:
for (var i = 0; i < tests.length; ) { // nb: deliberate .length test
if (tests[i].testId === 25) {
tests.splice(i, 1);
} else {
++i;
}
}
The "safely" caveat is because the functional methods of iterating through an entire array will get confused if the current element in the array is removed. That is not a concern in the "first match" methods shown below.
Removing first (or only) match
The plain for method is still pretty simple (and probably most efficient, too!)
for (var i = 0, n = tests.length; i < n; ++i) {
if (tests[i].testId === 25) {
tests.splice(i, 1);
break;
}
}
The .some method per Johan's answer can iterate through an array and then exit on first match (although some may object on philosophical grounds to a boolean predicate function also mutating the array):
var didRemove = tests.some(function(e, i, a) {
if (e.testId === 25) {
a.splice(i, 1);
return true; // causes the loop to exit
}
});
In ES6-draft there's .findIndex, which is a generalisation of .indexOf:
var index = tests.findIndex(function(e) {
return e.testId === 25;
});
if (index >= 0) {
tests.splice(index, 1);
}
One way is to loop through all objects and splice a matching object out of the array.
Instead of forEach I use some (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some). Because with the some method you can "break" the loop by returning true so that you don't need to go through all objects if a match has been found.
tests.some(function(testObject, index) {
if (testObject.testId === 25) {
tests.splice(index, 1);
return true;
}
});
Or wrap it in a function
var removeObjectById = function(id) {
tests.some(function(testObject, index) {
if (testObject.testId === id) {
tests.splice(index, 1);
return true;
}
});
}
removeObjectById(25)
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/