I'm relatively new to Javascript (learned Ruby first) but have been very confused by some strange inconsistencies I've been seeing and I'm trying to figure out what the underlying mechanisms are so I can better understand the language. One of these is that when I'm doing a comparison it shouldn't matter on which side of the === sign the two elements are, but I've seen that happen and don't understand why. Here's the example for a basic function to see if a string is sorted alphabetically or not:
This version always returns true:
function sorted(str) {
var arr = str.split("");
return arr.sort().join('') === arr.join('');
}
sorted('abc') // => true
sorted('cba') // => true
While this will return the correct answer:
function sorted(str) {
var arr = str.split("");
return arr.join('') === arr.sort().join('');
}
sorted('abc') // => true
sorted('cba') // => false
I've tried to figure this out and am pretty stuck - can anyone help explain?
Thank you!
Unlike the most of methods of Array.prototype, sort() manipulates the object in place. Hence, in your first snippet arr in the second operand of === is sorted already, and comparison always returns true.
Related
I'm working on JavaScript algorithms and could use some help. What am I doing wrong exactly?
// Given an array of arr, positive integers, and another number X.
// Determine whether or not there exist two elements in arr whose sum is exactly X.
function keyPair(arr, x){
var sum=false;
var key=0;
var temp=0;
for(var i=0;i<=arr.length;i++){
while(sum=false){
key=arr[i];
arr[i]=temp;
temp=key;
}
if(temp+arr[i]==x){
sum=true;
}
}
console.log(sum);
}
keyPair([1,2,4,3,6], 4);
I think this one deserves an explanation:
sum=false is an assignment statement. As an expression, an assignment statement is evaluated as undefined (regardless of the value assigned). So while(sum=false) is actually while(undefined), which is interpreted as while(false).
Your mistake was - as explained multiple times - the wrong comparison operator. I took the liberty of rewriting your code involving a .reduce() call:
function keyPair(arr,sum){
return arr.reduce((a,c,j)=>{
let i=arr.indexOf(sum-c);
if (i>-1 && j<i) a.push([c,arr[i]]);
return a;}, []);
}
console.log(keyPair([1,2,4,3,6], 5));
I'm not going to repeat what has been already said about your code. But even if you're starting coding in JS, it pays for you to start using JS built-ins that will save you a lot of time:
// Determine whether or not there exist two elements in arr whose sum is exactly X.
So you're only interested in whether they exist, not in which are those:
const keyPair = (arr,value) =>
// some: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
// slice: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
arr.some( (x,i) => arr.slice(i).some( y => x + y === value));
console.log(keyPair([1,2,4,3,6], 4))
console.log(keyPair([1,1,1,1,1], 4))
With some you check if there is an x+y sum that equals to your desired value
With slice you omit the portion of the array that you don't need to check
first off, you might want to start by considering changing while(sum=false) to (sum===false), although running on vs code- it does run regardless.
I was doing this kata on codewars. The question wants the function to return true if the first argument (string) passed in ends with the 2nd argument (also a string). So I wrote my function and everything worked just fine until it compares ':-)' with ':-(' and returns true.
What is wrong? I'm so confident that my code should work that I don't even know what to search for.
function solution(str, ending){
if (!ending) return true; // if ending is a empty string return true (the question wants that)
let ok;
const strArr = str.split(''), endingArr = ending.split('');
for (let i = 0; i < endingArr.length; i++) strArr.reverse()[i] === endingArr.reverse()[i] ? ok = true : ok = false;
return ok;
}
console.log(solution(":-)",":-("));
Your problem is a misunderstanding of what reverse() does. It does not return a reversed copy of the old array, it reverses the existing array and returns that same array. As a result, you keep reversing the arrays back and forth every iteration of the loop, causing some elements to be skipped and some to be checked twice.
Array.prototype.reverse() on MDN
Edit:
As pointed out by others in the comments, both to the question and this answer, there are in fact multiple problems.
reverse() aside, the loop always sets ok to the result of the last comparison, making the function ignore all previous results.
The easier way to implement this is to remove ok altogether. Instead, return false as soon as a mismatch is detected. If the function runs long enough to exit the loop, it means no mismatch was detected and true can be returned.
Edit 2:
Just as a friendly suggestion:
While both reverse() and ok are real issues with the code, I only noticed the first one the first time around due to the formatting of the code. The ok problem was off-screen due to the line being too long. As such, once I spotted the reverse() issue, I assumed that was it and didn't bother scrolling sideways to see the rest of the code.
I am not going to demand that you write your own code in a certain way, but if you format it properly, it allows others to read it more easily. In essence, you help us to more easily help you.
For instance, this line:
for (let i = 0; i < endingArr.length; i++) strArr.reverse()[i] === endingArr.reverse()[i] ? ok = true : ok = false;
...would have been significantly easier to read as...
for (let i = 0; i < endingArr.length; i++) {
if(strArr.reverse()[i] === endingArr.reverse()[i])
ok = true;
else
ok = false;
}
...or some variation thereof. Here, the problem is significantly more visible and obvious.
The other answer explains many of the mistakes you've made. I wanted to point out just how much you've over-thought your solution.
function solution(str, ending){
if (ending === "") return true; // if ending is a empty string return true (the question wants that)
return str.endsWith(ending);
}
console.log(solution(":-)",":-("));
console.log(solution("foo",""));
console.log(solution("foo","bar"));
console.log(solution("foobar","bar"));
Even my solution above is overengineered, str.endsWith("") always returns true. So this can be simplified further.
function solution(str, ending){
return str.endsWith(ending);
}
console.log(solution(":-)",":-("));
console.log(solution("foo",""));
console.log(solution("foo","bar"));
console.log(solution("foobar","bar"));
my aim is to test for a palindrome (word is the same forwards and backwards) and the log and array containing only the palindromes, so far this is the most I could come with.
const getAllPalindromes = (words) => {
return words.filter((word) => {
word.split("").reverse().join("") === word;
});
};
console.log(getAllPalindromes(["hello", "noon"]));
To my understanding this should return an array containing the items that are true from the boolean, any pointers will help a newbie thanks a lot!
You're not returning your filter conditional. You do the comparison, but then don't return it so the filter has an undefined as the return of the comparator, and thus it doesn't filter anything. Otherwise, the rest of your logic is correct!
const getAllPalindromes = (words) => words.filter((word) => word.split("").reverse().join("") === word);
console.log(getAllPalindromes(["hello", "noon"]));
Since you're using Arrow Functions we're going to use the implicit return in them to make things a little smaller especially since each function is really just a single statement. Here's the same code in a longer format for direct comparison
const getAllPalindromes = function (words) {
return words.filter(function (word) {
return word.split("").reverse().join("") === word;
});
};
console.log(getAllPalindromes(["hello", "noon"]));
The differences between Arrow Functions and Functions was already answered a long time ago, so here it is for reference.
It might also help if you use an editor or code linter that can help you catch the problems early. A missed return statement is just as easy to make as an off by one error.
Off by One errors are as easy to make as a missed return statement undefined
Happy coding!
I'm creating a few specific functions for a compiler I'm working on, But certain restrictions within the compiler's nature will prevent me from using native JavaScript methods like Array.prototype.pop() to perform array pops...
So I decided to try and write some rudimentary pseudo-code to try and mimic the process, and then base my final function off the pseudo-code... But my tests seem to fail... based on the compiler's current behavior, it will only allow me to use array.length, array element assignments and that's about it... My code is below...
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
y+1<z?(x[y]=arr[y]):(w=arr[y],arr=x);
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // undefined... should be 2
console.log(Arr); // [-1,0,1,2]... should be [-1,0,1]
I'm trying to mimic the nature of the pop function but can't seem to put my finger on what's causing the function to still provide undefined and the original array... undefined should only return if an initial empty array is sent, just like you would expect with a [].pop() call...
Anyone have any clues as to how I can tailor this code to mimic the pop correctly?
And while I have heard that arr.splice(array.length-1,1)[0]; may work... the compiler is currently not capable of determining splice or similar methods... Is it possible to do it using a variation of my code?
Thanks in advance...
You're really over-thinking [].pop(). As defined in the specs, the process for [].pop() is:
Get the length of the array
If the length is 0
return undefined
If length is more than 0
Get the item at length - 1
Reduce array.length by 1
Return item.
(... plus a few things that the JavaScript engine needs to do behind the scenes like call ToObject on the array or ensure the length is an unsigned 32-bit integer.)
This can be done with a function as simple as the one below, there's not even a need for a loop.
function pop(array) {
var length = array.length,
item;
if (length > 0) {
item = array[length - 1];
array.length -= 1;
}
return item;
}
Edit
I'm assuming that the issue with the compiler is that Array.prototype.pop isn't understood at all. Re-reading your post, it looks like arrays have a pop method, but the compiler can't work out whether the variable is an array or not. In that case, an even simpler version of this function would be this:
function pop(array) {
return Array.prototype.pop.call(array);
}
Try that first as it'll be slightly faster and more robust, if it works. It's also the pattern for any other array method that you may need to use.
With this modification, it works:
http://jsfiddle.net/vxxfxvpL/1/
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
if(y+1<z) {
(x[y]=arr[y]);
} else {
(w=arr[y],arr=x);
break;
}
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // 2
The problem now is to remove the last element. You should construct the original array again without last element. You will have problems with this because you can't modify the original array. That's why this tasks are maded with prototype (Array.prototype.pop2 maybe can help you)
Is there an alternative, faster method of returning the position/index of part of an array within another array (where multiple values match)? It's called a lot within my pathfinding algorithm so could do with being as fast as possible.
My current function is:
// Haystack can be e.g. [[0,1,278.9],[4,4,22.1212]]
function coordinate_location_in_array(needle,haystack){
for(n in haystack){
if(haystack[n][0]==needle[0] && haystack[n][1]==needle[1]) return n;
}
return false;
}
// Needle of [0,1]: returns 0
// Needle of [4,4]: returns 1
// Needle of [6,7]: returns false
Edit:
I've been messing around a bit and come up with a (rather ghastly) string manipulation-based method (thereby avoiding the costly for loop). I think it's still slightly slower. Could anybody benchmark these methods?
function coordinate_location_in_array(needle,haystack) {
var str1 = ':' + haystack.join(':');
var str2 = str1.replace(':'+needle[0]+','+needle[1],'*').split('*')[0];
if(str2.length == str1.length) return false;
var preceedingElements = str2.match(/:/g);
return preceedingElements!=null?preceedingElements.length:0;
}
Perhaps with some improvements this second method might provide some performance gain?
Edit 2:
Bench marked all 3 described methods using jsperf.com (initial method is fastest):
http://jsperf.com/finding-matched-array-within-array/3
Edit 3:
Just replaced the for(..in..) loop with a for(..;..;..) loop (since I know that the haystack array will never have "gaps") and performance seems to have significantly improved:
function coordinate_location_in_array(needle,haystack){
for(var n=0;n<haystack.length;n++){
if(haystack[n][0]==needle[0] && haystack[n][1]==needle[1]) return n;
}
return false;
}
I've updated the jsperf page to include this latest method.
If the "haystack" isn't sorted then there isn't a way to make it faster. Not knowing how the elements in a collection are ordered makes finding something out of it linear by nature, because you just have to check each thing.
If you are using this function over the same "haystack" over and over, you could sort the collection, and use the sorting to make it quicker to find the "needle" (look up different sorting and search algorithms to find one that fits your need best, such as using binary search to find the "needle" after haystack is sorted.)
i don't know if its faster, but you can do something like:
[1,2,3,4].slice(0,2).toString() == [1,2].toString()
in your case it would be:
function coordinate_location_in_array(needle,haystack){
for(n in haystack){
if(haystack[n].slice(0,2).toString() == needle.toString()) return n
}
return false;
}
Also found this post, which covers comparison of JS arrays: compare-two-arrays-javascript-associative
Cheers
Laidback
Using a for(..;..;..) loop rather than a for(..in..) loop made the biggest difference.
(See Edit 3 at the end of the question)
Seems to me this is just a substring search but with numbers instead of characters being the components of the string. As such, Boyer-Moore could be applicable, especially if your needles and haystacks get big.