Python has built in functions any() and all(), which are applied on a list (array in JavaScript) as following-
any(): Return True if any element of the iterable is true. If the iterable is empty, return False.
all(): Return True if all elements of the iterable are true (or if the iterable is empty).
We can create our customized functions for above, but please let me know if there any equivalent built-in functions available in JavaScript.
The Python documentation gives you pure-python equivalents for both functions; they are trivial to translate to JavaScript:
function any(iterable) {
for (var index = 0; index < iterable.length; index++) {
if (iterable[index]) return true;
}
return false;
}
and
function all(iterable) {
for (var index = 0; index < iterable.length; index++) {
if (!iterable[index]) return false;
}
return true;
}
Recent browser versions (implementing ECMAScript 5.1, Firefox 1.5+, Chrome, Edge 12+ and IE 9) have native support in the form of Array.some and Array.every; these take a callback that determines if something is 'true' or not:
some_array.some((elem) => !!elem );
some_array.every((elem) => !!elem );
The Mozilla documentation I linked to has polyfills included to recreate these two methods in other JS implementations.
You can use lodash.
lodash.every is equivalent to all
lodash.some is equivalent to any
Build-in function some is equivalent to any I suppose.
const array = [1, 2, 3, 4, 5];
const even = function(element) {
// checks whether an element is even
return element % 2 === 0;
};
console.log(array.some(even));
// expected output: true
You can read more in the docs
Related
I was wondering what would be more performant. Native functions of the language that implies 2 iterations or a simple for loop.
The idea is to find the index of an array of objects whose property filterId match a concrete value.
The solution with a for would be this one whose runtime is n
for (i = 0; i < entries.length; i++) {
if (entries[i].filterId === filterId) {
return i;
}
}
or this other solution which internally must use 2 loops one for the map and another one for the indexOf. However, these are JS functions which are optimized internally by the JS engine. runtime 2n.
entries.map(item=>item.filterId).indexOf(filterId);
some enlightenment about this?
You could use Array.prototype.findIndex.
entries.findIndex(item => item.filterId === filterId)
you can make it using widely available array capabilities and still have a complexity of O(n).
For instance you can usearray.some.
Advantage of array.some : it is available in almost all browsers, (available in IE since IE9).
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some for documentation
const data = [{id:1},{id:2},{id:3},{id:4}]
let foundIndex = null;
data.some((value, index) => {
if(value.id === 3) {
foundIndex = index;
return true;
}
return false;
});
console.log(foundIndex);
I can't get my head around this, i'm using filter to loop through an array and filter out all the integers passed as arguments ,i'm not limited in the number of arguments.
But i'm stuck here when it's about to get back to the function the value of the arguments object, at least more that once.
In my code below, obviously it's not fully working because i'm doing a return within the for…in loop, this is where i don't get how I can get the this second loop without having i re-initialised to 0…
function destroyer(arr) {
var args = arguments.length;
var arg0 = arguments[0];
var Nargs = Array.prototype.slice.call(arguments, 1);
var newArr = [];
newArr = arg0.filter(filtre);
/*Only the first argument is
filtered out,here it's the value "2",
but should filter out [2,3].
The expected result is [1,1]*/
console.log(newArr);
return newArr;
function filtre(e){
for (var i in Nargs){
return e !== Nargs[i];
}
}
}
destroyer([1, 2, 3, 1, 2, 3], 2,3);
Hope this is clear enough,
Thanks for any input !
Matth.
While rgthree provided a solution very similar to what you already had, I wanted to provide a solution that takes advantage of newer ES6 features for modern browsers. If you run this through Babel, it will result in essentially the same code as in that solution, though it will still require a shim for the includes call.
function destroyer(source, ...args) {
return source.filter(el => !args.includes(el));
}
Explanation:
...args uses the spread operator to get all but the first argument into an array named args.
We can directly return the filtered array, assuming you don't need to log it and that was just for debugging.
el => !args.includes(el) is an anonymous arrow function. Since no braces are used, it will automatically return the result of the expression.
Since args is an array, we can directly use Array.prototype.includes to check if the current element is in the arguments to be removed. If it exists, we want to remove it and thus invert the return with !. An alternative could be the following: el => args.includes(el) == false.
Since your return statement in your filtre function is always executed on the first run, it's only returning the whether the first number in Nargs is equal to the current item. Instead, you can use indexOf for full browser support (which returns the index of the item in an array, or "-1" if it's not in an array) instead of a loop (or includes, or a loop, etc. as shown further below):
function destroyer(arr) {
var args = arguments.length;
var arg0 = arguments[0];
var Nargs = Array.prototype.slice.call(arguments, 1);
var newArr = [];
function filtre(e){
return Nargs.indexOf(e) === -1;
}
newArr = arg0.filter(filtre);
console.log(newArr);
return newArr;
}
destroyer([1, 2, 3, 1, 2, 3], 2,3);
If you don't need Internet Explorer support, you could use Array.includes:
function filtre(e){
return !Nargs.includes(e);
}
And finally, if you really wanted to use a loop, you would only want to filter an item (return false) once it's found otherwise allow it (return true):
function filtre(e){
for (var i = 0, l = Nargs.length; i < l; i++) {
if (e === Nargs[i])
return false;
}
return true;
}
This question already has answers here:
What does `return` keyword mean inside `forEach` function? [duplicate]
(2 answers)
Why does this forEach return undefined when using a return statement
(5 answers)
Closed 1 year ago.
I wonder if forEach in JavaScript can make a return, here is my code:
var a = [0, 1, 2, 3, 4];
function fn(array) {
array.forEach(function(item) {
if (item === 2) return false;
});
return true;
}
var ans = fn(a);
console.log(ans); // true
I want to find out if 2 is in my array, if so, return false, but it seems that the forEach function has looped the whole array and ignore return.
I wonder if I can get the answer I want with forEach ( I know I can get what I want using for(..))? dear friend, pls help me, with great thanks!
You can use indexOf instead https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
function fn(array) {
return (array.indexOf(2) === -1);
}
Also from the documentation for forEach - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
Note: There is no way to stop or break a forEach() loop other than by
throwing an exception. If you need such behaviour, the .forEach()
method is the wrong tool, use a plain loop instead.
So, the return value cannot be used the way you are using it. However you could do a throw (which is not recommended unless you actually need an error to be raised there)
function fn(array) {
try {
array.forEach(function(item) {
if (item === 2) throw "2 found";
});
}
catch (e) {
return false;
}
return true;
}
In this case .indexOf() is probably what you want, but there's also .some() when a simple equality comparison is too simple:
var ans = a.some(function(value) { return value === 2; });
The .some() function returns true if the callback returns true for any element. (The function returns as soon as the callback does return true, so it doesn't bother looking beyond the first match.)
You can usually use the .reduce() function as a more general iteration mechanism. If you wanted to count how many instances of 2 were in your array for example:
var twos = a.reduce(function(c, v) { if (v === 2) c += 1; return c; }, 0);
Others have mentioned .indexOf() and .some(). I thought I would add that a good old fashioned for loop gives you the absolute most iteration control because you control the iteration and your processing code is not embedded in a callback function.
While .indexOf() already does exactly what you need, this code just shows how you can directly return when using the old fashioned for loop. It is somehow out of favor these days, but is often still the best choice for better looping control.
function fn(array) {
for (var i = 0, len = array.length; i < len; i++) {
if (array[i] === 2) return false;
}
return true;
}
Using the for loop, you can iterate backwards (useful when removing items from the array during the iteration), you can insert items and correct the iteration index, you can immediately return, you can skip indexes, etc...
forEach returns undefined by specification. If you want to find out if a particular value is in an array, there is indexOf. For more complex problems, there is some, which allows a function to test the values and returns true the first time the function returns true, or false otherwise:
a.some(function(value){return value == 2})
Obviously a trivial case, but consider if you want to determine if the array contains any even numbers:
a.some(function(value){return !(value % 2)})
or as an ECMA2015 arrow function:
a.some(value => !(value % 2));
If you want to test if a particular value is repeated in an array, you can use lastIndexOf:
if (a.indexOf(value) != a.lastIndexOf(value) {
// value is repeated
}
or to test for any duplicates, again some or every will do the job:
var hasDupes = a.some(function(value, i, a) {
return a.lastIndexOf(value) != i;
});
An advantage of some and every is that they only process members of the array until the condition is met, then they exit whereas forEach will process all members regardless.
You said you want with forEach, so I modified your code:
function fn(array) {
var b = true;
array.forEach(function (item) {
if (item === 2) {
b = false;
}
});
return b;
}
var a = [0, 1, 2, 3, 4];
function fn(array, callback) {
var r = true;
array.forEach(function(item) {
if (item === 2) r = false;
});
callback(r);
}
fn(a, function(data) {
console.log(data) //prints false.
});
You can use callbacks as mentioned above to return the data you need. This is not same as the return statement but you will get the data.
I have an array of objects. Each object has a unique userTestrId. Here is the code that I am using when I want to delete one of the objects. Is this the most efficient way I can perform the delete? What I am concerned with is once a row has been deleted the code will still go all the way through the array even though there is no chance of another entry:
var id = 99;
self.tests.forEach(function (elem, index) {
if (elem['userTestId'] === id)
self.tests.splice(index, 1);
});
}
var id = 99;
self.tests.some(function (elem, index) {
if (elem['userTestId'] === id)
self.tests.splice(index, 1);
return true;
});
return false;
}
Could utilise Array.some? Stops looping once you return TRUE from a callback.
This is an alternative to #benhowdle89's answer.
Use Array.prototype.every
The .every method is used to iterate over an array and check whether each and every element passes a test or not. If the callback returns false for any single element, the loop breaks.
Take the following example:
var odds = [3, 5, 7, 9, 11, 12, 17, 19];
//an array with all odd numbers except one
var checkEven = function (n, i, arr) {
console.log ("Checking number ", n);
if (n%2===0) {
arr.splice(i, 1);
return false;
}
return true;
}
console.log(odds.every(checkEven), odds);
If you run the above and look at the console, the loop executed till number 12 only, where it spliced, and stopped.
You can employ similar logic in your code very easily :)
var id = 99;
self.tests.some(function (elem, index) {
if (elem['userTestId'] === id)
self.tests.splice(index, 1);
return true;
});
return false;
}
Polyfill :
some was added to the ECMA-262 standard in the 5th edition; as such it may not be present in all implementations of the standard. You can work around this by inserting the following code at the beginning of your scripts, allowing use of some in implementations which do not natively support it.
// Production steps of ECMA-262, Edition 5, 15.4.4.17
// Reference: http://es5.github.io/#x15.4.4.17
if (!Array.prototype.some) {
Array.prototype.some = function(fun /*, thisArg*/) {
'use strict';
if (this == null) {
throw new TypeError('Array.prototype.some called on null or undefined');
}
if (typeof fun !== 'function') {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
for (var i = 0; i < len; i++) {
if (i in t && fun.call(thisArg, t[i], i, t)) {
return true;
}
}
return false;
};
}
see in detail
While your concern is technically correct, it's unlikely to be an actual problem(Javascript is fast, this is a microoptimization).
What you should do is focus on using the appropriate interface, so your code could be easy to read and understand. .forEach() does not tell you what you want to do, unless you really do want to do something with each element of the array.
Lodash has the .remove() function, which removes all elements matching a predicate. Unfortunately, I couldn't find the exact specific function you wanted in JS's standard library or in lodash, so you would have to write your own wrapper:
var id = 99
removeFirst(tests, function (elem) { return elem.userTestId === id })
function removeFirst(array, callback) {
var index = array.findIndex(callback)
array.splice(index, 1)
}
Having noted that, you should avoid using an array at all - splicing is way more expensive than looping the whole array to begin with! Instead, since you have a unique identifier, you could use a map:
var map = {}
tests.forEach(function mapper(elem) {
map[elem.userTestId] = elem
})
Now, your removal function is simply delete map[id].
I have been adding an Array.indexOf() polyfill to the main JavaScript file of our project. I took it from devdocs.io:
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement , fromIndex) {
var i,
pivot = (fromIndex) ? fromIndex : 0,
length;
if (!this) {
throw new TypeError();
}
length = this.length;
if (length === 0 || pivot >= length) {
return -1;
}
if (pivot < 0) {
pivot = length - Math.abs(pivot);
}
for (i = pivot; i < length; i++) {
if (this[i] === searchElement) {
return i;
}
}
return -1;
};
}
I need this because we still have to support IE 8, but it seems that in IE 8, the indexOf() function is added enumerable. That means, it appears when iterating over arrays using for..in loops, like this:
var a = [];
a[0]=123;
a[1]=456;
for(var value in a) {
alert(value); // this even alerts "indexOf", if the polyfill above is loaded, and this is a big problem
}
Is it possible to make the polyfill "unenumerable", so that I am able to use Array.indexOf() in IE 8, but it does not appear in for..in loops?
Generally speaking, you can add a property as non-enumerable using Object.defineProperty:
Object.defineProperty(Array.prototytpe, 'indexOf', {
enumerable : false,
value : function(){ /* my polyfill code */}
});
But, as you've probably guessed, there is no support for it in IE8, so you're stuck with #tkone's solution of filtering the for..in with hasOwnProperty.
Another pseudo-solution is to create a function you pass the array parameter to:
function indexOf(array, searchElement, fromIndex) {
if (Array.prototype.indexOf) return array.indexOf(searchElement, fromIndex);
/* my polyfill code with array instead of this*/
}
and simply use that instead of the native indexOf:
[1,2,3].indexOf(2); // change to:
indexOf([1,2,3], 2);
You could namespace your function as well so you remember to change it back to standards when you no longer bother with IE8:
IhateIE8.indexOf = ...
You should be using hasOwnProperty when you enumerate over the members of an object.
var a = [];
a[0] = 123;
a[1] = 456;
for(var value in a){
if(a.hasOwnProperty(value)){
alert(value);
}
}
But why aren't you just doing a simple for loop (or polyfilling something like forEach)
Is it possible to make the polyfill "unenumerable", so that I am able to use Array.indexOf() in IE 8, but it does not appear in for..in loops?
Officially, yes (see defineProperty and enumerable), but specifically for IE8 and lower, not really.
I don't think it's a good idea though (see the comments).