Filter() Question

Filter() Question
0

#1

This is another question related to the Palindromes exercise, but it’s more about filter() .

For the exercise, I needed to filter out all non-alphanumeric characters (punctuation and spaces). When I used:

array = array.filter(function(val){
              return (val >= "a" && val <= "z") || (val >= 0 && val <= 9);							  
                                               });

it left in the spaces, makiing inputs like “race car” show as not being palindromes.

I thought it might have something to do with the zero, not sure why, so I changed the return to this:

return (val >= "a" && val <= "z") || val === 0 || (val > 0 && val <= 9);

and it worked. Can someone tell me why this works when the other one doesn’t?

Thanks.


#2

I think it’s because when a space is compared to a number (using inequality operators), it is coerced to 0, which is of course >= 0, so it gets past the filter.

In the second example, the space is compared with 0, but === doesn’t perform a coercion; it compares the space with the number 0 directly, which is false.


#3

I found this algorithm a really good opportunity to learn and or reinforce knowledge of Regular Expressions. I would encourage you to give it a try using a regex.

Not sure if you tested this either but:

"a" > "A" // True
"a" > "C" // True
"b" > "C" // True
"c" < "C" // False

How you are comparing values is only reliable if you’ve already used string.toLowerCase() on your set.

I used this to help me with this one.
https://regexr.com/

and make sure to read up over operators


#4

I think it’s more javascript wierdness…

here’s what mdn says about it

A strict comparison (e.g., ===) is only true if the operands are of the same type and the contents match. The more commonly-used abstract comparison (e.g. ==) converts the operands to the same type before making the comparison. For relational abstract comparisons (e.g., <=), the operands are first converted to primitives, then to the same type, before comparison.


#5

This is why you shouldn’t use comparison operators other than === with strings — very unpredictable results. Use regexes if you need anything more complicated than comparing if two strings match exactly.

It’ll also make your code much more concise — /[^a-z1-9]/gi matches all non-alphanumerics.


#6

What is array ? I assume it’s array of one char long strings obtained after calling split("") on it.
So every element of the array is a string, yet you compare it to a number, i.e. val > 0 is different from val > "0".
Don’t compare string to a number, use “0” and “9” instead.


#7
  1. Remove all non-alphanumeric characters from the provided string using RegExp and String.prototype.replace and store it in a variable.
  2. Reverse the new string from step 1 and save it to another variable. Similar to reverse-a-string challenge.
  3. Compare them “==” and done.

Also learn String.prototype.toLowerCase, String.prototype.split, Array.prototype.reverse, Array.prototype.join and see how you could apply it in your task.

Filter is not the best use-case for this task. Don’t go that route instead learn from the links above and don’t get discouraged. You will thank yourself later. :wink:


#8

Thanks, everyone, for your help! It’s especially good to see this from your different perspectives.

So, I think @kevcomedia gave the most direct answer: the JS uses coercion with >= and changes the space to a zero. I hadn’t thought a space could be coerced!

@alper6, yes, it was a single-character array from splitting a string. I see your point about the numbers from it still being strings. I just tried it your way, and it works. Yours is the most correct solution to my code for the palindrome exercise - other solutions may be more efficient, but this one makes my code more correct.

@Dereje1, your answer (“more javascript weirdness”) made me laugh, which I needed after struggling with this. You confirmed that coercion was at work. And as a tester, I appreciate the test run.:slightly_smiling_face:

@Emgo-Dev, @lionel-rowe and @roshankharel, all of the solutions given for this use regular expressions, which I hadn’t even thought to use. I see their value, but I’m not ready for them yet. Yes, I had changed the string to lower case first thing. Emgo-Dev, I’m bookmarking your references to check when I’m ready to work on regexes.

@roshankharel, filter() was the best way to remove the punctuation marks and spaces from the string (without using a regex). I actually did get discouraged last night. There I was, struggling just to get it to work only to find that there are levels of efficiency/elegance I hadn’t even considered. I came to a decision that for now, I’m going to try to just get the algorithm exercises done, and later, I’ll circle back and focus on improving the efficiency. My priority right now is to learn to use Javascript for basic things with web pages, and so far, I can’t find any free online tutorials or courses that cover that. I’d hoped FCC would, since it seems to cover so much.


#9

In that case, your only realistic option (without relying on weird JS implementation details) is to use character code points, something like this:

var a = 'a'.codePointAt(0);
var z = 'z'.codePointAt(0);
var zero = '0'.codePointAt(0);
var nine = '9'.codePointAt(0);

var filteredCharArr = charArr.filter(function(char) {
  var code = char.toLowerCase().codePointAt(0);
  return (code >= a && code <= z) || (code >= zero && code <= nine);
});

#10

your solution is not inefficient, it’s perfectly OK for this task.


#11

If you really want to understand basic javascript you should look for any instructional material titled ‘fundamentals’.

I’ve used a wide variety of resources in my learning so it’s hard for me to pinpoint one source. I’ve already referenced MDN (Mozilla Developer Network). You can take a look at ECMAscript specification web pages to see how JS has changed over the years. Codecademy offers a good plain javascript exercise, but it’s outdated and they only help you ‘learn by doing’ which isn’t always effective because it’s too specific.

If you start here MDN will act as a bit of a walkthrough starting from square one. It’s more of a documentation site though so you’ll need to be able to pick up quick and work on your own with what they give you.

