In the source code of zepto,I found that in zepto.matches function,there's a fallback perform,
zepto.matches = function(element, selector) {
if (!element || element.nodeType !== 1) return false
var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector ||
element.oMatchesSelector || element.matchesSelector
if (matchesSelector) return matchesSelector.call(element, selector)
// fall back to performing a selector:
var match, parent = element.parentNode, temp = !parent
if (temp) (parent = tempParent).appendChild(element)
match = ~zepto.qsa(parent, selector).indexOf(element)
temp && tempParent.removeChild(element)
return match
}
despite the normal matchSelector way,I'm quite curious about the fallback,why it add ~ in front of this:
~zepto.qsa(parent, selector).indexOf(element)
document.getElementById("test");
$.zepto.matches(a,"#test") // -1
it returns -1 , is it right or am I missing something?
It's the Bitwise NOT operator. In this case it's used to have a boolean return value for indexOf: the indexOf methods (array, string), returns -1 if a match is not found, and the position if a match is found. The position can be also 0, of course. So using the bitwise NOT operator:
console.log(~-1); // 0
console.log(~0) // -1
console.log(~1) // -2
// and so on
So because in JS 0 is considered as a "Falsy" value, and any other number – except NaN – is a "Truthy" value, having ~zepto.qsa(parent, selector).indexOf(element) will returns 0 if the element is NOT found, and therefore can be thread has a "Falsy" value in the code:
var isFound = ~zepto.qsa(parent, selector).indexOf(element);
if (isFound) {
// do something
}
However, in that case I would expect the zepto methods to return a real boolean, because it's an API method. In this such case, I would prefer use simply:
match = zepto.qsa(parent, selector).indexOf(element) > -1;
Or
match = !!~zepto.qsa(parent, selector).indexOf(element);
The double ! it's used to have the proper Boolean representation: if indexOf returns -1, the ~ transform it in 0, then the first ! in true, and the second ones in false, that is the Boolean value we expect for "not found".
The Bitwise NOT it's also used for some other tricks, like this one:
var words = ["a", "b", "c", "b", "b"];
var frequency = words.reduce(function(freq, word) {
freq[word] = -~freq[word];
return freq;
}, {})
This code returns an object like {a:1, b:3, c:1}; it basically counts the frequency of the same string value in an array. Here the Bitwise NOT it's used with the Unary Negation operator to have an incremental counter: so if freq[word] is undefined – because this word is not yet in the "dictionary" – then the ~undefined is -1, and with the unary negation becames 1. The next time we'll have -~1, and therefore - -2, so 2; then -~2 will be 3 and so on.
The bitwise operators are really useful, but also easier to be abused. In some cases are necessary (For instance, the Zero-fill right shift it's currently the only way in JS to have a substitute of ToUint32) they're not, and they could lead to code hard to read.
But they're definitely worthy to be known.
Related
The following code uses the reduce method. It outputs the number of times an element appears in the array. If element appears once then it only outputs 1, otherwise if it is a repeated item then it it is added..
let a = ["a", "b", "c", "a", "b"]
const t = a.reduce((aa, ll) => {
const count = aa[ll];
count
?
aa[ll] = count + 1 :
aa[ll] = 1
return aa
}, {})
console.log(JSON.stringify(t))
// output
// { "a":2, "b":2, "c":1 }
Question is regarding the condition in the ternary operation, specifically the count variable. How is the count variable able to resolve true or false.
The concept is called "Truthy" and "Falsy" respectively. Ie anything that is different from false, 0, -0, 0n, NaN, null, undefined and "" (empty string) can be evaluated to true in Javascript
So your assign var counter = aa[ll] to be the value of the key ll in object aa. That's either a number or undefined. If it's a number !== 0 it's a truthy, if it's 0 or undefined it's falsy. Thus it can be used in a ternary operatator. Depending on the value of counter either the value of the first or the second assignment will be returned (but ignored). The return value of an assignment is always the value of the right hand side ...
While you can use also assignments in the expressions of a ternary operator, I personally wouldn't use it that way but write this assignment as follows
const t = a.reduce((aa, ll) => {
aa[ll] = (aa[ll] || 0) + 1;
return aa
}, {})
(aa[ll] || 0) will return the value of aa[ll] if it's a truthy or 0 otherwise. Thus, in your case, the result of this expression will always be a number >= 0. Then you increase the result by 1 (for the currenct occurence of ll) and assign it back to aa[ll]. This is much shorter than your original code and IMHO much more readable
Heres the answer I found.
"A javascript object consists of key-value pairs where keys are unique. If you try to add a duplicate key with a different value, then the older value for that key is overwritten by the new value."
basically the count variable was checking to see if the new property already exists.
This question already has answers here:
How do I check if an array includes a value in JavaScript?
(60 answers)
Closed 4 years ago.
I'm using Javascript to find if something is in an array or not, but when I'm using this code it doesn't alert anything at all, which means it's not working correctly. What am I doing wrong?
var theArray = new Array("one","two","three");
if (theArray.indexOf("one")) { alert("Found it!"); }
if (!theArray.indexOf("four")) { alert("Did not find it!"); }
you should use the includes function as:
if (theArray.includes('one')) { alert("Found it!"); }
Remember that the starting index of an array is 0.
From the docs.
The indexOf() method returns the first index at which a given element
can be found in the array, or -1 if it is not present.
The index of one is 0, which is falsy so the first check will fail. indexOf will return -1 if there is no match so you should explicitly check for that.
var theArray = new Array("one","two","three");
if (theArray.indexOf("one") !== -1) { alert("Found it!"); }
if (theArray.indexOf("four") === -1) { alert("Did not find it!"); }
That's because indexOf returns a number which is the index of the matched string, and -1 if the string does not exist in the array. You need to compare the return value to numbers like these:
if (thisArray.indexOf('one') > -1) { alert('Found it!') }
if (thisArray.indexOf('four') > -1) { alert('Did not find it') }
You can use includes to return a boolean instead, if you'd like.
if (thisArray.includes('one')) { alert('Found it!') }
You'd think that might work, wouldn't you? But there are two gotchas here.
.indexOf() returns the index of the first matching element it finds, or -1 if it doesn't find anything. And remember that JavaScript array's are zero-indexed, meaning the first array element has the index of zero. So if the match is the first element, then zero is the returned value, and like most languages, zero means false when you'd want it to be true. However this bring us to point #2.
.indexOf() performs a comparison using strict equality, or in other words ===. The returned value won't be coerced like if you used true == 1. This is highly relevant here because if it didn't use strict equality, then any element it found (other than the first) would have an index of one or higher, and then your comparison would succeed. For example if (theArray.indexOf("two")) would work since the index of that element is 1. However, single indexOf() does a strict equality check, it fails. Which is why you need to explicitly compare the returned value of indexOf() to something, normally > -1.
Linear search. It is a 'language agnostic' approach to solving the problem of searching an unordered list. Yes, you can use array.includes(), which is a neat one-linear specific to JavaScript. But, it appears as through you are new to programming, at least with JavaScript, and before you take advantage of some of those fancy tools that make life easier, it's worth implementing them yourself so you truly understand what's going on under the hood and more importantly, why it works.
function contains(array, value) {
// Loop through the entire array
for (let i = 0; i < array.length; i++) {
// Return true on first match
if (array[i] === value)
return true
}
// Return false on no match
return false
}
// Make an array
let a = ['one', 'two', 'three']
// If it has an element, notify
if (contains(a, 'one'))
alert('found it')
You could use the bitwise NOT ~ operator and check the value of the returned index or -1 if not found.
~ is a bitwise not operator. It is perfect for use with indexOf(), because indexOf returns if found the index 0 ... n and if not -1:
value ~value boolean
-1 => 0 => false
0 => -1 => true
1 => -2 => true
2 => -3 => true
and so on
var theArray = new Array("one", "two", "three");
if (~theArray.indexOf("one")) {
console.log("Found it!");
}
if (!~theArray.indexOf("four")) {
console.log("Did not find it!");
}
When having a simple boolean, it is simple to use it in a condition :
var running = true;
if(running) {/*do something*/}
Is there some trick to do this with a boolen array? something like this:
var running = [false,false,true,false];
if(running[]){/*do something*/}
At the moment I do it like this :
var uploading = false
for(i=0; i< running.length ; i++)
{
if(running[i]) uploading = true;
}
if(uploading)
But I feel like this is not really perfectly written code and that their could be some trick, shorthand method.. Could be plain javascript, could be Jquery
jQuery has the $.inArray method:
uploading = $.inArray(running, true) !== -1;
The name is slightly misleading, it returns the index of the element that matched (-1 if none).
JavaScript has Array#indexOf, but it's missing in IE8 and earlier:
uploading = running.indexOf(true) !== -1;
JavaScript as of ES5 has the Array#some method, but it's overkill if you can look for the specific value with either of the above. Still, it's useful if you need to massage the values at all. For instance, if any "truthy" value is okay rather than specifically true:
uploading = running.some(function(entry) { return !!entry; });
You can just use this:
uploading = running.indexOf(true) !== -1;
If the array doesn't contain a true, indexOf will return -1, resulting in a false from !== -1. If the array contains a true, indexOf will return it's index, resulting in a true.
A slightly shorter option:
uploading = !!~running.indexOf(true);
The ~ is a bitwise NOT, this inverts the value from indexOf: -1 -> 0, 0 -> -1, 1 -> -2, etc. Then the double ! first casts this value to a boolean, then inverts it again, resulting in a boolean output that's true then the array contains a true.
I just thought about little trick that can work in this situation:
uploading = !!Math.max.apply(null, [false, true, false, false]); // => true
Basically without !! path you will get 1 (if there is at least one true) or false (for all false in array). Max calls ToNumber for each passed argument, so true => 1.
Note: you'd better use indexOf, this is just for fun and to demonstrate another unobvious way.
I think that it is obvious what my code does.
Why does my code return a whole string if I use the !== operator? I know that arrays in Javascript start from at index 0, and here I'm entering the whole filename as the argument, so indexOf(".") will always be greater then 0. No, I'm not passing an .htaccess file here.
function getFileExtension(i) {
// return the file extension (with no period) if it has one, otherwise false
if(i.indexOf(".") !== 0) { //
return i.slice(i.indexOf(".") + 1, i.length);
} else {
return false;
}
}
// here we go! Given a filename in a string (like 'test.jpg'),
getFileExtension('pictureofmepdf'); return given string
// both operand are same type and value
But if I change comparasion to
(i.indexOf(".") > 0) // logs false
P.S. I case that you are asking, this is form usvsth3m.
indexOf() returns the index of the substring, so it can return 0, which would mean that the substring appears at position 0. If the substring is not found, it returns -1 instead, so change your if statement to reflect this logic:
if(i.indexOf(".") >= 0)
Additionally, you should use substring() to extract a substring from a string - slice() is for arrays.
return i.substring(i.indexOf(".") + 1, i.length);
Still, I think a better way to do this is with split():
var fileNameArray = i.split("."); // "foo.txt" --> ["foo" "txt"]
if(fileNameArray.length >= 2) {
return fileNameArray[1];
} else {
return false; //maybe you want to return "" instead?
}
The String method indexOf returns (if it is founded) the first index of the string you search, and remember, index can be zero, that why you have to do a strict comparision to check if indexOf it is not returning a boolean false.
I will suggest you to use lastIndexOf for this case because a file named as something.min.js will return min.js as an valid extension, and nope.
Well, to simplify, I omit that indexOf returns index which is typeof number, or -1, not returning boolean value FALSE in case when given value not found. So in case of comparing -1 to 0, result is true, and that's why I actually geting outputed given string, instead false. Well, MDN is in my bookmark bar now
var z = -1;
console.log(z >= 0); // evaluates false because -1 < 0
console.log(z !== 0); // evaluates true because -1 !== 0
// also if z > 0 it is !== 0, but could be < 0
So next code works like a charm.
function getFileExtension(i) {
// i will be a string, but it may not have a file extension.
// return the file extension (with no period) if it has one, otherwise false
if(i.indexOf(".") >= 0) {
return i.substring(i.indexOf(".") + 1, i.length);
} else {
return false;
}
}
getFileExtension('pictureofmepdf');
I was wondering why / when -1 is used in JavaScript or jQuery, respectively.
I'm using this snippet:
if ($.inArray('false', answers) > -1) {
window.console.log('you Lose!');
}
Cheers,
Chris
In this case, -1 is returned if the expression you are looking for is not in the array you are looking in.
While, according to its documentation, $.inArray returns the index of the element (if it was found), an index of -1 is impossible: This is due to the fact that indices start with 0 as lowest possible value.
Hence -1 does just mean a non-valid index position, i.e. not found.
So, in your snippet, the test for > -1 means: Check whether any valid index was found, or in other words, check whether the given value was in the array.
This is even mentioned in the documentation:
Because JavaScript treats 0 as loosely equal to false (i.e. 0 == false, but 0 !== false), if we're checking for the presence of value within array, we need to check if it's not equal to (or greater than) -1.
Hope this helps :-)
Because indexes is starting with 0. (0-index).
var arr = ['a', 'b', 'c'];
alert( arr[0] ) // a;
So when checking the index of an element in an array ($.inArray and [].indexOf) it would be wrong to return 0 if the element isn't present:
var arr = ['a', 'b', 'c'];
alert( arr.indexOf('a') ); // 0
alert( arr.indexOf('b') ); // 1
alert( arr.indexOf('c') ); // 1
alert( arr.indexOf('d') ); // -1
I would say that jQuery have a design flaw here, I would rather think that jQuery.inArray would return a boolean (true/false).