I have created a game that is based on a grid being populated with words
In my code I have a small bit of underscore.js that helps my words fit in the space available in the grid without breaching the grids barriers.
I understand that it is very powerful java script and don't personally have a problem with it. But my team manager would like to get rid of it for some jQuery that will provide the same solution as there is only one function and would save me having a whole library. How I would replace this with some jQuery?
function getWordToFitIn(spaceAvail, wordlist) {
var foundIndex = -1;
var answer = _.find(wordlist, function (word, index) {
if (word.length <= spaceAvail) {
foundIndex = index;
return true;
}
});
if (foundIndex == -1) {
answer = getXSpaces(spaceAvail);
_.find(wordlist, function (word, index) {
if (word[0] == " ") {
foundIndex = index;
return true;
}
});
}
if (foundIndex != -1) {
wordlist.splice(foundIndex, 1);
}
return answer;
}
As far as I can see, the only underscore method youre using is _.find. But I don’t think you are using it as it was intended. It looks like you are simply looping and returning true when a criteria is met.
You can use the native forEach if you don’t have legacy support, or use a shim. Or you can use the jQuery.each method.
The first loop could probably (I’m not 100% sure about the answer variable) be written like this:
var answer;
$.each(wordlist, function(index, word) {
if (word.length <= spaceAvail) {
foundIndex = index;
answer = word;
return false; // stops the loop
}
});
And the second one:
$.each(wordlist, function (index, word) {
if (word[0] == " ") {
foundIndex = index;
return false;
}
});
Related
I want to create a function "palindromes()" which checks whether a value is a palindrome (spelled the same forwards and backwards).
In order to do that, I have created 4 functions, which:
Makes all letters small
Removes all non-letter characters
Reverses the ensuing array, and finally...
Checks whether that array is a palindrome.
See functions bellow:
function makeSmall(input) {
lowerCase = input.toLowerCase();
return lowerCase;
}
function keepOnlyLetters(input) {
var patt1 = /[a-z]/g;
var onlyLetters = input.match(patt1);
return onlyLetters;
}
function reverseArray(array) {
var reversedArray = array.slice().reverse();
return reversedArray;
}
function checkPalindromes(array) {
var reversedArray = array.slice().reverse();
for (let i = 0; i <= array.length; i++) {
if (array[i] != reversedArray[i]) {
return false;
}
}
return true;
}
How do I make sure that the function "palindromes()" takes one value and runs it through all these functions to finally give me an answer (true or false) of whether that value is a palindrome or not?
Best regards,
Beni
There's a point of diminishing returns with functions. When calling the function is just as short as using the body of the function inline, you've probably hit that point. For example, makeSmall(input) is really no improvement to just using input.toLowerCase() inline and will be slower and harder to understand. input.toLowerCase() is already a function; it's just wasted work to wrap it in another function.
Having said that, to answer your question, since all your functions return the value that's input to the next, you can put you functions in an array and call reduce():
function palindromes(input) {
return [makeSmall, keepOnlyLetters, reverseArray, checkPalindromes].reduce((a, c) => c(a), input)
}
So first before trying to do composition at first it sometimes works best to do it sequentially to make sure you understand the problem. As you get better at composition eventually you'll know what tools to use.
function checkPalindrome(string){
return string
.toLowerCase()
.match(/[a-z]/g)
.reverse()
.reduce(function ( acc, letter, index ) {
return acc && string[index] == letter
})
}
checkPalindrome('test') // false
checkPalindrome('tet') // true
Okay good we understand it procedurally and know that there are four steps. We could split those four steps out, however since two steps require previous knowledge of the array state and we don't want to introduce converge or lift just yet we should instead just use a pipe function and combine the steps that require a previous state. The reason for that is eventually functions just lose how much smaller you can make them, and attempting to split those steps up not only hurts readability but maintainability. Those are not good returns on the effort invested to make two functions for that part!
function pipe (...fns){
return fns.reduce( function (f, g){
return function (...args){
return g(
f(...args)
)
}
}
}
All this function does it it pre-loads(composes) a bunch of functions together to make it so that the output of one function applies to the input of the next function in a left to right order(also known as array order).
Now we just need out three functions to pipe:
function bringDown(string){ return string.toLowerCase() } // ussually called toLower, see note
function onlyLetters(string){ return string.match(/[a-z]/g) }
function flipItAndReverseItCompare(arrayLike){ // I like missy elliot... ok?
let original = Array.from(arrayLike)
return original
.slice()
.reverse()
.reduce(function (acc, val, ind){
return acc && val == original[ind]
})
}
Now we can just pipe them
let palindrome = pipe(
bringDown,
onlyLetters,
flipItAndReverseItCompare
)
!palindrome('Missy Elliot') // true... and I never will be
palindrome('Te t') // true
Now you're well on your way to learning about function composition!
You can just string the function calls together like this...
var input = 'Racecar';
if (checkPalindromes(reverseArray(keepOnlyLetters(makeSmall(input))))) {
alert("It's a palindrome");
}
You can just call them in a nested fashion and return the final result in your palindrome function.
Sample Code: (with changes indicated in the comments)
function makeSmall(input) {
// Added var to prevent it from being a global
var lowerCase = input.toLowerCase();
return lowerCase;
}
function keepOnlyLetters(input) {
var patt1 = /[a-z]/g;
var onlyLetters = input.match(patt1);
return onlyLetters;
}
// This function is not really needed and is unused
/*function reverseArray(array) {
var reversedArray = array.slice().reverse();
return reversedArray;
}*/
function checkPalindromes(array) {
var reversedArray = array.slice().reverse();
for (let i = 0; i <= array.length; i++) {
if (array[i] != reversedArray[i]) {
return false;
}
}
return true;
}
// New Palindromes function
function palindromes(input){
return checkPalindromes(keepOnlyLetters(makeSmall(input)));
}
Note:
You don't really need so many functions to do this. I'm putting this here as a strict answer to your exact question. Other answers here show how you can solve this in shorter (and better?) ways
try the following snippet.
function makeSmall(input) {
lowerCase = input.toLowerCase();
return lowerCase;
}
function keepOnlyLetters(input) {
var patt1 = /[a-z]/g;
var onlyLetters = input.match(patt1);
return onlyLetters;
}
function reverseArray(array) {
var reversedArray = array.slice().reverse();
return reversedArray;
}
function checkPalindromes(array) {
var reversedArray = array.slice().reverse();
for (let i = 0; i <= array.length; i++) {
if (array[i] != reversedArray[i]) {
return false;
}
}
return true;
}
var result = checkPalindromes(reverseArray(keepOnlyLetters(makeSmall("Eva, Can I Stab Bats In A Cave"))));
console.log(result);
Notice how functions are called one after the other in one line.
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.
I have been working on a palindrome function (will check if word is spelled the same forward and backwards):
var palinDromes = function(palMap) {
palMap.split(" ").map(function(word) {
var palCheck = (word.toLowerCase() === word.toLowerCase().split("").reverse().join(""));
return palCheck;
});
};
console.log(palinDromes('Hannah speaks English and Malayalam'));
But the output is always undefined. I believe the issue is in the first step, something involving the console.log(palinDrome(...)); transition to palMap, but I'm not sure what exactly.
The issue might also be that I am not returning palCheck correctly at the end of the function?
var palinDromes = function(palMap) {
return palMap.split(" ").map(function(word) {
var palCheck = (word.toLowerCase() === word.toLowerCase().split("").reverse().join(""));
return palCheck;
});
};
console.log(palinDromes('Hannah speaks English and Malayalam'));
EDIT: I added a return before palMap.split(" ").map(function(word) { ...
I have an if statement:
if(firstString == "no" && secondString == "no" && thirdString == "no"){
// Do stuff here
}
Is there a prettier way to format this? Using false instead of "no" is not an option, since the data I'm checking is from an AJAX request and I don't control its output. Otherwise I'd write it this way:
if(!firstString && !secondString && !thirdString){
// Do stuff here
}
Thanks
UPDATE:
I know this is totally ridiculous, but it occurred to me that this might actually be the shortest way:
if(firstString + secondString + thirdString == "nonono"){
// Do stuff here
}
Given that the number of strings is known in advance, then you have 2 options as far as I can see..
Leave it as it is. The if statement isn't hard to read, and any alternate formats will either be as complicated or more complicated.
convert the strings to booleans when you retrieve the data from the AJAX request, so that you're storing TRUE or FALSE instead of "yes" and "no". That would allow you to use a your preferred if statement format, and might be more efficient than many string comparisons if you do a lot of them.
In the end, which you do is up to you, but personally I think it would be better to just stick with what you've got. Don't worry about formatting an if statement, it's pretty obvious what it does, and in my opinion doesn't need to change.
If( "no" == firstString && firstString == secondString && secondString == thirdString )
It was a little difficult to determine exactly what you are evaluating to true or false, but this can be tweaked a tad to get what you're looking for.
var checkStrings = function() {
var no = "no",
args = Array.prototype.slice.call(arguments);
for (var i = 0, len = args.length; i < len; i++) {
if (args[i] !== no) {
return false;
}
}
return true;
};
if (checkStrings(firstString, secondString, thirdString)) {
// Do stuff here
}
Sorry, wasn't thinking--this is if you were checking whether ANY were 'no'
if ($.inArray('no', [firstString, secondString, thirdString]) >= 0) {
// Do something if the value is 'no'
}
UPDATED ANSWER
Unfortunately, jQuery doesn't have the reduce() function (another Array extra introduced in JS 1.6, but not available in older browsers) which would do the trick nicely.
Here's one way to check if all are 'no':
var found = true;
$.each([firstString, secondString, thirdString], function (i, str) {
if (str !== 'no') {
found = false;
}
});
It may seem uglier, but it should be shorter if you have a lot more strings to check.
If you want it wrapped in a function you could do:
function allTrue (arr, checkStr) {
var found = true;
$.each(arr, function (i, str) {
if (str !== checkStr) {
found = false;
}
});
return found;
}
if (allTrue([firstString, secondString, thirdString], 'no')) {
// ...
}
function F(var value){
return value === "no";
}
if(F(firstString) && F(secondString) && F(thirdString)){
// Do stuff here
}
Another option, using jQuery.unique:
var uniques = $.unique([firstString, secondString, thirdString]);
if (uniques.length === 1 && uniques[0] === "no") {
// do stuff
}
I have a three strings of categories
"SharePoint,Azure,IT";
"BizTalk,Finance";
"SharePoint,Finance";
I need to find a way to check if a string contains for example "SharePoint" and "IT", or "BizTalk" and "Finance". The tests are individual strings themselces.
How would i loop through all the category strings (1 - 3) and only return the ones which have ALL instances of the souce.
i have tried the following
function doesExist(source, filterArray)
{
var substr = filterArray.split(" ");
jQuery.each(substr, function() {
var filterTest = this;
if(source.indexOf(filterTest) != -1 )
{
alert("true");
return true;
}else
{
alert("false");
return false;
}
});
}
with little success...the code above checks one at a time rather than both so the results returned are incorrect. Any help would be great.
Thanks
Chris
UPDATE: here is a link to a work in progress version..http://www.invisiblewebdesign.co.uk/temp/filter/#
Try this:
function doesExist(source, filter)
{
var sourceArray = source.split(",");
var filterArray = filter.split(",");
var match = true;
$.each(filterArray, function(i, val) {
match = match && ($.inArray(val, sourceArray) != -1);
});
return match;
}
gives doesExist("SharePoint,Azure,IT", "SharePoint,IT")==true but doesExist("SharePoint,Azure,IT", "SharePoint,BizTalk")==false.
you could try something like:
if(filterArray.indexOf("SharePoint") > -1 && filterArray.indexOf("IT") > -1) {
...
}