I waited a while with regex too. I don’t use it too much and I’m still learning it.


#12

@lionel-rowe, I finished that challenge before ever creating this post. FYI, this was the code I submitted that worked:

function palindrome(str) {
  var cleanStr = str.toLowerCase();
  var array = cleanStr.split("");
  array = array.filter(function(val){
                                     return (val >= "a" && val <= "z") || val === 0 || (val > 0 && val <= 9);
                                     // this didn't remove spaces: return (val >= "a" && val <= "z") || (val >= 0 && val <= 0);							  
                                              });
							         // this is corrected, per @alper6: return (val >= "a" && val <= "z") || (val >= "0" && val <= "9");							  
                                              });
   cleanStr = array.join(""); 
   var reverseArray = array.reverse();
   var reverseStr = reverseArray.join("");
   if (cleanStr === reverseStr) {
                                         return true;
                                                }
   else {
         return false;
        }
} </spoiler>

What does it take to get those spoiler tags to work???

But you introduced something I haven’t seen before. What is codePointAt() - what does it do?

@alper6, I agree, my solution is fine for the task as given. But in the hint explanation, they point out that it’s much too inefficient if the “string” you’re checking is the Bible. And their algorithm is just much, much better: to compare characters starting at the front and back of the string. Once two characters don’t match, you know it’s not a palindrome and don’t need to check the rest of the string, so it’s much faster.

@Emgo-Dev, I’ve already explored a multitude of sources (have to be free for now) on JS. They all deal with the syntax and console-style problems, which sure, I need to improve on. What I specifically want is a JS source that teaches how to get inputs from a web page form, use them in functions and provide output or next steps. If I have multiple inputs in a form, each one needs its own listener, yet I need to wait for all of the inputs in order to take the next action… this should be so basic, because it’s such a common thing, but no one seems to have instruction or exercises/projects for it.

Been through Codecademy - I know the basics, just need practice (which I’m getting from FCC, but again still at console level).

I’ll take a look at MDN, which I find overly complicated, but I hadn’t found the place your link leads to. Thanks!


#13

Here is just one example how you can do that. I don’t understand your specific situation but this gathers elements from the DOM, stores them, allows you to act on them.

let inputs = document.getElementsByTagName("INPUT"); // Gather Elements
// or
let inputs = document.querySelectorAll("#formID.orFormClass input[name=\"Specific_Name\"]"); // Gather Elements

// Add Event Listeners to each element (x) in inputs
// x: current index value, y: current index, x: this array
inputs.forEach(function(x,y,z){
  x.addEventListener("change", function(){
    // Your Function Fires when Input value changes
  }
});

Note: you cannot use forEach() on an HTML collection, which is the array that returns when you use getElementsByTagName(). You can however use it on the NodeList result from querySelector which is why I prefer that. Just replace the forEach with your normal for(x=0; x < array.length; x++) loop

Change Event


#14

It just works” doesn’t necessarily mean it’s an appropriate solution.

In the case of your code, it’s somewhat subtle, because there’s no obvious case in which it would fail (someone who knows more about string-to-number coercion in JS might be able to find one, though).

But even if no such case exists, it’s still problematic because the code is difficult to reason about. Code has two types of “audience”: human and machine. Even if the code runs perfectly in all cases, human readers (including highly proficient coders) might not understand it, which is a problem when you’re working collaboratively. Being difficult to reason about is also a problem when you’re reading back through your own code, maybe after not working on it for an extended period of time.

Are you sure that’s what you need? If you just need all the data in the form to be included before submission, you just need a single onsubmit listener that checks if everything’s there. Something like this (this would just check that there’s something in each field, it wouldn’t perform any validation on the data entered):

var form = document.querySelector('#my-form');
var usernameField = document.querySelector('#username-field');
var passwordField = document.querySelector('#password-field');
var captchaField = document.querySelector('#captcha-field');

form.onsubmit = function(event) {
  event.preventDefault();
  if (usernameField.value && passwordField.value && captchaField.value) {
    //do whatever you need to with the form data
  } else {
    alert('Please fill in all the data.');
  }
}

#15

there’s no need for codePointAt() for this example, string comparison is a perfectly OK practice (google: Lexicographical ordering)


#16

If it involves any comparison operator other than ===, it’s risky unless you and every other person on your team have an intimate knowledge of how it works. Given that it’s such a specific subdomain of JavaScript knowledge, that’s unlikely. OP was already confused by the behavior, and rightly so… because it’s confusing. Why write code that you’re not quite sure what it will do when you could write code that you’re exactly sure what it will do?

Also, lexicographically, “Z” would come after “a”, but it doesn’t in JavaScript’s implementation. If you want lexicographical ordering, use str.localeCompare() (along with whatever parameters you need to get the exact sorting function you want).

Of course, the counterargument to all of this is “but it doesn’t matter, it’s not production code”. That’s somewhat true. Still, it doesn’t hurt to learn basic principles and best practices early on.


#17

Sorry, I have no idea what you’re blabbing about, there’s nothing mystic here.
Once you ensure val is a lowercased 1 char long string, the following will safely and efficiently tell if it is a string of one digit or one letter(from the English alphabet):

return (val >= "a" && val <= "z") || (val >= "0" && val <= "9");

#18

I originally typed out a long response, but I figured it would just detail the thread further, so here’s the tl;dr version:

Sometimes, people have different opinions about stuff.