Why / when does $.inArray return -1? - javascript

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).

Related

Ternary operator condition

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.

Finding if something is in an array or not in javascript [duplicate]

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!");
}

JavaScript: match variable to element in array of arrays

I get the first two variables loaded from the backend, then I want to match the brand name I get back and return a two letter code. I put the associated brands in an array of arrays.
It doesn't seem match() is an option, cuz I can't put a variable in regExp().
This didn't work:
if (brand.indexOf(brand_code[i])) {
bc = brand_code[i][1];
}
This didn't work.
if (brand_code[i][0]===brand)
bc = brand_code[i][1];
}
This is my latest attempt.
$(document).ready(function() {
var phone_model='$request.getHeader("x-wurfl-model-name")',
brand='$request.getHeader("x-wurfl-brand-name")',
brand_code=[
['Alcatel','AL'],
['Huawei','HU'],
['LG','LG'],
['Motorola','MT'],
['Samsung','SA'],
['Unimax','UX'],
['ZTE','ZE']];
for (var i = brand_code.length - 1; i >= 0; i--) {
if ($.inArray(brand,brand_code[i])) {
bc = brand_code[i][1];
}
}
$('.faq .mobile_tutorial a').append(bc+phone_model);
});
This gives me an error of Cannot read property '3' of undefined
Where phone_model='Z990g' & brand='ZTE'
Where am I going wrong?
If you would structure your data differently in the variable brand_code, it would become a bit easier:
brand_code={
'Alcatel':'AL',
'Huawei':'HU',
'LG':'LG',
'Motorola':'MT',
'Samsung':'SA',
'Unimax':'UX',
'ZTE':'ZE'
};
bc = brand_code[brand];
}
This will not need to go through an array. Most JavaScript engines find the match in constant time if you use the object-based lookup above. In ES you can use a Map for the same purpose and with the same efficiency.
About your attempt
$.inArray returns 0 when ZTE matches the first element of an array, so the if condition would be false in that case. But worse, when ZTE is not found, the method returns -1, which makes the if condition true.
So, you would have had better results if you had put:
if ($.inArray(brand,brand_code[i])>-1) {
From the jQuery documentation:
The $.inArray() method is similar to JavaScript's native .indexOf() method in that it returns -1 when it doesn't find a match. If the first element within the array matches value, $.inArray() returns 0.
Use Array.filter to find your match, then you can either check that the result's length > 0 and get result[0][1] from it, or use Array.reduce to return only the code:
// filter down to match and reduce the match to it's code value
brand_code.filter(function(pair) {
return pair[0] === brand
}).reduce(function(out, match) {
return match[1];
}, '');
OR ES6:
brand_code.filter(pair => pair[0] === brand)
.reduce((_, match) => match[1], '');

indexOf with multiple arguments

Is it possible to use multiple arguments when determining indexOf on an array?
I want to determine if my array contains any of three integers. Important to note at this stage that the array will only have one value (if it has more, it won't reach this code block).
array.indexOf(123 || 124 || 125) === 0
So if array = [123] then my indexOf should be 0 and therefore true.
If array = [124] then my indexOf should be 0 and therefore true.
What I am finding is happening is [123] works OK but it's not even bothering to check the indexOf for the 2nd or 3rd arguments, and is just returning false.
http://codepen.io/anon/pen/WxmyGp?editors=0011
The || operator returns the left hand side if it is true, otherwise it returns the right hand side. 123 || 124 || 125 just means 123.
If you want to test if any of multiple values are in an array, then you have to test each one in turn.
array.indexOf(123) == 0 || array.indexOf(124) == 0 || array.indexOf(125) == 0
Since you only care about one specific index the array, you can turn the whole thing on its head:
[123, 124, 125].indexOf(array[0]) > -1
You can do this using Array.some().
The nice thing about this approach is that you will only have to iterate through the array once. If you || multiple indexOf() calls together, you're going to keep iterating the array with every missed search.
function checkArray(array) {
return array.some(function(item) {
return item == 123 || item == 124 || item == 125;
});
};
console.log(checkArray([123, 456, 789]));
console.log(checkArray([456, 789]));
console.log(checkArray([789, 456, 125]));
If you want to get multiple results one other way of doing this is Array.prototype.filter(). Bu this will return you the objects itself. If you are interested in the indices then you have to combine it with Array.prototype.reduce() such as;
var a = [1,2,3],
b = a.filter(e => e === 1 || e === 2)
.reduce((p,c) => p.concat(a.indexOf(c)),[]);
console.log(b);

understand zepto.matches function

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.

Categories